CS49J Mini Project

You will submit your work for this mini project as homeworks 23, 24, 25, 26.

Your code (but not your name unless you choose to add it to the source files) will be visible to all classmates after each submission. You can study your classmates' code, and you can use any of it (with attribution) in the next phase of the project.

The purpose of the mini project is to develop a small scene editor that lets users add and rearrange shapes to a scene, and save their work.

Deliverable for each phase: A zip file with classes SceneViewer, SceneComponent, Shape, the shape classes, a test input file, and any images that you are using. There is no CodeCheck. (CodeCheck doesn't work for GUI programs.)

Phase 1 (Homework 23)

There are three kinds of shape:

Provide a superclass or interface Shape with a method

void draw(Graphics2D)

Make a SceneComponent that holds an ArrayList<Shape>. Its paintComponent method calls draw on all shapes.

Your main method should read a file with shapes and draw all of them. The file has a specific format. Each line starts with the shape class and is followed by the construction parameters, separated by commas. Example:

RectShape,255,0,0,false,10,20,100,50
TextShape,Serif,Hello World,30,200,200
ImageShape,dog.gif,0,200

Tip: The challenge in drawing a TextShape is that you are given the top left corner, but drawString uses the base point. You need to adjust by the ascent (see section 10.7).

Phase 2 (Homework 24)

Make it so that one can select, move, copy, and delete shapes.

Add methods

void translate(int dx, int dy)
Rectangle getBounds()

to the Shape type and its subclasses.

When the user clicks on the scene component, find the first shape whose bounds contain the mouse cursor, and draw “grabber squares” in the four corner points. When the user drags the mouse, translate the shape by the difference in mouse movement.

Add a button “Delete” that deletes the currently selected shape.

Add a button “Copy” that clones the currently selected shape and adds it to the drawing, translating it slightly. Make that the selected shape so that the user can easily drag it to the desired location.

Put those buttons in some convenient place.

Tip: Now the “model” of the scene component is an ArrayList<Shape> + the currently selected shape. Modify paintComponent to draw the grabbers when a shape is selectd. Call repaint on the SceneComponent whenever you change something, just like in listing 11.5.

Tip: The mouse dragging is a bit fussy. You need to remember the old mouse coordinates when the mouse is clicked. When it is dragged, compute the difference between the old and new location, use that to translate the shape, and remember the new mouse location. The simplistic strategy in Listing 11.5 doesn't look as good because it makes the shape jump to the center.

Tip: Computing the bounding rectangle of a TextShape requires you to compute the string bounds (see Section 10.7).

Phase 3 (Homework 25)

We want to be able to add new shapes. Make a text field and an Add button. The user types the same line as one would find in the file in the text area. Also make a Save button that saves the diagram. Simply add toString methods to each Shape subclass, using the same string format as for reading.

Next, we want more shapes. Instead of having all sorts of classes LineShape, EllipseShape, and so on, we'll use the java.awt.Shape interface that is implemented by lots of shape classes. Note that Graphics2D.draw and Graphics2D.fill have parameters of type java.awt.Shape.

Define a GeneralShape subclass of our own Shape type that has an instance variable of type java.awt.Shape. The GeneralShape.draw method calls Graphics2D.draw or Graphics2D.fill with that shape. The getBounds method calls getBounds on that shape.

You have to work a bit harder on translate since Shape has no translate method. Instead, make it so that a GeneralShape has an x- and y-coordinate that is updated by translate. In the draw method, call translate on the Graphics2D object, then call draw or fill, and then call translate again in the opposite direction.

Finally, one needs to construct those shapes. We'll assume that the shape has a constructor that can take a sequence of integer and floating-point values. (They all do.) The text description contains the class name, in the format accepted by Class.forName, and the construction parameters. This follows the color, the fill mode, and the translation offsets. For example:

GeneralShape,255,0,0,false,0,0,java.awt.geom.RoundRectangle2D$Double,10,20,100,50,5,5

Tip: Use Class.forName to load the given class and call newInstance with the remaining parameters. That's a bit tricky because you need to know whether to pass Integer, Float, or Double values. Look through all constructors and find one that has the right number of arguments, all of which should be of type int.class or double.class. Then convert each string into an Integer or Double.

Tip: You need to be able to write the shape out again. It could have been translated. Save the color, fill mode, and translation offsets, followed by the description of the shape when it was constructed (which you should store).

Phase 4 (Homework 26)

Clean up the user interface. Instead of that silly text field, provide a combo box for the shape types next to the Add button and then pop up a dialog with the fields needed for the shape type. A color picker would be nice. Also add a menu with options File → Open and File → Save that pop up a JFileChooser, and File → Exit that closes the program. If the user exits without having saved, ask if that's ok.