TopRecursive Data StructuresScribbles

Scribbles

The scribble program we wrote early in the semester was definitely limited. Suppose the user wants to move around the things they've drawn, or might want to change their color, or perhaps even erase them.

Consider how we build up a scribble: initially, a scribble is empty. When the user presses the mouse, they signal their intent to draw a scribble, but there's nothing there yet. As soon as a mouse drag is detected, we attach a new line to the existing (empty at first) scribble. With each subsequent drag, we attach a new line segment to the existing scribble.

So a scribble is simply a line attached to a slightly smaller scribble.

Thus we will define two kinds of scribbles: empty scribbles and non-empty scribbles.

Step 1: define the interface:

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);
}

Step 2: define the base class:

public class EmptyScribble implements ScribbleIfc{
   public EmptyScribble() {  // nothing to do - can be omitted
   }

   // 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) {
   }
}

Because the constructor takes no arguments and does not do anything, we may actually leave it out. If a class has no constructor, Java automatically inserts an empty constructor like the one above. Because nothing shows up, contains is always false and move does nothing (but must occur in class!).

Unlike with RingedTarget, we do not construct a non-empty Scribble all at once - a Scribble needs to constructed piece by piece (or, to be more specific, Line by Line) as the user drags the mouse. To see how this is done, consider the constructor for the Scribble class given below:

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);
   }
}

Step 3: As discussed earlier, a Scribble is simply a Line and a smaller Scribble. The Line is named "first", while the smaller Scribble is named "rest" (standing for the rest of the Scribble). The rest of the scribble may be either empty or non-empty. Its type is ScribbleIfc so that it can hold either type of scribble. The constructor clearly creates an scribble with one extra line over the old one.

Step 4: The definitions of the contains and move methods should look familiar as they are essentially the same as those for FullBullsEye.

The program starts with an empty scribble and creates a new scribble out of the old scribble and the new line segment formed by the most recent drag.

Demo: SimpleRecScribbler
A last example: Chain reaction


TopRecursive Data StructuresScribbles