This week, you will be implementing a program we call "Scribbler." This is a more sophisticated version of the scribble class you've seen in lecture as a demo. While no design is required for this lab, we strongly recommend that you show up with a plan for at least steps 1 and 2 in the "How to Proceed" section.
You are encouraged to work in pairs on this program. You must do all of the design and coding together. Only turn in one copy of the program, but make sure both of your names are on the folder and at the top of each class.
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:
The program also has an "Erase Last" button that will erase the most recently drawn Scribble. Unlike our simple version in class, if you press the button repeatedly it will continue to erase Scribbles in the reverse order of which they ar drawn.
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.
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. Because of this you may not use an array to keep track of these. Instead, we're going to do it with recursion.
Look at the ScribbleCollectionIfc interface. Like the ChainReaction method, we're going to keep track of our collection of Scribbles by keeping a reference to the current Scribble and then the rest of the collection.
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.
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.
The lab is due at 11 PM on Monday. When your work is complete you should drop the folder in the dropbox. Make sure the folder name begins with your name(s) and that it includes the phrase "Lab 11". Also make sure that your name(s) is (are) 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 AllocationsValue | Feature |
Style (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 (9 points total) | |
1.5 point | Drawing the GUI components correctly |
1.5 point | Switching among modes correctly |
1.5 point | Draw mode adds correctly to the ScribbleCollection |
1.5 point | Move mode works correctly |
1.5 point | Color mode works correctly |
1.5 point | Erase button works correctly |
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. We have also include the starting ScribbleCollectionIfc interface which you will need to modify.
public interface ScribbleCollectionIfc { // returns the scribble that contains the point; // if none contain it, returns an empty scribble ScribbleIfc scribbleSelected(Location point); // returns the first scribble in the list; // returns null if the list is empty. ScribbleIfc getFirst(); // returns the list of scribbles excluding the first. // returns null if the list is empty. ScribbleCollectionIfc getRest(); }
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 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 Button setDraw, setMove, setErase, setColor; // Choice button to select color private Choice chooseColor; // new color for scribble private Color newColor; // label indicating current mode private Label 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 Button("Draw"); setMove = new Button("Move"); setColor = new Button("Color"); Panel buttonPanel = new Panel(); buttonPanel.add(setDraw); buttonPanel.add(setMove); buttonPanel.add(setColor); chooseColor = new Choice(); chooseColor.addItem("red"); chooseColor.addItem("blue"); chooseColor.addItem("green"); chooseColor.addItem("yellow"); setErase = new Button("Erase last"); Panel choicePanel = new Panel(); choicePanel.add(setErase); choicePanel.add(chooseColor); Panel controlPanel = new Panel(new GridLayout(3,1)); modeLabel = new Label("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 (draggingScribble) {// move current scribble 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.