CS 51 Homework Laboratory # 8
Scribbler

Objective: To gain more experience using recursion and recursive data structures.

We're now back with a regular lab, so you will need to come to lab with a design for all of the steps in the "How to Proceed" section. This week, you will be implementing a program we call "Scribbler." You have seen a simpler version of this program in class as a demo.




The image above will be a working version of the Scribbler if your web browser supports Java applets. You should experiment with this demonstration version after you finish reading the lab handout to make sure you understand exactly what we have in mind.

The Scribbler program has three modes: Draw mode, Move mode, and Color mode. The program starts in Draw mode. Draw mode, Move mode, and Color mode are selected by pressing buttons, and the color used by Color mode is selected by choosing a color from a choice component filled with color names. The modes behave as follows:

Draw mode:
Drag to draw a new Scribble on the canvas.
Move mode:
Drag a Scribble by pressing the mouse on it and dragging.
Color mode:
Click on any Scribble to change its color to that selected in the color choice component.

The program also has an "Erase Last" button that will erase the most recently drawn Scribble. Only the most recently drawn Scribble is erased. Pressing the button repeatedly will erase Scribbles in the reverse order of which they were drawn. Make sure your program does not crash after erasing the last one!

How to Proceed

The starter for this lab is a working Scribbler, but it has only the Draw mode and a simplified Move mode that is capable of moving only the most recently drawn Scribble. You will need to add code that will manage your Scribbles to allow the various modes and the "Erase last" button to work correctly.

There are a number of step-by-step approaches you could take to solve this problem, but it is important that you have a plan, and that you add functionality one step at a time. Here is one possible ordering of the tasks. We recommend that you develop and test your program incrementally - make sure you have a working implementation at each step before moving on to the next.

  1. Implement a simplified Color mode. This is similar to Move mode, except that you set the color of the Scribble that contains the mouse point instead of moving it. To do this, you will have to add a setColor method to the ScribbleIfc interface, and the Scribble and EmptyScribble classes. Initially, you will only be able to set the color of the most recently drawn Scribble.
  2. Implement a simplified Erase mode. Here, you respond to the "Erase last" button's actionPerformed event by deleting the most recently drawn Scribble from the canvas. For now, you will only be able to erase the most recently drawn Scribble. A second button press will do nothing.
  3. Since you need to keep track of a number of Scribbles, you will need to define classes representing a collection of Scribbles. The interface for these classes will be called ScribbleCollectionIfc, and the classes implementing that interface will be named EmptyScribbleCollection and ScribbleCollection. This interface and classes will be similar to the ball lists in the ChainReaction examples in Lecture 21.

    Just as we do not know how many line segments will make up a Scribble when we start to draw one, we will not know how many Scribbles will be drawn and stored in our ScribbleCollection. In your Scribbler class you will need a variable with type ScribbleCollectionIfc that will be initialized with an object created from EmptyScribbleCollection. You will add new Scribbles to it using the constructor from ScribbleCollection as they are drawn. Consider carefully what methods your scribble collection needs to be able to support the functionality of the four modes. We have provided the skeleton of a ScribbleCollectionIfc interface, and ScribbleCollection and EmptyScribbleCollection classes.

Sketch of classes provided for this program

We are providing some classes and interfaces to help you get started. These are almost identical to the Scribbler examples from lecture. Please download the starter to see the classes and interfaces we provide before working on your design. We also include the startup code for the Scribbler class and the ScribbleIfc interface.

Submitting Your Work

The lab is due at 11 PM two days after your lab. When your work is complete you should deposit in the appropriate dropoff folder a folder that contains your program and all of the usual files needed by JBuilder. Make sure the folder name includes your name and the phrase "Lab 8". Also make sure that your name is included in the comment at the top of each Java source file.

Before turning in your work, be sure to double check both its logical organization and your style of presentation. Make your code as clear as possible and include appropriate comments describing major sections of code and declarations.

Grading Point Allocations

Value

Feature
Design preparation (3 points total)
1 points Simplified color mode
1 points Erase mode
1 point ScribbleCollection class
Code Quality (5 points total)
2 points Descriptive comments
1 point Good names
1 point Good use of constants
1 point Appropriate formatting
Design (6 points total)
1 point Good use of boolean expressions, loops, conditionals
1 point Not doing more work than necessary
2 points Appropriate methods in ScribbleCollection
2 points Appropriate recursive structure in ScribbleCollection
Correctness (6 points total)
1 point Drawing the GUI components correctly
1 point Switching among modes correctly
1 point Draw mode adds correctly to the ScribbleCollection
1 point Move mode works correctly
1 point Color mode works correctly
1 point Erase button works correctly

Sketch of classes provided for this program

We are providing several classes and interfaces to help you get started. We include here printouts of the main program and the classes and interface for a single scribble. These are very similar to the Scribbler examples from lecture. Here is the code from the basic Scribbler class. You may need to add additional methods to it:

// A very simple drawing program.
public class Scribbler extends WindowController
                       implements ActionListener {

   // user modes
   // using ints rather than boolean to allow for extension to
   // other modes
   private static final int DRAWING = 1;
   private static final int MOVING = 2;
   private static final int COLORING = 3;

   // empty scribble as placeholder when nothing there.
   private static final EmptyScribble empty = new EmptyScribble();

   // the current scribble
   private ScribbleIfc currentScribble;

   // the collection of scribbles
   private ScribbleCollectionIfc scribbles;

   // stores last point for drawing or dragging
   private Location lastPoint;

   // whether the most recent scribble has been selected for moving
   private boolean draggingScribble;

   // buttons that allow user to select modes
   private JButton setDraw, setMove, setErase, setColor;

   // Choice button to select color
   private JComboBox chooseColor;

   // new color for scribble
   private Color newColor;

   // label indicating current mode
   private JLabel modeLabel;

   // the current mode -- drawing mode by default
   private int chosenAction = DRAWING;

   // create and hook up the user interface components
   public void begin() {

      setDraw = new JButton("Draw");
      setMove = new JButton("Move");
      setColor = new JButton("Color");
      setErase = new JButton("Erase last");

      JPanel buttonPanel = new JPanel();
      buttonPanel.add(setDraw);
      buttonPanel.add(setMove);
      buttonPanel.add(setColor);
      buttonPanel.add(setErase);

      chooseColor = new JComboBox();
      chooseColor.addItem("red");
      chooseColor.addItem("blue");
      chooseColor.addItem("green");
      chooseColor.addItem("yellow");
      Panel choicePanel = new Panel();
      choicePanel.add(chooseColor);

      Panel controlPanel = new Panel(new GridLayout(3,1));
      modeLabel = new JLabel("  Current mode: drawing");
      controlPanel.add(modeLabel);
      controlPanel.add(buttonPanel);
      controlPanel.add(choicePanel);

      add(controlPanel, BorderLayout.SOUTH);

      // add listeners
      setDraw.addActionListener(this);
      setMove.addActionListener(this);
      setErase.addActionListener(this);
      setColor.addActionListener(this);
      
      // make the current scribble empty
      currentScribble = empty;
      
      validate();
   }

   // if in drawing mode then start with empty scribble
   // if in moving mode then prepare to move
   public void onMousePress(Location point) {
      if (chosenAction == DRAWING) {
             // start with an empty scribble for drawing
         currentScribble = empty;
      } else if (chosenAction == MOVING) {
             // check if user clicked on current scribble
          draggingScribble = currentScribble.contains(point);
      }

      // remember point of press for drawing or moving
      lastPoint = point;
   }

   // if in drawing mode, add a new segment to scribble
   // if in moving mode then move it
   public void onMouseDrag(Location point) {
      if (chosenAction == DRAWING) {
         // add new line segment to current scribble
         Line newSegment = new Line(lastPoint, point, canvas);

         currentScribble =
               new Scribble(newSegment, currentScribble);
         
      } else if (chosenAction == MOVING) {
    	  // if dragging, move current scribble
         if (draggingScribble) {
            currentScribble.move(point.getX() - lastPoint.getX(),
                                 point.getY() - lastPoint.getY());
         }
      }
      
      // update for next move or draw
      lastPoint = point;
   }

   public void onMouseRelease(Location point) {
      // Add code when have collection of scribbles
   }

   // Set mode according to button pressed.
   public void actionPerformed(ActionEvent e) {
      if (e.getSource() == setDraw) {
         chosenAction = DRAWING;
         modeLabel.setText(" Current mode: drawing");
      } else if (e.getSource() == setMove) {
         chosenAction = MOVING;
         modeLabel.setText("  Current mode: moving");
      } 
   }
}

Here is the starting code for ScribbleIfc:

public interface ScribbleIfc
{
   // returns whether point is contained in scribble
   boolean contains(Location point);

   // move scribble by dx in x-direction and dy in y-direction
   void move(double dx, double dy);
}

Here is the starting code for Scribble:

// A class to represent a non-empty scribble
public class Scribble implements ScribbleIfc {
   private Line first;			// an edge line of the scribble
   private ScribbleIfc rest;		// the rest of the scribble

   public Scribble(Line segment, ScribbleIfc theRest) {
      first = segment;
      rest = theRest;
   }

   // returns true iff the scribble contains the point
   public boolean contains(Location point) {
      return (first.contains(point) || rest.contains(point));
   }

   // the scribble is moved xOffset in the x direction
   //    and yOffset in the y direction
   public void move(double xOffset, double yOffset) {
      first.move(xOffset, yOffset);
      rest.move(xOffset, yOffset);
   }
}

Here is the starting code for EmptyScribble:

/* Class representing an empty scribble */
public class EmptyScribble implements ScribbleIfc{
   public EmptyScribble() {
   }

   // point is never in an empty scribble!
   public boolean contains(Location point) {
      return false;
   }

   // nothing to move, so do nothing!
   public void move(double dx, double dy) {
   }
}

The start up code for the collection interface and classes is omitted here.


Computer Science 051
Department of Computer Science
Pomona College