Computer Science 252
Problem Solving with Java

Spring 2014, The College of Saint Rose

FallingThings Demo

A working demo of FallingThings will appear below. Click inside the applet to interact with it.



FallingThings BlueJ Project

Click here to download a BlueJ project for FallingThings.


FallingThings Source Code

The Java source code for FallingThings is below. Click on a file name to download it.


FallingThings.java

import objectdraw.*;
import java.awt.*;

/*
 * Example FallingThings: an example of the Drawable2DInterface to
 * generate objects that can fall down the canvas using a generic
 * active object.
 *
 * Jim Teresco, The College of Saint Rose, CSC 252, Spring 2014
 *
 * $Id: FallingThings.java 2329 2014-02-27 02:46:26Z terescoj $
 */

public class FallingThings extends WindowController {

    // the Image objects we'll need to pass to our generators
    private Image birdImage, knightImage;
    
    // begin just loads some images
    public void begin() {
        birdImage = getImage("WhiteBird.png");
        knightImage = getImage("GoldenKnight.png");
    }
    
    // Each time we press the mouse, we'll randomly create a
    // generator for the objects that will fall down the canvas.
    // We pass in the Image objects for each VisibleImage we
    // might generate as well
    public void onMousePress(Location point) {
    
        new ItemGenerator(birdImage, knightImage, canvas);
    }

}

FallingThing.java


/**
 * An active object that is capable of animating any object type
 * that implements the Drawable2DInterface interface in Objectdraw.
 * 
 * @author Jim Teresco 
 */

import objectdraw.*;
import java.util.Random;

public class FallingThing extends ActiveObject {

    // settings for a simple constant speed animation
    private static final int MOVE_BY = 4;
    private static final int DELAY = 33;
    
    // our item to have fall
    private Drawable2DInterface thing;
    
    // the y-coordinate of the bottom of the canvas
    private double yMax;
    
    // constructor remembers a few things, chooses a random
    // position, places the item just off the top of the canvas
    // and starts things up
    public FallingThing(Drawable2DInterface thing, DrawingCanvas c) {
        
        // remember what we need to remember for the run method
        this.thing = thing;
        yMax = c.getHeight();
        
        // get the item into a random starting position just off the screen
        Random r = new Random();
        double width = thing.getWidth();
        double height = thing.getHeight();
        
        double xPos = r.nextDouble() * (c.getWidth() - width);
        thing.moveTo(xPos, -height);
        
        start();
    }
    
    // the life of this falling object
    public void run() {
        
        while (thing.getY() < yMax) {
            thing.move(0, MOVE_BY);
            pause(DELAY);
        }
        
        thing.removeFromCanvas();
    }
}

ItemGenerator.java


/**
 * An active object that generates, randomly, items to fall down
 * the canvas using another, generic, active object that can
 * control the fall of any object that implements the Objectdraw
 * Drawable2DInterface interface.
 * 
 * @author Jim Teresco
 */

import objectdraw.*;
import java.awt.*;
import java.util.Random;

public class ItemGenerator extends ActiveObject {

    // how many items will each instance generate?
    private static final int NUM_ITEMS = 100;

    // we'll need some random numbers
    private Random r = new Random();

    // the images we'll use for some of our objects
    private Image birdImage, knightImage;

    // and we'll need to remember our canvas
    private DrawingCanvas canvas;

    // the constructor need only remember a few things 
    // and start up the thread
    public ItemGenerator(Image b, Image k, DrawingCanvas c) {

        birdImage = b;
        knightImage = k;
        canvas = c;

        start();
    }

    // the run method generates our objects periodically
    public void run() {

        // we generate 100 objects per ItemGenerator
        for (int i = 0; i < NUM_ITEMS; i++) {
            // A variable that will hold an object to be animated, and
            // since it can be one of many object types, we declare it
            // as a Drawble2DInterface.  But by doing so, we agree only
            // to interact with that object only through the methods
            // specified by that interface.
            Drawable2DInterface item;

            // we'll pick a random number that determines which of the many
            // types of objects actually is going to be created
            int itemType = r.nextInt(10);
            switch (itemType) {
                case 0:
                    item = new FilledRect(0, 0, 50, 50, canvas);
                    item.setColor(Color.red);
                    break;
                case 1:
                    item = new FilledOval(0, 0, 50, 50, canvas);
                    item.setColor(Color.blue);
                    break;
                case 2:
                    item = new FramedRect(0, 0, 50, 50, canvas);
                    item.setColor(Color.green);
                    break;
                case 3:
                    item = new FramedOval(0, 0, 50, 50, canvas);
                    item.setColor(Color.magenta);
                    break;
                case 4:
                    item = new FilledArc(0, 0, 50, 50, 45, 270, canvas);
                    item.setColor(Color.yellow);
                    break;
                case 5:
                    item = new Text("I'm falling!", 0, 0, canvas);
                    item.setColor(Color.orange);
                    break;
                case 6:
                    item = new VisibleImage(birdImage, 0, 0, canvas);
                    break;
                case 7:
                    item = new VisibleImage(knightImage, 0, 0, canvas);
                    break;
                case 8:
                    item = new TShirt(0, 0, canvas);
                    break;
                default:
                    item = new NiceBBall(0, 0, 75, canvas);
                    break;
            }
            
            new FallingThing(item, canvas);
            pause(500);  // generate one every half second
        }
    }

}

NiceBBall.java

import objectdraw.*;
import java.awt.Color;
import java.util.Random;

/*
 * A class that implements a nice looking basketball, this one implementing
 * the Drawable2DInterface from Objectdraw.
 *
 * Jim Teresco, The College of Saint Rose, Spring 2014
 *
 * $Id: NiceBBall.java 2329 2014-02-27 02:46:26Z terescoj $
 */

public class NiceBBall implements Drawable2DInterface {

    // the color to draw the ball
    private static final Color BALL_ORANGE = new Color(250, 85, 10);

    // size and starting angles for cut arc in degrees
    private static final int CUTSIZE = 100;
    private static final int RIGHTCUTSTART = 90 + (180 - CUTSIZE) / 2;
    private static final int LEFTCUTSTART = 270 + (180 - CUTSIZE) / 2;

    // the orange part of the ball
    private FilledOval body;

    // the border of the ball
    private FramedOval border;

    // the two curves on the sides of the ball
    private FramedArc leftCut, rightCut;

    // the vertical and horizontal lines through the ball
    private Line vert, horiz;

    // to pick new random color for ball
    private Random colorPicker = new Random();

    /*
     * Create a new basketball on the given DrawingCanvas.  Note how this class
     * does not know about the default "canvas", since that's available only in
     * classes that extend the WindowController class.
     */
    public NiceBBall(double left, double top, double size, DrawingCanvas aCanvas) {

        // draw the circles that make it look like a ball
        body = new FilledOval(left, top, size, size, aCanvas);
        body.setColor(BALL_ORANGE);
        border = new FramedOval(left, top, size, size, aCanvas);

        // draw the lines and arcs that make it look like a basketball
        rightCut = new FramedArc(left + size * 2 / 3, top, size, size, RIGHTCUTSTART, CUTSIZE, aCanvas);
        leftCut = new FramedArc(left - size * 2 / 3, top, size, size, LEFTCUTSTART, CUTSIZE, aCanvas);
        vert = new Line(left + size / 2, top, left + size / 2, top + size, aCanvas);
        horiz = new Line(left, top + size / 2, left + size, top + size / 2, aCanvas);

    }

    /*
     * Move the ball by specified offsets.
     */
    public void move(double dx, double dy) {
        // move each part of the ball by the offsets provided
        body.move(dx, dy);
        border.move(dx, dy);
        vert.move(dx, dy);
        horiz.move(dx, dy);
        leftCut.move(dx, dy);
        rightCut.move(dx, dy);
    }

    /*
     * Check to see if the ball contains a specified location.
     */
    public boolean contains(Location point) {
        return body.contains(point);
    }

    /*
     * Move the ball to a specified position. 
     */
    public void moveTo(double x, double y) {
        // determine how far away (x,y) is
        double dx, dy;
        dx = x - body.getX();
        dy = y - body.getY();

        // move each part of the ball by the offset,
        //   using our own move method.
        this.move(dx, dy);
    }

    /*
     * Return the x coordinate of ball's corner.
     */
    public double getX() {
        return body.getX();
    }

    /*
     * Return the y coordinate of ball's corner.
     */
    public double getY() {
        return body.getY();
    }
    // we are required by Drawable2DInterface to provide an overlaps method
    // which just checks if the "body" here overlaps the other
    public boolean overlaps(Drawable2DInterface other) {

        return body.overlaps(body);
    }

    // getLocation required by Drawable2DInterface
    public Location getLocation() {

        return body.getLocation();
    }

    // we also must provide getWidth and getHeight
    public double getWidth() {

        return body.getWidth();
    }

    public double getHeight() {

        return body.getHeight();
    }

    // also stacking order methods
    public void sendToFront() {

        body.sendToFront();
        border.sendToFront();
        rightCut.sendToFront();
        leftCut.sendToFront();
        vert.sendToFront();
        horiz.sendToFront();
    }

    public void sendToBack() {

        border.sendToBack();
        rightCut.sendToBack();
        leftCut.sendToBack();
        vert.sendToBack();
        horiz.sendToBack();
        body.sendToBack();
    }

    public void sendBackward() {

        border.sendBackward();
        rightCut.sendBackward();
        leftCut.sendBackward();
        vert.sendBackward();
        horiz.sendBackward();
        body.sendBackward();
    }

    public void sendForward() {

        border.sendForward();
        rightCut.sendForward();
        leftCut.sendForward();
        vert.sendForward();
        horiz.sendForward();
        body.sendForward();
    }

    // standard get and set color needed by Drawable2DInterface
    public void setColor(Color c) {

        body.setColor(c);
    }

    public Color getColor() {

        return body.getColor();
    }

    public void moveTo(Location newLoc) {

        moveTo(newLoc.getX(), newLoc.getY());
    }

    // hiding and showing
    public boolean isHidden() {

        return body.isHidden();
    }

    public void show() {

        border.show();
        rightCut.show();
        leftCut.show();
        vert.show();
        horiz.show();
        body.show();
    }

    public void hide() {

        border.hide();
        rightCut.hide();
        leftCut.hide();
        vert.hide();
        horiz.hide();
        body.hide();
    }

    public DrawingCanvas getCanvas() {

        return body.getCanvas();
    }

    public void addToCanvas(DrawingCanvas c) {

        border.addToCanvas(c);
        rightCut.addToCanvas(c);
        leftCut.addToCanvas(c);
        vert.addToCanvas(c);
        horiz.addToCanvas(c);
        body.addToCanvas(c);
    }

    public void removeFromCanvas() {

        border.removeFromCanvas();
        rightCut.removeFromCanvas();
        leftCut.removeFromCanvas();
        vert.removeFromCanvas();
        horiz.removeFromCanvas();
        body.removeFromCanvas();
    }        
        
}

TShirt.java

import objectdraw.*;
import java.awt.*;
import java.util.Random;

/*
 * A T-shirt object, this one implements the Objectdraw built in
 * interface Drawable2DInterface.
 *
 * Jim Teresco, The College of Saint Rose, Spring 2014
 * Based on example from Williams College CSCI 134
 *
 * $Id: TShirt.java 2329 2014-02-27 02:46:26Z terescoj $
 */

public class TShirt implements Drawable2DInterface {

    // constants to define the size of the shirt
    // note how in this case, all are defined relative
    // to a single "SIZE" that we can change and
    // have all components change in an appropriate
    // proportion.
    private static final double SIZE = 120;

    private static final double SLEEVE_WIDTH = SIZE;
    private static final double SLEEVE_HEIGHT = 0.2*SIZE;

    private static final double BODY_WIDTH = 0.6*SIZE;
    private static final double BODY_HEIGHT = (0.65)*SIZE;

    private static final double BODY_INSET = 0.2*SIZE;

    private static final double NECK_WIDTH = 0.3*SIZE;
    private static final double NECK_HEIGHT = 0.06*SIZE;

    private static final double NECK_INSET = 0.35*SIZE;

    // rectangles that form a border around the shirt
    private FramedRect sleeveTrim, bodyTrim;   
    private FramedOval neckTrim;				

    // rectangles that form the interior color of the shirt
    private FilledRect body, sleeves;	
    private FilledOval neck;					

    // Current color of the shirt
    private Color hue;							

    // Random number generator used to select next object
    private Random generator = new Random();

    // create a new T-shirt with its upper left corner at (x,y).
    public TShirt(double x, double y, DrawingCanvas canvas) {
        
        // create boundary rectangles
        sleeveTrim = new FramedRect(x, y + NECK_HEIGHT/2, SLEEVE_WIDTH, 
            SLEEVE_HEIGHT, canvas);
        bodyTrim = new FramedRect(x + BODY_INSET, y + NECK_HEIGHT/2,
            BODY_WIDTH, BODY_HEIGHT, canvas);

        // create interior rectangles
        sleeves = new FilledRect(x+1, y+NECK_HEIGHT/2+1, SLEEVE_WIDTH-1,
            SLEEVE_HEIGHT-1, canvas);
        body = new FilledRect(x+BODY_INSET+1, y+NECK_HEIGHT/2+1,
            BODY_WIDTH-1, BODY_HEIGHT-1, canvas);

        // give it a neck hole
        neck = new FilledOval(x + NECK_INSET, y, NECK_WIDTH,
            NECK_HEIGHT, canvas);
        neckTrim = new FramedOval(x + NECK_INSET, y, NECK_WIDTH, 
            NECK_HEIGHT, canvas);

        // set the interior color
        setColor();
    }

    // move the t-shirt by specified offsets.
    public void move(double xOffset, double yOffset) {
        
        body.move(xOffset,yOffset);
        neck.move(xOffset,yOffset);
        sleeves.move(xOffset,yOffset);
        bodyTrim.move(xOffset,yOffset);
        sleeveTrim.move(xOffset,yOffset);
        neckTrim.move(xOffset,yOffset);
    }

    // remove the t-shirt from the canvas.
    public void removeFromCanvas() {
        
        body.removeFromCanvas();
        neck.removeFromCanvas();
        sleeves.removeFromCanvas();
        bodyTrim.removeFromCanvas();
        sleeveTrim.removeFromCanvas();
        neckTrim.removeFromCanvas();
    }

    // move the t-shirt to a new upper-left coordinate position
    public void moveTo(double x, double y) {
        
        move(x - sleeves.getX(), y - neck.getY());
    }

    // returns true if the t-shirt contains the point; false otherwise
    public boolean contains(Location pt) {
        return body.contains(pt) || sleeves.contains(pt) || neck.contains(pt);

    }

    // pick a new random color for the shirt
    private void setColor() {

        hue = new Color(generator.nextInt(256),
            generator.nextInt(256),
            generator.nextInt(256));

        body.setColor(hue);
        sleeves.setColor(hue);
        neck.setColor(hue);
    }

    // we are required by Drawable2DInterface to provide an overlaps method
    // which just checks if the essential parts overlap the other object
    public boolean overlaps(Drawable2DInterface other) {
        
        return other.overlaps(body) || other.overlaps(sleeves) || other.overlaps(neck);
    }
    
    // getLocation, getX, getY are required by Drawable2DInterface
    public Location getLocation() {
        
        return new Location(sleeves.getX(), neck.getY());
    }
    
    public double getX() {
        
        return sleeves.getX();
    }
    
    public double getY() {
        
        return neck.getY();
    }
    
    // we also must provide getWidth and getHeight
    public double getWidth() {
        
        return SLEEVE_WIDTH;
    }
    
    public double getHeight() {
        
        return body.getY() + body.getHeight() - neck.getY();
    }
    
    // also stacking order methods
    public void sendToFront() {
        
        sleeveTrim.sendToFront();
        bodyTrim.sendToFront();
        sleeves.sendToFront();
        body.sendToFront();
        neck.sendToFront();
        neckTrim.sendToFront();
       
    }
    
    public void sendToBack() {
        
        neckTrim.sendToBack();
        neck.sendToBack();
        body.sendToBack();
        sleeves.sendToBack();
        bodyTrim.sendToBack();
        sleeveTrim.sendToBack();
    }
    
    public void sendBackward() {
        
        sleeveTrim.sendBackward();
        bodyTrim.sendBackward();
        sleeves.sendBackward();
        body.sendBackward();
        neck.sendBackward();
        neckTrim.sendBackward();
        
    }
    
     public void sendForward() {
        
        sleeveTrim.sendForward();
        bodyTrim.sendForward();
        sleeves.sendForward();
        body.sendForward();
        neck.sendForward();
        neckTrim.sendForward();
        
    }
    
    // standard get and set color needed by Drawable2DInterface
    public void setColor(Color c) {
        
        sleeves.setColor(c);
        body.setColor(c);
        neck.setColor(c);
    }
    
    public Color getColor() {
        
        return sleeves.getColor();
    }
    
    public void moveTo(Location newLoc) {
        
        moveTo(newLoc.getX(), newLoc.getY());
    }
    
    // hiding and showing
    public boolean isHidden() {
        
        return body.isHidden();
    }
    
    public void show() {
        
        body.show();
        sleeves.show();
        neck.show();
        bodyTrim.show();
        neckTrim.show();
        sleeveTrim.show();
    }
    
    public void hide() {
        
        body.hide();
        sleeves.hide();
        neck.hide();
        bodyTrim.hide();
        neckTrim.hide();
        sleeveTrim.hide();
    }
    
    public DrawingCanvas getCanvas() {
        
        return body.getCanvas();
    }
    
    public void addToCanvas(DrawingCanvas c) {
        
        body.addToCanvas(c);
        sleeves.addToCanvas(c);
        neck.addToCanvas(c);
        bodyTrim.addToCanvas(c);
        neckTrim.addToCanvas(c);
        sleeveTrim.addToCanvas(c);
    }
}