import javax.swing.*; import java.awt.*; import java.util.List; import java.util.*; import java.awt.geom.Line2D; /** * Canvas is a class to allow for simple graphical drawing on a canvas. * This is a modification of the general purpose Canvas, specially made for * the BlueJ "shapes" example. It has been modified slightly for use in * the Bouncing Boulders program. * * @author: Bruce Quig * @author: Michael Kölling (mik) * * @version 2016.02.29 */ public class Canvas { // Note: The implementation of this class (specifically the handling of // shape identity and colors) is slightly more complex than necessary. This // is done on purpose to keep the interface and instance fields of the // shape objects in this project clean and simple for educational purposes. private static Canvas canvasSingleton; /** * Factory method to get the canvas singleton object. */ public static Canvas getCanvas() { if(canvasSingleton == null) { canvasSingleton = new Canvas("Boulders", 800, 600, Color.white); } canvasSingleton.setVisible(true); return canvasSingleton; } // ----- instance part ----- private JFrame frame; private CanvasPane canvas; private Graphics2D graphic; private Color backgroundColor; private Image canvasImage; private List objects; private HashMap shapes; /** * Create a Canvas. * @param title title to appear in Canvas Frame * @param width the desired width for the canvas * @param height the desired height for the canvas * @param bgColor the desired background color of the canvas */ private Canvas(String title, int width, int height, Color bgColor) { frame = new JFrame(); canvas = new CanvasPane(); frame.setContentPane(canvas); frame.setTitle(title); frame.setLocation(30, 30); canvas.setPreferredSize(new Dimension(width, height)); backgroundColor = bgColor; frame.pack(); objects = new ArrayList(); shapes = new HashMap(); } public Dimension getSize() { return canvas.getSize(); } public static int getWidth() { return getCanvas().getSize().width; } public static int getHeight() { return getCanvas().getSize().height; } /** * Set the canvas visibility and brings canvas to the front of screen * when made visible. This method can also be used to bring an already * visible canvas to the front of other windows. * @param visible boolean value representing the desired visibility of * the canvas (true or false) */ public void setVisible(boolean visible) { if(graphic == null) { // first time: instantiate the offscreen image and fill it with // the background color Dimension size = canvas.getSize(); canvasImage = canvas.createImage(size.width, size.height); graphic = (Graphics2D)canvasImage.getGraphics(); graphic.setColor(backgroundColor); graphic.fillRect(0, 0, size.width, size.height); graphic.setColor(Color.black); graphic.setStroke(new BasicStroke(3.0f)); } frame.setVisible(visible); } /** * Draw a given shape onto the canvas. * @param referenceObject an object to define identity for this shape * @param color the color of the shape * @param shape the shape object to be drawn on the canvas */ // Note: this is a slightly backwards way of maintaining the shape // objects. It is carefully designed to keep the visible shape interfaces // in this project clean and simple for educational purposes. public void draw(Object referenceObject, String color, Shape shape) { objects.remove(referenceObject); // just in case it was already there objects.add(referenceObject); // add at the end shapes.put(referenceObject, new ShapeDescription(shape, color)); redraw(); wait(80); } /** * Erase a given shape's from the screen. * @param referenceObject the shape object to be erased */ public void erase(Object referenceObject) { objects.remove(referenceObject); // just in case it was already there shapes.remove(referenceObject); redraw(); } /** * Set the foreground color of the Canvas. * @param newColor the new color for the foreground of the Canvas */ public void setForegroundColor(String colorString) { if(colorString.equals("red")) { graphic.setColor(new Color(235, 25, 25)); } else if(colorString.equals("black")) { graphic.setColor(Color.black); } else if(colorString.equals("blue")) { graphic.setColor(new Color(30, 75, 220)); } else if(colorString.equals("yellow")) { graphic.setColor(new Color(255, 230, 0)); } else if(colorString.equals("green")) { graphic.setColor(new Color(80, 160, 60)); } else if(colorString.equals("magenta")) { graphic.setColor(Color.magenta); } else if(colorString.equals("white")) { graphic.setColor(Color.white); } else { graphic.setColor(Color.black); } } /** * Wait for a specified number of milliseconds before finishing. * This provides an easy way to specify a small delay which can be * used when producing animations. * @param milliseconds the number */ public void wait(int milliseconds) { try { Thread.sleep(milliseconds); } catch (Exception e) { // ignoring exception at the moment } } /** * Redraw all shapes currently on the Canvas. */ public void redraw() { erase(); try { for(Object shape : objects) { shapes.get(shape).draw(graphic); } } catch (ConcurrentModificationException e) { // Could end up here if we're adding items while redrawing. // Ignore the error and continue. } canvas.repaint(); } /** * Erase the whole canvas. (Does not repaint.) */ private void erase() { Color original = graphic.getColor(); graphic.setColor(backgroundColor); Dimension size = canvas.getSize(); graphic.fill(new Rectangle(0, 0, size.width, size.height)); graphic.setColor(original); } /************************************************************************ * Inner class CanvasPane - the actual canvas component contained in the * Canvas frame. This is essentially a JPanel with added capability to * refresh the image drawn on it. */ private class CanvasPane extends JPanel { public void paint(Graphics g) { g.drawImage(canvasImage, 0, 0, null); } } /************************************************************************ * Inner class CanvasPane - the actual canvas component contained in the * Canvas frame. This is essentially a JPanel with added capability to * refresh the image drawn on it. */ private class ShapeDescription { private Shape shape; private String colorString; public ShapeDescription(Shape shape, String color) { this.shape = shape; colorString = color; } public void draw(Graphics2D graphic) { setForegroundColor(colorString); if (shape instanceof Line2D.Double) { graphic.draw(shape); } else { graphic.fill(shape); } } } }