CS111, Wellesley College, Spring 2003

Problem Set 9

Due on Tuesday, April 29

About this Problem Set

The purpose of this problem set is to give you experience with declaring Java classes, drawing object diagrams, using Java graphics, and playing with AnimationWorld. In Task 1, you will draw object diagrams showing the state of several objects. In Task 2, you will create a class the models simple cartoon faces, and draw these faces. In Task 3, you will create sprite classes for some simple animations.

There are also two optional extra credit problems. Extra Credit Challenge 1 is an open-ended opportunity to draw any picture(s) of your own design. In Extra Credit Challenge 2 is a chance for you to create your own animation.

All code for this assignment is available in the ps9_programs folder in the cs111 download directory on the CS fileserver.

How to turn in this Problem Set

You are required to turn in both a hardcopy and a softcopy. For general guidelines on problem set submission, including how to submit a softcopy and how to check if you softcopy submission was successful, click here. Please make sure to keep a copy of your work, either on a zip disk, or in your private directory (or, to play it safe, both).

Hardcopy Submission

Your hardcopy packet should consist of:
  1. The cover page;
  2. Your object diagram from Task 1;
  3. The final version of Faces.java from Task 2. If you are unable to complete all the subtasks, turn in the version of your code that solves those subtasks you have completed.
  4. The final version of Spinner.java from Task 3.
  5. The final version of MyPicture.java from (optional) Extra Credit Challenge 1.
  6. The final version of any sprite and animation classes you created for (optional) Extra Credit Challenge 2.
Staple these together, and slide the packet under the door of Elena's office (E127, in minifocus).

Softcopy Submission

You should submit your final version of your ps9_programs folder. In particular,

Task 1: Node Diagrams

The purpose of this problem is to give you some practice drawing object diagrams. Recall that an object diagram shows a collection of interconnected class instances and arrays:

Consider the following Java class declaration:


public class Node {

    public int value;
    public Node [] subnodes;

    public Node (int value, Node [] subnodes) {
	this.value = value; 
	this.subnodes = subnodes;
    }

    public Node (int value) {
	this.value = value; 
    }
}

The Node class describes instances that have two instance variables: a value variable that holds an integer, and a subnodes variable that holds an array of other Node instances.

Here is a sequence of statements that creates and manipulates Node instances:


Node [] nodes = new Node [5]; nodes[4] = new Node(4); nodes[3] = new Node(3, new Node [] {nodes[4]}); nodes[2] = new Node(2, new Node [] {nodes[4], nodes[3]}); nodes[1] = new Node(1, new Node [] {nodes[2], nodes[3]}); nodes[4].subnodes = new Node [] {nodes[1]}; nodes[1].subnodes[1].subnodes[0] = nodes[1].subnodes[0]; nodes[1].value = nodes[2].subnodes[1].subnodes[0].value + nodes[1].subnodes[0].subnodes[0].value;

In this problem, you are to draw a single object diagram that depicts the state of all objects (and their component variables) in ObjectLand after the above eight statements are executed. Your diagram should also include the local variable nodes.

Follow these conventions:


Task 2: Faces

In this problem, we will model simple cartoon faces like those shown below:

Each face is characterized by the following state:

For example, the following table shows the values for the color (r = red, g = green, and b = blue), happiness (h) and sleepiness (s) of each face in the corresponding position in the figure above:

r=255, g=0, b=0,
h=0.0,
s=0.0
r=255, g=63, b=0,
h=0.25,
s=0.0
r=255, g=127, b=0,
h=0.50,
s=0.0
r=255, g=191, b=0,
h=0.75,
s=0.0
r=255, g=255, b=0,
h=1.0,
s=0.0
r=255, g=0, b=63,
h=0.0,
s=0.25,
r=255, g=63, b=63,
h=0.25,
s=0.25
r=255, g=127, b=63,
h=0.50,
s=0.25
r=255, g=191, b=63,
h=0.75,
s=0.25
r=255, g=255, b=63,
h=1.0,
s=0.25
r=255, g=0, b=127,
h=0.0,
s=0.50
r=255, g=63, b=127,
h=0.25,
s=0.50
r=255, g=127, b=127,
h=0.50,
s=0.50
r=255, g=191, b=127,
h=0.75,
s=0.50
r=255, g=255, b=127,
h=1.0,
s=0.50
r=255, g=0, b=191,
h=0.0,
s=0.75
r=255, g=63, b=191,
h=0.25,
s=0.75
r=255, g=127, b=191,
h=0.50,
s=0.75
r=255, g=191, b=191,
h=0.75,
s=0.75
r=255, g=255, b=191,
h=1.0,
s=0.75
r=255, g=0, b=255,
h=0.0,
s=1.0,
r=255, g=63, b=255,
h=0.25,
s=1.0
r=255, g=127, b=255,
h=0.50,
s=1.0
r=255, g=191, b=255,
h=0.75,
s=1.0
r=255, g=255, b=255,
h=1.0,
s=1.0

We will represent a face by an instance of the Face class, which is described by the following contract:


Face Contract

Constructor Methods:

public Face (Color c, double h, double s)
Constructs and returns a Face instance whose color is c, whose happiness is h, and whose sleepiness is s.

public Face ()
Constructs and returns a Face instance whose color is yellow, whose happiness is 1.0, and whose sleepiness is 0.0.

Instance Methods:

public Color getColor()
Returns the color of this face.

public double getHappiness()
Returns the happiness of this face.

public double getSleepiness()
Returns the sleepiness of this face.

public void setColor (Color c)
Changes the color of this face to c.

public void setHappiness (double h)
If h is in the the range [0.0, 1.0], changes the happiness of this face to h. Otherwise, leaves h unchanged and displays an error message indicating h is out of range.

public void setSleepiness (double s)
If s is in the the range [0.0, 1.0], changes the sleepiness of this face to s. Otherwise, leaves s unchanged, and displays an error message indicating s is out of range.

public void draw (Graphics g, Rectangle r)
Draw a picture of this face inscribed in rectangle r within graphics context g. Note that Rectangle is Java's standard rectangle class in the java.awt package. See the Rectangle contract page for the Rectangle contract, and the the CS111 Contracts page for other contracts.

public String toString ()
Return a string representation of this face. A face with red/green/blue color values r, g, and b and with happiness h and sleepiness s should have the following string representation:

"Face[color=java.awt.Color[r=r,g=g,b=b]; happiness=h; sleepiness=s]"

Note that you can use the toString() method for colors to get the string representation of a color.


Task 2a: The Face Class

In this subtask, you will define a preliminary version of the Face class. A skeleton of this class can be found in the file Face.java within the Faces subfolder of the ps9 folder. You are flesh out the skelelton with instance variable declarations, constructor method declarations, and instance method declarations so that it implements the Face contract given above except for the draw() method. Rather than actually drawing the face in the given rectangle, your initial draw() method should simply display a string representation of the face in the Java Console window (via System.out.println).

For example, when you test the default applet ManyFaces.html, you should get the following output:

Face[color=java.awt.Color[r=255,g=0,b=0]; happiness=0.0; sleepiness=0.0]
Face[color=java.awt.Color[r=255,g=0,b=63]; happiness=0.0; sleepiness=0.25]
Face[color=java.awt.Color[r=255,g=0,b=127]; happiness=0.0; sleepiness=0.5]
Face[color=java.awt.Color[r=255,g=0,b=191]; happiness=0.0; sleepiness=0.75]
Face[color=java.awt.Color[r=255,g=0,b=255]; happiness=0.0; sleepiness=1.0]
Face[color=java.awt.Color[r=255,g=63,b=0]; happiness=0.25; sleepiness=0.0]
Face[color=java.awt.Color[r=255,g=63,b=63]; happiness=0.25; sleepiness=0.25]
Face[color=java.awt.Color[r=255,g=63,b=127]; happiness=0.25; sleepiness=0.5]
Face[color=java.awt.Color[r=255,g=63,b=191]; happiness=0.25; sleepiness=0.75]
Face[color=java.awt.Color[r=255,g=63,b=255]; happiness=0.25; sleepiness=1.0]
Face[color=java.awt.Color[r=255,g=127,b=0]; happiness=0.5; sleepiness=0.0]
Face[color=java.awt.Color[r=255,g=127,b=63]; happiness=0.5; sleepiness=0.25]
Face[color=java.awt.Color[r=255,g=127,b=127]; happiness=0.5; sleepiness=0.5]
Face[color=java.awt.Color[r=255,g=127,b=191]; happiness=0.5; sleepiness=0.75]
Face[color=java.awt.Color[r=255,g=127,b=255]; happiness=0.5; sleepiness=1.0]
Face[color=java.awt.Color[r=255,g=191,b=0]; happiness=0.75; sleepiness=0.0]
Face[color=java.awt.Color[r=255,g=191,b=63]; happiness=0.75; sleepiness=0.25]
Face[color=java.awt.Color[r=255,g=191,b=127]; happiness=0.75; sleepiness=0.5]
Face[color=java.awt.Color[r=255,g=191,b=191]; happiness=0.75; sleepiness=0.75]
Face[color=java.awt.Color[r=255,g=191,b=255]; happiness=0.75; sleepiness=1.0]
Face[color=java.awt.Color[r=255,g=255,b=0]; happiness=1.0; sleepiness=0.0]
Face[color=java.awt.Color[r=255,g=255,b=63]; happiness=1.0; sleepiness=0.25]
Face[color=java.awt.Color[r=255,g=255,b=127]; happiness=1.0; sleepiness=0.5]
Face[color=java.awt.Color[r=255,g=255,b=191]; happiness=1.0; sleepiness=0.75]
Face[color=java.awt.Color[r=255,g=255,b=255]; happiness=1.0; sleepiness=1.0]

Task 2b: Put on a Happy Face

In this part, you are to change the implementation of your draw() method so that it draws an alert, happy face with the face's color in the specified rectangle. Your method should ignore the actual happiness of the face and simply assume that it is 1.0. Similarly, your method should ignore the actual sleepiness of the face, and just assume that it is 0.0. However, your method should draw the face using the actual color of the face. So when you run the ManyFaces.html applet, the goal is to get the following picture:

Below is a diagram of a happy face inscribed in the unit square indicating the placement of the eyes, nose, and mouth. You should draw the happy face exactly as depicted in this diagram.

Notes:

Task 2c: Sleepy Faces

In this part, you are to modify the implementation of your draw() method so that it correctly shows droopy eyelids for sleepy faces, as shown below:

A face with sleepiness factor s (between 0.0 and 1.0) should have "eyelids" covering a fraction s of the height of the eyes from the top to the bottom. This effect can be achieved as follows:

Note that, according to its contract, the drawOval() method covers an area that is width + 1 pixels wide and height + 1 pixels tall. In contrast, the the fillOval() method covers an area that is width pixels wide and height pixels tall. Because of this discrepency, to get your picture to "look right" you may need to subtract 1 from the width and height arguments of drawOval().

Task 2d: Drawing Happiness

In this part, your goal is to modify the mouth-drawing part of your drawMethod() so that it takes into account the happiness of the face. A face with happiness 0.0 should have a line as a mouth, while larger happiness values (up to 1.0) should be drawn as filled half-ovals whose height is proportional to the happiness value.

For example, here are faces whose happiness value changes from left to right:

Here is the specification for the mouth that you should implement:

The above specification can be implemented via a single invocation of the fillArc() method that handles all happiness values between 0.0 and 1.0. The key to getting this to work is to determine the coordinates of the rectangle in which the arc is inscribed as a function of the happiness value. Do not attempt to do this by trial and error! Instead, determine the correct formula on a sheet of paper before you attempt to implement this part!


Task 3: Spinners

A Note on Viewing Applets

This problem description contains an embedded AnimationWorld applet. Experience shows that this applet may run fine in some browsers but not in others. If you cannot get the applet to work in one browser, try another one instead. If you can't find any browser that works, you can run the applet in CodeWarrior from the Test subfolder of ps9_programs/Spinners.

The following Spinners animation shows "spinning" disks of various sizes and colors:

In this problem, your goal is to flesh out the declaration of the Spinner class (a subclass of Sprite) within the Spinners folder so that it describes the behavior of spinning two-colored disks. Instances of Spinner should be created via the following constructor method:

public Spinner(int x, int y, int radius, int dRadius, Color color1, Color color2);
Creates a spinning disk with radius radius whose center is at the position (x, y) in the Java coordinate system. The disk appears to be "hanging" by a black string from the top of the window and appears to have two "sides", one of which is colored color1, and the other of which is colored color2. Initially a circle of color1 should be displayed. The "spinning" motion is simulated by changing the horizontal radius of the oval by the amount dRadius at every invocation of updateState(). The height of the oval should not change.

For example, the animation shown above is specified as follows:


  import java.awt.*; 

  class SpinnerAnimation extends Animation { 

    public SpinnerAnimation() {
      addSprite(new Spinner(200,100,50,3,Color.red,Color.blue));
      addSprite(new Spinner(250,150,125,1,Color.yellow,Color.green));
      addSprite(new Spinner(300,200,50,5,Color.pink,Color.gray));
      addSprite(new Spinner(400,200,100,2,Color.cyan,Color.magenta));
      setNumberFrames(Animation.NO_MAX_FRAMES);
    }

  }

To complete this problem, you will have to

Notes:

Extra Credit Challenge 1: Create Your Own Picture [up to 5 points]

This is a completely optional problem. You should only attempt it after completing the rest of the assignment.

You can use Java graphics as a tool to create fascinating pictures. Here is a chance to be artistically creative and earn some extra credit points at the same time.

For this challenge, flesh out the paint() method in the MyPicture class within the MyPicture folder to draw whatever picture you imagine. If you like, you can draw more than one by making copies of MyPicture.java where the files (and the contained class) have different names.

Notes:


Extra Credit Challenge 2: Open-Ended Animation [up to 10 points]

You can use AnimationWorld to create dazzling animations that show off your artistry and your programming talents. Here is a chance to be creative and earn some extra credit points at the same time.

For this challenge, build an animation of your own design. You may use existing sprites that we have studied, but you should create and use at least one new kind of sprite from scratch.

The MyAnimation subfolder of the ps9_programs folder contains a copy of the sprites and animations shown in Lecture 21. This is a good starting point for your own animations. You may edit the existing files or create ones of your own. Add any animations you create to MyShowcase.java.

Notes: