//////////////////////////////////////////////////////////////

// From JAVA PROGRAMMING: FROM THE BEGINNING, by K. N. King //

// Copyright (c) 2000 W. W. Norton & Company, Inc.          //

// All rights reserved.                                     //

// This program may be freely distributed for class use,    //

// provided that this copyright notice is retained.         //

//                                                          //

// NervousShapes.java (Chapter 11, page 473)                //

//////////////////////////////////////////////////////////////


// Program name: NervousShapes

// Author: K. N. King

// Written: 1999-08-12

// 

// Displays a frame containing a random mixture of circles

// and rectangles with random colors, sizes, and positions.

// The shapes periodically change position, with the 

// direction of motion chosen randomly for each shape. The

// new x coordinate for each shape will either be the same

// as the old x coordinate, one pixel smaller, or one pixel

// larger; the new y coordinate will be computed in a

// similar manner. Shapes will be constrained so that they

// do not move outside the drawing area.


import java.awt.*;

import jpb.*;


public class NervousShapes {
  // Constants

  private static final int DELAY = 10;
    // Animation delay (milliseconds)

  private static final int MAX_SIZE = 20;
    // Maximum width and height of a shape

  private static final int MIN_SIZE = 10;
    // Minimum width and height of a shape

  private static final int NUM_SHAPES = 50;
    // Number of shapes

  private static final int WINDOW_SIZE = 200;
    // Width and height of drawable portion of frame


  // Class variables

  private static DrawableFrame df;
    // Frame in which shapes are displayed

  private static Graphics g;
    // Graphics context for frame

  private static Shape shapes[] = new Shape[NUM_SHAPES];
    // Array of shapes


  public static void main(String[] args) {
    createWindow();
    createShapes();
    animateShapes();
  }

  ///////////////////////////////////////////////////////////

  // NAME:       createWindow

  // BEHAVIOR:   Creates a frame labeled "Nervous Shapes",

  //             displays the frame, and sets the size of

  //             the frame (using the WINDOW_SIZE class

  //             variable). Assigns the frame to the df

  //             class variable, and assigns the frame's

  //             graphics context to the g class variable.

  // PARAMETERS: None

  // RETURNS:    Nothing

  ///////////////////////////////////////////////////////////

  private static void createWindow() {
    // Create a frame labeled "Nervous Shapes" and set its

    // size

    df = new DrawableFrame("Nervous Shapes");
    df.show();
    df.setSize(WINDOW_SIZE, WINDOW_SIZE);

    // Get the frame's graphics context

    g = df.getGraphicsContext();
  }

  ///////////////////////////////////////////////////////////

  // NAME:       createShapes

  // BEHAVIOR:   Creates enough Circle and Rectangle objects

  //             to fill the shapes array. Each shape has a

  //             random color, size, and position. The height

  //             and width of each shape must lie between

  //             MIN_SIZE and MAX_SIZE (inclusive). The

  //             position is chosen so that the shape is

  //             completely within the drawing area.

  // PARAMETERS: None

  // RETURNS:    Nothing

  ///////////////////////////////////////////////////////////

  private static void createShapes() {
    for (int i = 0; i < shapes.length; i++) {
      // Select a random color

      int red = generateRandomInt(0, 255);
      int green = generateRandomInt(0, 255);
      int blue = generateRandomInt(0, 255);
      Color color = new Color(red, green, blue);

      // Decide whether to create a circle or a rectangle

      if (Math.random() < 0.5)  {
        // Generate a circle with a random size and position

        int diameter = generateRandomInt(MIN_SIZE, MAX_SIZE);
        int x = generateRandomInt(0, WINDOW_SIZE - diameter);
        int y = generateRandomInt(0, WINDOW_SIZE - diameter);
        shapes[i] = new Circle(x, y, color, diameter);
      } else {
        // Generate a rectangle with a random size and

        // position

        int width = generateRandomInt(MIN_SIZE, MAX_SIZE);
        int height = generateRandomInt(MIN_SIZE, MAX_SIZE);
        int x = generateRandomInt(0, WINDOW_SIZE - width);
        int y = generateRandomInt(0, WINDOW_SIZE - height);
        shapes[i] = new Rectangle(x, y, color, width, height);
      }
    }
  }

  ///////////////////////////////////////////////////////////

  // NAME:       animateShapes

  // BEHAVIOR:   Establishes an infinite loop in which the

  //             shapes are animated. During each loop

  //             iteration, the drawing area is cleared and

  //             the shapes are then drawn at new positions.

  //             The new x and y coordinates for each shape

  //             will either be the same as the old ones,

  //             one pixel smaller, or one pixel larger. A

  //             shape is not moved if doing so would cause

  //             any portion of the shape to go outside the

  //             drawing area. At the end of each animation

  //             cycle, there is a brief pause, which is

  //             controlled by the delay constant.

  // PARAMETERS: None

  // RETURNS:    Nothing

  ///////////////////////////////////////////////////////////

  private static void animateShapes() {
    while (true) {
      // Clear drawing area

      g.setColor(Color.white);
      g.fillRect(0, 0, WINDOW_SIZE - 1, WINDOW_SIZE - 1);

      for (int i = 0; i < shapes.length; i++) {
        // Change the x coordinate for shape i

        int dx = generateRandomInt(-1, +1);
        int newX = shapes[i].getX() + dx;
        if (newX >= 0 && 
            newX + shapes[i].getWidth() < WINDOW_SIZE)
          shapes[i].move(dx, 0);

        // Change the y coordinate for shape i

        int dy = generateRandomInt(-1, +1);
        int newY = shapes[i].getY() + dy;
        if (newY >= 0 && 
            newY + shapes[i].getHeight() < WINDOW_SIZE)
          shapes[i].move(0, dy);

        // Draw shape i at its new position

        shapes[i].draw(g);
      }

      // Call repaint to update the screen

      df.repaint();

      // Pause briefly

      try {
        Thread.sleep(DELAY);
      } catch (InterruptedException e) {}
    }
  }

  ///////////////////////////////////////////////////////////

  // NAME:       generateRandomInt

  // BEHAVIOR:   Generates a random integer within a

  //             specified range.

  // PARAMETERS: min - the lower bound of the range

  //             max - the upper bound of the range

  // RETURNS:    A random integer that is greater than or

  //             equal to min and less than or equal to max

  ///////////////////////////////////////////////////////////

  private static int generateRandomInt(int min, int max) {
    return (int) ((max - min + 1) * Math.random()) + min;
  }
}


syntax highlighted by Code2HTML, v. 0.9.1