diff options
Diffstat (limited to 'Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java')
-rw-r--r-- | Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java | 167 |
1 files changed, 141 insertions, 26 deletions
diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java index 9fca89a..71c644a 100644 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java @@ -44,41 +44,83 @@ import javax.swing.JProgressBar; import javax.swing.SwingWorker; /** - * - * @author camilstaps + * The Grid is both a View and a Controller: it shows a graphical representation + * of the FractalModel, and allows the user to control that FractalModel by + * clicking (zoom in), shift-clicking (zoom out) and dragging (box zoom). + * @author Camil Staps */ public class Grid extends JPanel implements Observer, MouseListener, MouseMotionListener { + /** + * Hard maximum amount of repetitions and the amount of steps to take to get + * to that maximum + */ + private static final int REPETITIONS_MAX = 1000, STEPS = 50; + + /** + * Width and height of the grid + */ + private static final int WIDTH = 400, HEIGHT = 400; + + /** + * List of colours for visualisation + */ + private final int[] colors; + + /** + * The FractalModel to visualise + */ private final FractalModel fractalModel; + /** + * Used for visualisation + */ private final BufferedImage image; private final WritableRaster raster; - private final int width = 400, height = 400; + /** + * Count pixels changed to check if we need to repaint + */ private int pixelCounter; - private static final int REPETITIONS_MAX = 1000, STEPS = 50; - - private final int[] colors; - + /** + * Updating the view is done by SwingWorkers. The solution provides support + * for multiple SwingWorkers; Updaters; which are held in this list. + */ private final List<Updater> updaters = new ArrayList<>(); + /** + * Used for handling mouse events and zooming + */ private int start_x, start_y, old_x, old_y; private boolean dragging = false; Graphics graphics; + /** + * A progress bar + */ private ProgressView progressView; + /** + * Whether or not to use multiple SwingWorkers. + * Using multiple SwingWorkers is *not* faster. The application is already + * faster by using memory in the MandelbrotFractal class. Using multiple + * SwingWorkers does not aid in speed. + */ private boolean useMultipleSwingWorkers = false; + /** + * Create a new Grid to view and control a FractalModel + * @param fractalModel + */ public Grid(FractalModel fractalModel) { this.fractalModel = fractalModel; fractalModel.addObserver(this); - image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); raster = image.getRaster(); - setPreferredSize(new Dimension(width, height)); + setPreferredSize(new Dimension(WIDTH, HEIGHT)); colors = new int[REPETITIONS_MAX + 1]; for (int i = 0; i <=REPETITIONS_MAX; i++) { @@ -94,6 +136,10 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion update(fractalModel, null); } + /** + * Get the progress bar (create it if it doesn't exist yet) + * @return + */ public ProgressView getProgressView() { if (progressView == null) { progressView = new ProgressView(); @@ -103,18 +149,28 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion @Override public int getWidth() { - return width; + return WIDTH; } @Override public int getHeight() { - return height; + return HEIGHT; } + /** + * Change whether we use multiple SwingWorkers or not. This change will + * take effect when the FractalModel is updated. + * @param value + */ public void setUseMultipleSwingWorkers(boolean value) { useMultipleSwingWorkers = value; } + /** + * Stop the previous update and start rendering all over again. + * @param o + * @param o1 + */ @Override public final synchronized void update(Observable o, Object o1) { if (updaters != null) { @@ -128,20 +184,20 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion synchronized (updaters) { updaters.clear(); if (useMultipleSwingWorkers) { - Updater u1 = new Updater(0, width / 2, 0, height / 2); + Updater u1 = new Updater(0, WIDTH / 2, 0, HEIGHT / 2); u1.execute(); updaters.add(u1); - Updater u2 = new Updater(width / 2, width, 0, height / 2); + Updater u2 = new Updater(WIDTH / 2, WIDTH, 0, HEIGHT / 2); u2.execute(); updaters.add(u2); - Updater u3 = new Updater(0, width / 2, height / 2, height); + Updater u3 = new Updater(0, WIDTH / 2, HEIGHT / 2, HEIGHT); u3.execute(); updaters.add(u3); - Updater u4 = new Updater(width / 2, width, height / 2, height); + Updater u4 = new Updater(WIDTH / 2, WIDTH, HEIGHT / 2, HEIGHT); u4.execute(); updaters.add(u4); } else { - Updater u = new Updater(0, width, 0, height); + Updater u = new Updater(0, WIDTH, 0, HEIGHT); u.execute(); updaters.add(u); } @@ -154,23 +210,45 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion g.drawImage(image, 0, 0, null); } + /** + * Set a pixel on the grid + * @param x + * @param y + * @param rgb + */ public synchronized void setPixel(int x, int y, int[] rgb) { raster.setPixel(x, y, rgb); pixelCounter++; - if (pixelCounter == width * height) { + if (pixelCounter == WIDTH * HEIGHT) { pixelCounter = 0; repaint(); } } + /** + * Get the 'mathematical' x coordinate that belongs to a pixel's x coordinate + * @param pixel_x + * @return + */ protected double getRealX(int pixel_x) { - return ((double) pixel_x * (fractalModel.getEndX() - fractalModel.getStartX()) / (double) width) + fractalModel.getStartX(); + return ((double) pixel_x * (fractalModel.getEndX() - fractalModel.getStartX()) + / (double) WIDTH) + fractalModel.getStartX(); } + /** + * Get the 'mathematical' y coordinate that belongs to a pixel's y coordinate + * @param pixel_y + * @return + */ protected double getRealY(int pixel_y) { - return ((double) pixel_y * (fractalModel.getEndY() - fractalModel.getStartY()) / (double) height) + fractalModel.getStartY(); + return ((double) pixel_y * (fractalModel.getEndY() - fractalModel.getStartY()) + / (double) HEIGHT) + fractalModel.getStartY(); } + /** + * Click to zoom in; Shift-click to zoom out + * @param me + */ @Override public void mouseClicked(MouseEvent me) { if ((me.getModifiers() & InputEvent.SHIFT_MASK) != 0) { @@ -180,12 +258,20 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion } } + /** + * Keep track of mouse data for the box zoom + * @param me + */ @Override public void mousePressed(MouseEvent me) { old_x = start_x = me.getX(); old_y = start_y = me.getY(); } + /** + * If this was a box zoom, ... well, perform a box zoom. + * @param me + */ @Override public synchronized void mouseReleased(MouseEvent me) { if (me.getX() != start_x || me.getY() != start_y) { @@ -193,7 +279,6 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion } dragging = false; - notifyAll(); } /** @@ -253,6 +338,10 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion @Override public void mouseExited(MouseEvent me) {} + /** + * Draw the zoombox on dragging + * @param me + */ @Override public void mouseDragged(MouseEvent me) { Graphics g = getSafeGraphics(); @@ -290,11 +379,21 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion @Override public void mouseMoved(MouseEvent me) {} + /** + * A SwingWorker to update the graphics + */ protected class Updater extends SwingWorker<Map<Point,Integer>, Map<Point,Integer>> { private boolean doneProcessing = true, stop = false; private final int start_x, end_x, start_y, end_y; + /** + * Update only the given rectangle + * @param start_x + * @param end_x + * @param start_y + * @param end_y + */ public Updater(int start_x, int end_x, int start_y, int end_y) { super(); @@ -304,8 +403,12 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion this.end_y = end_y; } + /** + * Calculate all points in the rectangle using the FractalModel + * @return the resulting points + */ @Override - protected Map<Point, Integer> doInBackground() throws Exception { + protected Map<Point, Integer> doInBackground() { Map<Point,Integer> results = new HashMap<>(); for (int repetitions = REPETITIONS_MAX / STEPS; repetitions <= REPETITIONS_MAX && !stop; repetitions += REPETITIONS_MAX / STEPS) { @@ -327,6 +430,10 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion return results; } + /** + * Process a list of points, and update the progress bar + * @param results + */ @Override protected void process(List<Map<Point,Integer>> results) { if (progressView != null) { @@ -347,16 +454,17 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion doneProcessing = true; } - @Override - public void done() { - //updaters.remove(this); - } - + /** + * Tell the updater to stop + */ public void stop() { stop = true; } } + /** + * A point on the grid + */ protected class Point { int x, y; @@ -380,6 +488,10 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion } } + /** + * A special ProgressBar which is hidden when full, and takes the average + * of the progresses of all Updaters as its value + */ protected class ProgressView extends JProgressBar { public ProgressView() { @@ -394,6 +506,9 @@ public class Grid extends JPanel implements Observer, MouseListener, MouseMotion super.setValue(n); } + /** + * Calculate the value as the average of the progresses of all Updaters + */ public void setValue() { int sum = 0, count = 0; for (Updater updater : updaters) { |