Graphic by Keith Ohlfs

CS111, Wellesley College, Fall 1999

Problem Set 10

Due: Saturday, December 11 by 4pm

[CS111 Home Page] [Syllabus] [Lecture Notes] [Assignments] [Programs] [Documentation] [Software Installation] [FAQ] [CS Dept.] [CWIS]

About this Problem Set

The goals of this problem set are:

This problem set has three problems (Problems 1, 2, and 3) that require turning something in. But it begins with another problem, Problem 0, that does not require turning anything in. Problem 0 is intended to familiarize you with the SketchWorld applets that we have been discussing in lecture. You should complete Problem 0 before attempting Problems 2 or 3. (Problem 1 is independent of Problem 0).

How to turn in this Problem Set

Softcopy submission: Save your modified Poly.java and FigureStackArrayDouble.java files in the ps10_programs folder. Upload the entire folder (including all the other .java and .class files -- everything) to your ps10 drop folder.

Hardcopy submission: Turn in only one package of hardcopy materials. Staple your files together and submit your hardcopy package by placing it in the box outside of Lyn's office (121B, behind the mini-focus consultant's desk). Your package should include printouts of

Reminders


Problem 0: Understanding Sketchworld

Note: There is nothing to turn in for this problem. Its purpose is to prepare you for Problems 2 and 3.

We have spent the last three lectures studying various sketching applets. In the last two lectures we focused on a collection of related applets known as SketchWorld. You will be extending the code for SketchWorld in Problems 2 and 3 of this assignment.

The purpose of this problem is to understand the organization of the SketchWorld classes and to study some of the classes in detail. All the classes mentioned here can be found in the SketchWorld folder of the ps10_programs folder. Due to some recent changes, some of the files are not exactly the same as the ones that we studied in class. You should study the files in the ps10_programs folder, rather than those in the lec23_programs and lec24_programs folders, as these will be the ones you are working with on this assignment.

The SketchWorld programs use a large number of classes. The following diagram shows an inheritance hierarchy of some of the more important classes used in SketchWorld:

The sheer number of classes in the diagram is daunting! But you don't have to understand every detail of every class. For most of the classes, it is only necessary to understand their contracts, and not their implementations. This is a key advantage of black box abstraction: it is possible to construct large programs out of parts that you can understand independently.

You are expected to study the classes that are boxed. It is particularly important to carefully study the classes in the thick-lined boxes, because you will be implementing classes similar to these (Poly and FigureStackArrayDouble). The classes in the thin-lined boxes closely interact with the ones in the thick-lined boxes, so it is worth studying them, too; but you need not understand them at the same level of detail.

For each boxed class, you should read through the code for that class, and ask yourself the following kinds of questions:

While inheritance can make code much shorter than it would be without inheritance, it is worth noting that inheritance can make code more difficult to read. When you're studying a method ZZZ() and you see a call to super.ZZZ(), you need to understand what the ZZZ() method of the superclass does. Even trickier, if you are studing the XXX() method of a class A and it calls the YYY() method, you can't assume that the YYY() method will always be the one in class A. It might very well be the case that the YYY() method is overridden by the YYY() method of the B subclass of A and again by the C subclass of B. So following your mental control dot around the code can cause you to bounce up and down the inheritance hierarchy! To assist you in studying the code, comments have been included to indicate which variables and methods are inherited and which are overridden.


Problem 1: Object Diagrams with Arrays

Background

Bud Lojack is a summer intern at Moon Macrosystems, where he working with a team on implementing the Java JDK 1.0.2 Abstract Window Toolkit (AWT) specification. In particular, Bud is implementing a subset of the Polygon class, which represents a sequence of connected lines. A Java polygon can also be thought of as the sequence of points that would need to be connected in order to make the sequence of lines. Here is the contract for the subset of the Polygon class that Bud is implementing:


Contract for the Polygon class:

INSTANCE VARIABLES:

  public int npoints;
  The number of points in this polygon.
 
  public int [] xpoints;
  An array whose first npoints slots (indices 0 through npoints - 1) are the x coordinates 
  of the point sequence in this polygon. The length of xpoints may be greater than npoints. 
  In this case, the contents of the slots with indices npoints through (xpoints.length - 1)
  should be ignored. 
 
  public int [] ypoints;
  An array whose first npoints slots (indices 0 through npoints - 1) are the y coordinates 
  of the point sequence in this polygon. The length of ypoints may be greater than npoints. 
  In this case, the contents of the slots with indices npoints through (ypoints.length - 1)
  should be ignored. 

CONSTRUCTOR METHODS:

  public Polygon ();
  Creates and returns a new empty polygon (a polygon with a sequence of 0 points).
	
  public Polygon (int [] xpoints, int [] ypoints, int npoints)
  Assume that npoints is less than or equal to xpoints.length and ypoints.length.
  Returns a new polygon with npoints points, whose x coordinates are given by 
  xpoints[0] through xpoints[npoints - 1], and whose y coordinates are given by 
  ypoints[0] through ypoints[npoints - 1]. The returned polygon should not share
  any structure with the arguments. That is, after the result of this constructor is returned,
  changes to the arrays passed as the xpoints and ypoints parameters should have no
  effect on the returned polygon, and and vice versa. 

NSTANCE METHODS:

  public void addPoint (int x, int y);
  Modifies this polygon to have the point (x,y) at the end of its sequence of points. 

Bud writes the following class to implement the above specification.


public
class Polygon { //---------------------------------------------------------------------- // INSTANCE VARIABLES // Because these are public, they can be seen by everyone else. public int [] xpoints; // Array of x coordinates of polygon points public int [] ypoints; // Array of y coordinates of polygon points public int npoints; //---------------------------------------------------------------------- // CONSTRUCTOR METHODS public Polygon () { xpoints = new int [0]; ypoints = new int [0]; npoints = 0; } public Polygon (int [] xpoints, int [] ypoints, int npoints) { this.xpoints = xpoints; this.ypoints = ypoints; this.npoints = npoints; } //---------------------------------------------------------------------- // INSTANCE METHODS public void addPoint (int x, int y) { xpoints = arrayPostpend(xpoints, x); ypoints = arrayPostpend(ypoints, y); npoints++; // Remember that we just added a point. }   //---------------------------------------------------------------------- // CLASS METHODS // Returns a new array with length a.length + 1 in which the first // a.length elements are those of 1 and the last element is n. private static int [] arrayPostpend(int [] a, int n) { int [] result = new int [a.length + 1]; for (int i = 0; i < a.length; i++) { result[i] = a[i]; } result[a.length] = n; return result; }


Task 1

Unfortunately, Bud's implementation has a bug. Show that Bud's implementation is buggy by drawing a single object diagram that shows the final result of executing the following sequence of statements:

Polygon p1 = new Polygon();
p1.addPoint(1,3); 
p1.addPoint(2,4);
int [] xs = p1.xpoints;
int [] ys = p1.ypoints;
Polygon p2 = new Polygon(ys, xs, 2); // Yes, the ys and xs are swapped.
Polygon p3 = new Polygon(xs, xs, 2); // Yes, there are two xs.
xs[0] = 5;

Your object diagram should include the following:

Be careful to accurately depict all sharing in your diagram.

In addition to drawing the diagram, briefly explain why Bud's implementation violates the Polygon contract.


Task 2

Bud's supervisor, Abby Stracksen, tells Bud that he can fix his implementation by changing his definition of his second Polygon constructor as follows

public Polygon (int [] xpoints, int [] ypoints, int npoints) {
    this.xpoints = copyArray(xpoints);
    this.ypoints = copyArray(ypoints);
    this.npoints = npoints;
  }

where copyArray is a new class method defined as follows:

// Returns a copy of the given array. 
private static int [] copyArray(int [] a) {
    int [] result = new int [a.length];
    for (int i = 0; i < a.length; i++) {
      result[i] = a[i];
    }
    return result;
  }

Suppose that Bud makes the changes that Abby has suggested. Draw a new object diagram that shows the result of executing the statements in Task 1 using the modified implementation of Polygon.


Remarks

The specification of Polygon given above is consistent with the Java JDK 1.0.2 AWT specification of the Polygon class (see http://cs111.wellesley.edu/~cs111/JDK-1.0.2-API/api/java.awt.Polygon.html ), but is less ambiguous. The Java JDK 1.0.2 AWT Polygon specification does not prohibit sharing between the arguments of the Polygon constructor and the resulting polygon. It also does not explain the relationship between npoints and the lengths of xpoints and ypoints.

For simplicity, a few instance methods of the Polygon class have been omitted in the Polygon contract given above.

You may wonder why npoints is allowed to be less that xpoints.length and ypoints.length. This gives the implementer more flexibility. See Problem 3 for another data structure implementation that takes advantage of this sort of flexibility.


Problem 2: Adding Polygons To SketchWorld

Background

The SketchpadEphemeral, SketchpadPermanent, and SketchpadEdit applets discussed in class support drawing lines, rectangles, and ovals. It would be nice to support the drawing of other kinds of graphical entities. In this problem, we will implement a SketchpadEditPoly applet that is just like SketchpadEdit except that it also supports the drawing of polygons. In SketchpadEditPoly, it is easy to create stunning pictures like this self-portrait of your lecturer:

You should begin this problem by experimenting with the SketchpadEditPoly applet in the Test subfolder of the SketchWorld folder. This applet has the behavior that we desire. The interface is just as in the SketchpadEdit applet except that the figure mode menu also has a "Polygon" option. If you select Polygon mode then you can draw polygons in the canvas. Pressing the mouse button will add a point to a "current" polygon. The most recently added point acts as an "anchor" for a "rubber band" line that follows the mouse as you move it. (It doesn't matter whether the mouse button is pressed down or not as you move the mouse; the rubber band line follows the mouse in either case.) If the sketch editor is in fill mode, you will see a "rubber sheet" rather than just a rubber band line attached to the mouse. Both the rubber band line and rubber sheet are drawn in XOR mode, so they are always black, regardless of what the specified color is.

To indicate that a point should be the last point of the current polygon, press the Shift key on the keyboard while pressing the mouse button. This will complete the current polygon, put the editor into paint mode, and paint the polygon in the specified color. Pressing the mouse button again without pressing the Shift key will start a new polygon. Thus, it possible to draw as many polygons as you want in Polygon mode. If the mouse ever leaves the canvas area, then the polygon currently being constructed will be deleted.

Once completed, a polygon can be manipulated like any other figure. So if you go into Edit mode, you can select a polygon, recolor it, raise it to the top of the figure stack, put it at the bottom of the figure stack, copy it, or delete it.

Most of this behavior has already been implemented for you. The files SketchpadEditPoly.java and PadEditPoly.java contain a mostly working implementation of the applet. In particular, PadEditPoly (which is a subclass of PadEdit and ultimately a subclass of Canvas) is responsible for tracking the mouse clicks and motions in Polygon mode and drawing the rubber band lines and sheets.

The one thing that is missing is the implementation of the Poly subclass of Figure that allows a polygon to be "remembered". Recall that instances of subclasses of Figure (such as the Line, Rect, and Oval classes studied in lecture) are used to remember the figures that the user has drawn on the canvas. These instances are stored in a "figure stack" maintained by the canvas and contain all the information nececessary to redraw the picture as well as to edit it.

When a polygon is completed (by pressing the mouse button and Shift key at the same time), a instance of the Poly class is created, stored in the figure stack, and drawn. This is accomplished by the following three lines of code within the polyPointAction() method of PadEditPoly :

      Poly polyFig = new Poly(penColor, fillMode, currentPolygon);
      figures.add(polyFig);
      polyFig.draw(this.getGraphics());
 

Your Task

The file Poly.java contains a skeleton of the Poly subclass of Figure. Your task is to flesh out the methods of the Poly class so that it works correctly. The Poly class has the following contract:


Contract for the Poly class

Constructor Method

public Poly (Color color, boolean fillMode, Polygon poly)
Constructs and a Poly instance with the given color, fill mode and polygon. You may assume that the polygon remembered by an instance of Poly is shared with the argument passed in the poly parameter. That is, later changes to the Polygon passed as the poly parameter may affect the Poly instance, and changes to the Poly instance may affect the poly parameter.

Instance Methods

public void draw (Graphics g)
Draws the polygon of this Poly figure, using its color and fill mode.

public Rectangle boundingBox()
Returns the smallest Rectangle containing the polygon of this Poly figure.

public void translate (int x, int y)
Translates the x-coordinates of points in the polygon of this Poly figure by x and the y-coordinates by y.

public Figure copy()
Returns a copy of this Poly figure. Changes to the copy should not affect this Poly, and vice versa.


Pay attention to the following notes when writing and testing your code:


Problem 3: Data Abstraction: Another Implementation of FigureStack

Background

The Benefits of Data Abstraction

The principle of data abstraction says that the user and the implementer of a program should communicate only through an explicit contract that hides unnecessary details about one from the other. There are two main benefits of data abstraction. First, in order to use an abstraction, a user need only understand its contract, and not its implementation. You have been taking advantage of this benefit all semester long when using BuggleWorld, PictureWorld, TurtleWorld, lists, trees, etc.

The second benefit is that the implementer has the flexibility to change the implementation as long as it still satisfies the contract. This makes it possible to improve the efficiency of the implementation of an abstraction without users having to change any of their code. This problem is intended to give you the flavor of this second benefit. If you go on to take CS230, you will see a lot more of this benefit.

The FigureStack Class

The PadPermanent class and its subclasses use a "figure stack" to remember the figures that have been drawn on the canvas. In SketchWorld, FigureStack is a so-called abstract class that specifies the contract for such a collection of figures. What makes it "abstract" is that it declares the contract for certain methods without saying how to implement them. A "concrete" subclass is required to implement all the abstract methods of an abstract superclass.

Because an abstract class is lacking code for some of its methods, it is not possible to make an instance of an abstract class. So an abstract class never has any constructor methods. However, it may have code for some instance and class methods; this code is inherited by all subclasses, just as usual.

Here is the contract for the abstract FigureStack class:


Contract for the FigureStack class

abstract public void draw (Graphics g);
Draws figures in this FigureStack from bottom up

abstract public Figure find (int x, int y);
Returns the topmost figure in this FigureStack whose bounding box contains the point (x,y). If there is no such figure returns null (i.e., Java's way of writing the null pointer).

abstract public void add (Figure f);
Adds the given figure to the top of this FigureStack.

abstract public void insertAtBottom (Figure f);
Inserts the given figure to the bottom of this FigureStack.

abstract public void remove (Figure f);
Removes the given figure from this FigureStack. Assume the given figure occurs at most once in FigureStack.


The FigureStackList Class

It is possible to implement the FigureStack contract in many ways. One such implementation is provided in the SketchWorld folder of ps10_programs: the FigureStackList class. This class is a concrete subclass of FigureStack that represents a figure stack as a list of figures ordered from the top down. For example, an instance of PadPermanent holding a stack of the three figures f3, f2, and f1 (in top down order) would be depicted as follows:

Adding Figure f4 to the top of the figure stack would change it to:

and subsequently adding Figure f5 to the top of the figure stack would change it to:

Finally, removing f3 would yield:

You should study the code in FigureStackList.java to understand how it implements the contract for FigureStack.

The FigureStackArray Class

An alternative representation of figure stacks is as an array whose length is the number of figures and whose contents are ordered from the bottom of the stack (low index) to the top of the stack (high index). In this representation, an instance of PadPermanent containing (in top down order) the figures f3, f2, and f1 would be depicted as:

Adding f4 to the figure stack would yield

and subsequently adding f5 would yield:

Finally, deleting f3 would give the following:

You have not been provided with an implementation of FigureStackArray, but you will consider its implementation in laboratory this week.

The FigureStackArrayDouble Class

The final representation of figure stacks that we will consider is one that is similar to the array representation used in FigureStackArray except that the array may be longer than the number of figures in the stack. In this case, we need an additional instance variable (call it size) that indicates how many of the initial slots are filled with figure in the stack; the rest of the slots contain unknown values, which we will call "garbage". Thus, for an array with n slots, slots 0 through size - 1 contain figures of the stack (in order from the bottom figure (low index) to the top figure (high index), and slots size through (n - 1) contain garbage.

Suppose that the initial array length is 4. Then in this representation, an instance of PadPermanent containing (in top down order) the figures f3, f2, and f1 would be depicted as:

Here, the size of 3 indicates that there are three figures in the stack. The fact that the last slot of the array is numbered 3 is just a coincidence; this slot contains garbage, which we will depict by a question mark.

Adding figure f4 to the top of the stack in this representation yields:

Note that the size instance variable has been incremented to 4 to reflect that there is an extra figure in the stack.

How do we add another figure (call it f5) to the stack? Since we have used all the slots, we need to make the array longer. In this situation, rather than just incrementing the array by a single new slot (as in FigureStackArray), we will instead double the size of the array to get four new slots, the first of which we will fill with f5:

Now we could add three more figures before it would be necessary to double the array size again, this time to 16 slots.

In the array doubling representation, the size of the array never shrinks. So if we now remove figure f3, we get the following configuration:

Here, the size instance variable has been decrement to 4, but the number of slots in the array is unchanged. Figures f4 and f5 have each been shifted left by one slot. It turns out that the slot with index 4 still holds figure f5, but we put a question mark there to emphasize that anything at slots size and above is considered garbage.

Why would we prefer FigureStackArrayDouble over FigureStackArray? Suppose we are going to add a large number of figures to the canvas. Then FigureStackArray requires creating a new array and copying the elements of the old array into the new one every time we add a new figure. In contrast, FigureStackArrayDouble only requires creating a new array and copying elements into it when the number of figures reaches powers of two, like 4, 8, 16, 32, 64, etc. So the total work done in creating arrays and copying elements is significantly less for FigureStackArrayDouble than it is for FigureStackArray. In the context of the sketching applet, it would be difficult to detect the different between the two approaches, because even the fastest humans can sketch only very slowly compared to the speed of the computer. But if we wrote a program that keeps adding new figures to the canvas at a fast rate, then we could see that FigureStackArrayDouble is significantly better than FigureStackArray. If you go on to take CS230, you will learn formal ways to compare the efficiency of the three implementations of FigureStack presented above.

Your Task

In this problem, you are to flesh out the skeleton in the file FigureStackArrayDouble.java to implement the FigureStack contract using the array doubling representation as described in the last subsection above. The FigureStackArrayDouble class has two instance variables:

  1. int size, which contains the number of figures in the figure stack.
  2. Figure [] figs, which contains an array of figures. Indices 0 through (size - 1) contain the figures of the figure stack in order from bottom (low index) to top (high index). Indices size through (figs.length - 1) contain garbage.

When implementing and testing FigureStackArrayDouble, pay attention to the following notes: