Computer Science 252
Problem Solving with Java

Fall 2013, The College of Saint Rose

NestedSquaresSizes Demo

A working demo of NestedSquaresSizes will appear below. Click inside the applet to interact with it.



NestedSquaresSizes BlueJ Project

Click here to download a BlueJ project for NestedSquaresSizes.


NestedSquaresSizes Source Code

The Java source code for NestedSquaresSizes is below. Click on a file name to download it.


NestedSquaresSizes.java

import objectdraw.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.JPanel;

/*
 * Example NestedSquaresSizes: draw nested squares objects 
 * of various sizes, drag them around, and
 *
 * Jim Teresco, The College of Saint Rose, CSC 252, Fall 2013
 *
 * $Id: NestedSquaresSizes.java 2230 2013-10-27 02:26:10Z terescoj $
 */

public class NestedSquaresSizes extends WindowController
    implements ActionListener  {
   
    // minimum and maximum sizes for our objects
    private static final int MIN_SIZE = 25;
    private static final int MAX_SIZE = 200;
    
    // standard dragging stuff
    private NestedSquares newestSquares;
    private boolean dragging;
    private Location lastMouse;
   
    // a slider to determine the size of the next box to 
    // create
    private JSlider nextBoxSize;
    
    // a "set color" and "get info" button
    // used to modify or report on the newest
    // NestedSquares object in existence
    private JButton setColor, getInfo;
    
    // a label where the "info" will be reported
    private JLabel infoLabel;
    
    // we'll need some random numbers too
    private Random r = new Random();
    
    public void begin() {
        
        setSize(500, 600);
        
        // get the content pane to which we can add items
        Container contentPane = getContentPane();
        
        // we'll put our buttons and slider into a panel
        // at the bottom of the window
        JPanel southPanel = new JPanel();
        
        // our buttons
        setColor = new JButton("Random Color");
        southPanel.add(setColor);
        setColor.addActionListener(this);
        
        getInfo = new JButton("Get Info");
        southPanel.add(getInfo);
        getInfo.addActionListener(this);
        
        // a slider to control sizes
        nextBoxSize = new JSlider(JSlider.HORIZONTAL, MIN_SIZE, MAX_SIZE,
            (MIN_SIZE + MAX_SIZE)/2);
        // with some labels for end amounts?
        southPanel.add(new JLabel(""+MIN_SIZE));
        southPanel.add(nextBoxSize);
        southPanel.add(new JLabel(""+MAX_SIZE));
        
        // add our panel in the south
        contentPane.add(southPanel, BorderLayout.SOUTH);
        
        // create a Label for information
        infoLabel = new JLabel("Info will appear here when you click \"Get Info\"");
        contentPane.add(infoLabel, BorderLayout.NORTH);
        
        contentPane.validate();

    }
    
    public void onMousePress(Location point) {

        if (newestSquares != null && newestSquares.contains(point)) {
            dragging = true;
            lastMouse = point;
        }
        else {
            newestSquares = new NestedSquares(point.getX(), point.getY(), nextBoxSize.getValue(), canvas);
        }
    }
    
    public void onMouseDrag(Location point) {
        
        if (dragging) {
            newestSquares.move(point.getX() - lastMouse.getX(),
                               point.getY() - lastMouse.getY());
            lastMouse = point;
        }
    }
    
    public void onMouseRelease(Location point) {
        
        dragging = false;
    }
    
    // event handler to react to our buttons
    public void actionPerformed(ActionEvent e) {
        
        if (e.getSource() == getInfo) {
            // it's the get info button
            if (newestSquares != null) {
                int numLevels = newestSquares.squareCount();
                double totalPerimeter = newestSquares.totalPerimeter();
                infoLabel.setText("Newest has " + numLevels + " squares, total perimeter: " + totalPerimeter);
            }
            else {
                infoLabel.setText("Please create a nested squares first!");
            }
        }
        else {
            // must be the setColor button
            if (newestSquares != null) {
                Color c = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
                newestSquares.setColor(c);
            }
        }
    }
}

NestedSquares.java

import objectdraw.*;
import java.awt.*;

/*
 * Example NestedSquares: a recursive implementation of an object
 * consisting of nested squares, complete with contains and move
 * methods to support dragging.
 *
 * Jim Teresco, The College of Saint Rose, CSC 252, Fall 2013
 *
 * $Id: NestedSquares.java 2230 2013-10-27 02:26:10Z terescoj $
 */

public class NestedSquares {

    private static final int SIZE_CHANGE = 4;
    
    // instance variables to keep track of the outermost
    // square and then NestedSquares inside
    private FramedRect outline;
    private NestedSquares rest;
    
    // a recursive implementation: we draw nested squares
    // by creating the outermost square, then calling
    // THIS CONSTRUCTOR to draw the rest, which are really
    // just another set of nested squares, just a little bit
    // smaller and a little bit down and to the right.
    public NestedSquares(double x, double y, int size, DrawingCanvas c) {
        
        // draw one        
        outline = new FramedRect(x, y, size, size, c);
        
        // draw more only if there are more to draw, otherwise
        // we remember rest as null (so we know the recursion
        // is over) -- if size is not greater than SIZE_CHANGE,
        // the next level would not exist, so we don't try to
        // draw it
        if (size > SIZE_CHANGE) {
            // draw the rest
            rest = new NestedSquares(x + SIZE_CHANGE/2, y+SIZE_CHANGE/2,
                size - SIZE_CHANGE, c);
        }
        else {
            // this will flag the end of the recursion
            rest = null;
        }
    }
    
    // contains is easy - we just check containment of the outline
    public boolean contains(Location point) {
        
        return outline.contains(point);
    }
    
    // move method will need to be recursive: we move the whole thing
    // by moving the outline, then moving the rest (using this method!)
    public void move(double dx, double dy) {
        
        outline.move(dx, dy);
        if (rest != null) {
            rest.move(dx, dy);
        }
    }
    
    // we will also be able to set the color
    public void setColor(Color c) {
        
        // we achieve this recursively
        outline.setColor(c);
        if (rest != null) {
            rest.setColor(c);
        }
    }
    
    // a recursive accessor method to count the number of nested squares
    public int squareCount() {
        
        // if this is the innermost, we have just the 1
        if (rest == null) return 1;
        // otherwise we have the one (the outline) plus whatever
        // number is in the rest
        return 1 + rest.squareCount();
    }
    
    // also compute the total perimeter of all of the nested squares
    public double totalPerimeter() {
        
        // first, compute the perimeter of the outermost square
        double outlinePerim = 4 * outline.getWidth();
        
        // again, if this is the innermost, we have just this one to consider
        if (rest == null) return outlinePerim;
        
        // otherwise, we have this one plus the smaller ones inside of rest
        return outlinePerim + rest.totalPerimeter();
    }
}