aboutsummaryrefslogtreecommitdiff
path: root/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java
diff options
context:
space:
mode:
Diffstat (limited to 'Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java')
-rw-r--r--Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java167
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) {