//////////////////////////////////////////////////////////////
// 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