Homework 4 - 20 points

The Fibonacci spiral and H Fractal
(Expanding the Model-View with a Controller)


Now it's time to make an interactive program, where the user can do something with your software.  Your goal is to produce a program that displays the following shapes, the fibonacci spiral and the fractal H, shown below at different levels:

The level 1 pictures are the initial state of our program,  the first fibonacci square (with its circle arc in the first quadrant) and an H shape displayed.

The level 2 fibonacci spiral is created by adding the 2nd fibonacci square (with a circle arc in the 2nd quadrant) to the left of the 1st square, the level 3 by adding the 3rd fibonacci square (with a circle arc in the 3rd quadrant) below the first two squares. In general to create level n+1 from level n, just add the next n+1 fibonacci square. If the fibonacci square n has its circle arc in quadrant q, the fibonacci square n+1 has its circle arc in quadrant q + 1 (of course if q = 4, the next quadrant should be 1). Another issue is to add the next square at the correct location. Checking the quadrant of the last added square should tell you the location of the next square to add. For instance, square 1 has its circle arc in quadrant 1. The next quadrant is 2, which tells you that square 2 should go to the left of square 1.

To create the level 2 picture for the H, tile the H with 7 squares a third of the size of the original H and replace each square with an H as shown below. 

To create level 3,  just repeat the process with each one of the 7 H's drawn in level 2. Thus, level 3 has 49 H's.

When the program starts, the shapes are displayed in their initial state (level 1.)  Display at least one fibonacci square and one H. Make sure that they don't overlap. Apart from these requirements, you can pick whatever size, color, location you want (Of course, it is better to pick a size so that it is easy to see the subsequent levels.)   For each mouse click on a shape, the level  will increment or decrement by 1 and the display is updated.  There should be a reset button as well:  when the user clicks on the reset button,  the display goes back to the original state.  Finally, the current level number should also be displayed and updated as the user works with the program (see the use of the TextViewer below).

There will be a limit on the number of levels the user can display.  The program will stop adding to the spiral if it goes off screen or if overlaps with a neighboring shape (that is find the maximum fibonacci number allowed by the size of your frame and stop adding to the spiral when you reach that number.) The program will stop dividing the H shape if the sizes of the new H's are too small (e.g. < 25 pixels). A dialog box should pop up telling the user that they have reached the maximum level.

A dialog box should also pop up if the user wants to decrease the level below 1.


Changes to the Shape interface, the AbstractShape, FibonacciSquare, and HShape classes
(though these changes are going to be used by the FibonacciSquare and HShape classes, some belong in the Shape interface or the AbstractShape class-- that inheritance thing again.)

To model the fibonacci spiral, we can think of every fibonacci square n as being the child of the square n-1 that precedes it. For instance, at level 3, square 1 has one child = square 2. Square 2 has one child = square 3. And square 3 doesn't have any child.

For the H shape, a division can be thought of creating a covering of 7 smaller H's for every H making up the shape. That is we can think of an HShape has having 7 children. Even though the construction of the fibonacci spiral and the H are different, we can use the same approach for both and write some of the code in their common AbstractShape base class.

In the AbstractShape class, add an array of Shape objects to store the Shape objects that are the child shapes. Since the array will be of size 1 for a fibonacci square and of size 7 for an H shape, it can't be initialized in the constructor of AbstractShape. Make the visibility of the array protected and initialize the array in the constructors of the derived classes (FibonacciSquare and HShape). The elements of the children array are null for an initial fractal. When level 2 is created, the array is filled with actual shapes. For level 3, it is the arrays of the shapes that are added at level 2 that are filled, etc.

In the AbstractShape class, write a method to add a level (e.g. addLevel()) to a shape. It will do so by initializing the elements of the array of children for the last current level. This method will be called by the model when instructed by the controller to add a new level. Since the model has only a reference to the top level shapes, the method will iterate to get to the last level of the shape that was selected by the mouse click. Do so by using recursion (don't use any loop, except to loop over the elements of the array of children in a current level.) The base case of the method will be attained when the array of children is empty. Fill the array of children by calling a method (e.g. createChildren()) declared in the interface Shape and implemented in FibonacciSquare and HShape. Dynamic binding will automatically select the correct implementation! Of course, the array should be filled only if the spiral doesn't get too big, or if the H's don't get too small. Return a boolean to the model to tell it if a new level could be added. The boolean value can be relayed to the controller to tell it if the operation was successful. If a new level could not be added, then the controller displays a message box to the user explaining why (e.g. "size limit has been reached.")

In the AbstractShape class, write a method to remove a level (e.g. removeLevel()) from a shape. It will do so by setting to null the elements of the array of children for the last current level. This method will be called by the model when instructed by the controller to remove a level. Contrary to the addLevel method, removeLevel won't iterate to the last level of the selected shape. It will iterate to the level preceding the last level. This is because to remove the last level, the method needs to set to null the elements of the array of children that refer to that last level. As for addLevel, iterate by using recursion (that is don't use any loop, except to loop over the elements of the array of children in a current level.) If the shape that is displayed is at level 1, a level can't be removed. In that case, return false to the model. In all other cases, return true to the model. The boolean value will be passed on to the controller so that it can display a message box if a level could not be removed.

In AbstractShape, if you haven't done so in homework 3, provide a toString method for a shape. It should give the type of the shape (FibonacciSquare or HShape available by calling getClass()), the coordinates of the shape, its color and its current level.

In FibonacciSquare and HShape, modify the draw method. Recursion will come very handy when rewriting this method. Note that all levels are drawn for the fibonacci spiral, and that only the last level is drawn for an HShape.

You don't need to provide any deep copy method for a shape. If you have programmed a deep copy method for homework 2, you can remove the method.

Add any other methods that you think are needed for your implementation.

Changes to the Model

The model needs only to keep track of the shapes initially created since the shapes that are added when increasing the levels are stored within the initial shapes. Just add methods that allow the model to add or to remove a level to a shape.

There is also no need to return a deep copy of the list of shapes when sending a message to a view requiring the list of the shapes. In other words, you may just return the reference to the list of the shapes stored by the model in the method that returns the list of the shapes to a view.



Build your controller class(es)  to implement the user interface as described above.   The controller should handle the mouse clicks of the user and send appropriate messages to the model (namely increase or decrease the level of the shape at the location of the click). We will say that a mouse click is on a shape if it is within its half of the JFrame (assuming your application displays only a shape in each half of the frame.) Display two JRadioButtons (see the information about JRadioButton and ButtonGroup on the Oracle site) to let the user choose between a level increase and a level decrease. You should have at least one public class that has controller functionality (like the ButtonListener in some of the examples done in class.  Don't go overboard using anonymous inner classes.)  For the dialog box,  see the JOptionPane class.  It has some useful static methods.

A Text Viewer

Create a new class that implements DrawingView which is all text based. It should print in the terminal window the information about the shapes as given by the toString method of a shape. This view should be refreshed only if the model changes. For example, a mouse click outside of any shape should not print again the list of the shapes.

Class with the main method

Here is where you put all the classes together Model, View, and Controller. This is the class that can also create the two radio buttons and the reset button. Use a JButton for the reset button.



Written Report

  1. Planning and operation: What does your program do?  Describe the major features.  If you worked with a partner, how did you divide responsibilities?
  2. Implementation:  How is your program organized?  What are the major classes?  How do the classes interact? Draw a class diagram.
  3. Give a clear explanation of the recursive algorithms to add a level to and remove a level from a shape.
  4. Testing:  How did you test your code?  What sort of bugs did you encounter?  What works and what doesn't?  Are there any unresolved problems in the code?
  5. Evaluate this project: What did you learn from it?  Was it worth the effort?  This could include things you learned about specifications and interfaces, design issues, Java language or library issues, debugging, etc.

Turn in your report as a pdf file along with your java files on the turn-in page.


Checklist: a summary of the important points of this assignment

Though you may (and very likely will) have more features, your project should have all of the points listed below


Good Luck and start early!