diff options
| author | Camil Staps | 2015-06-05 15:57:28 +0200 | 
|---|---|---|
| committer | Camil Staps | 2015-06-05 15:57:28 +0200 | 
| commit | 614c03cc1b8eb508d6ed3c698dfa3bc3a6936ca9 (patch) | |
| tree | 81e34734c883868426f90a8f110d51b2c9b7bee6 /Week15 Mandelbrot/src/com/camilstaps | |
| parent | Option for multiple (4) or single swingworker(s) (diff) | |
Cleanup; javadoc
Diffstat (limited to 'Week15 Mandelbrot/src/com/camilstaps')
6 files changed, 261 insertions, 99 deletions
| diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/FractalModel.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/FractalModel.java index dd09100..4c2499d 100644 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/FractalModel.java +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/FractalModel.java @@ -26,28 +26,34 @@ package com.camilstaps.mandelbrot;  import java.util.Observable;  /** - * - * @author camilstaps + * The FractalModel holds the bounds of the shown fractal + * @author Camil Staps   */  public class FractalModel extends Observable { -    private double start_x, start_y, end_x, end_y; +    private double start_x = -1, end_x = 1, start_y = -1, end_y = 1; -    public FractalModel() { -        start_x = -1; -        end_x = 1; -        start_y = -1; -        end_y = 1; +    /** +     * Get the Mandelbrot number for a specific point up to some maximum +     * @param x the x coordinate of the point +     * @param y the y coordinate of the point +     * @param repetitions the maximum +     * @return the mandelbrot number +     */ +    public int getMandelNumber(double x, double y, int repetitions) { +        return MandelbrotFractal.mandelNumber(x, y, repetitions);      } +    /** +     * Get the Mandelbrot number for a specific point up to some maximum +     * @param p the point +     * @param repetitions the maximum +     * @return the mandelbrot number +     */      public int getMandelNumber(MandelbrotFractal.Point p, int repetitions) {          return MandelbrotFractal.mandelNumber(p, repetitions);      } -    public int getMandelNumber(double x, double y, int repetitions) { -        return MandelbrotFractal.mandelNumber(x, y, repetitions); -    } -          public double getStartX() {          return start_x;      } @@ -64,7 +70,14 @@ public class FractalModel extends Observable {          return end_y;      } -    public synchronized void setBorders(double start_x, double end_x, double start_y, double end_y) { +    /** +     * Set all bounds together +     * @param start_x +     * @param end_x +     * @param start_y +     * @param end_y  +     */ +    public synchronized void setBounds(double start_x, double end_x, double start_y, double end_y) {          if (start_x == this.start_x && end_x == this.end_x && start_y == this.start_y && end_y == this.end_y)              return; 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) { diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotFractal.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotFractal.java index 43909fe..ec05939 100644 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotFractal.java +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotFractal.java @@ -39,11 +39,24 @@ public class MandelbrotFractal {       */      private static final Map<Point,Result> mandelNumbers = new HashMap<>(); +    /** +     * Calculate the Mandelbrot number for a specific point up to some maximum +     * @param x the x coordinate of the point +     * @param y the y coordinate of the point +     * @param repetitions the maximum +     * @return the mandelbrot number +     */      public synchronized static int mandelNumber(double x, double y, int repetitions) {          Point p = new Point(x, y);          return mandelNumber(p, repetitions);      } +    /** +     * Calculate the Mandelbrot number for a specific point up to some maximum +     * @param p the point +     * @param repetitions the maximum +     * @return the mandelbrot number +     */      public synchronized static int mandelNumber(Point p, int repetitions) {          if (mandelNumbers.containsKey(p)) {              Result result = mandelNumbers.get(p); @@ -61,6 +74,12 @@ public class MandelbrotFractal {          }      } +    /** +     * Calculate the Mandelbrot number for a specifc point up to some maximum +     * @param p the point +     * @param repetitions the maximum +     * @return the result +     */      protected synchronized static Result calculateMandelNumber(Point p, int repetitions) {          Result start = new Result();          start.x = p.x; @@ -69,6 +88,13 @@ public class MandelbrotFractal {          return start;      } +    /** +     * Calculate the Mandelbrot number for a specific point up to some maximum, +     * starting from an earlier result +     * @param p the point +     * @param repetitions the maximum +     * @param start the earlier result; will be updated with the new result +     */      protected synchronized static void calculateMandelNumber(Point p, int repetitions, Result start) {          int n = start.repetitions; @@ -82,6 +108,9 @@ public class MandelbrotFractal {          start.mandelNumber = n;      } +    /** +     * A point on a coordinate system +     */      public static class Point {          double x, y; @@ -113,6 +142,10 @@ public class MandelbrotFractal {          }      } +    /** +     * A Mandelbrot result consists of a point, the mandelbrot number and the  +     * maximum with which we calculated that result. +     */      protected static class Result {          int mandelNumber = -1, repetitions = 0;          double x, y; diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotWindow.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotWindow.java index 81a4204..a7e6832 100644 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotWindow.java +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotWindow.java @@ -33,6 +33,9 @@ import javax.swing.JFrame;   */  public class MandelbrotWindow extends JFrame { +    /** +     * Create a window with a grid, progress bar and textfields +     */      private MandelbrotWindow() {          super("Mandelbrot"); diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Textfields.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Textfields.java index d719c4a..5cd20f3 100644 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Textfields.java +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Textfields.java @@ -36,18 +36,32 @@ import javax.swing.JPanel;  import javax.swing.JTextField;  /** - * - * @author camilstaps + * The Textfields form a view and a controller for the FractalModel class, and + * a controller for the Grid class + * @author Camil Staps   */  public class Textfields extends JPanel implements Observer { +    /** +     * The FractalModel to view and control +     */      private final FractalModel fractalModel; +     +    /** +     * The Grid to control +     */      private final Grid grid; +    /** +     * Initial values of the text fields +     */      private final String INITIAL_CENTER_X = "-0.46",              INITIAL_CENTER_Y = "0",              INITIAL_SCALE = "0.5"; +    /** +     * The components +     */      private final JTextField field_centerX = new JTextField(INITIAL_CENTER_X, 6);      private final JTextField field_centerY = new JTextField(INITIAL_CENTER_Y, 6);      private final JTextField field_scale = new JTextField(INITIAL_SCALE, 6); @@ -55,6 +69,11 @@ public class Textfields extends JPanel implements Observer {      private final JButton button_reset = new JButton("Reset");      private final JCheckBox checkbox_multiple_swingworkers = new JCheckBox("Multiple SwingWorkers"); +    /** +     * Create a new instance +     * @param fractalModel +     * @param grid  +     */      public Textfields(FractalModel fractalModel, Grid grid) {          super(new BorderLayout()); @@ -68,6 +87,9 @@ public class Textfields extends JPanel implements Observer {          button_redraw.doClick();      } +    /** +     * Setup the swing components with their listeners +     */      private void setupControls() {          JPanel panel = new JPanel(new GridLayout(9,1)); @@ -81,16 +103,24 @@ public class Textfields extends JPanel implements Observer {          panel.add(field_scale);          button_redraw.addActionListener(new ActionListener() { +            /** +             * Update the FractalModel's bounds +             * @param ae  +             */              @Override              public void actionPerformed(ActionEvent ae) {                  if (ae.getActionCommand().equals("Redraw")) { -                    fractalModel.setBorders(getStartX(), getEndX(), getStartY(), getEndY()); +                    fractalModel.setBounds(getStartX(), getEndX(), getStartY(), getEndY());                  }              }          });          panel.add(button_redraw);          button_reset.addActionListener(new ActionListener() { +            /** +             * Reset the textfields to their default values +             * @param ae  +             */              @Override              public void actionPerformed(ActionEvent ae) {                  field_centerX.setText(INITIAL_CENTER_X); @@ -103,6 +133,10 @@ public class Textfields extends JPanel implements Observer {          panel.add(button_reset);          checkbox_multiple_swingworkers.addActionListener(new ActionListener() { +            /** +             * Tell the Grid to use single or multiple SwingWorkers +             * @param ae  +             */              @Override              public void actionPerformed(ActionEvent ae) {                  grid.setUseMultipleSwingWorkers(checkbox_multiple_swingworkers.isSelected()); @@ -119,30 +153,51 @@ public class Textfields extends JPanel implements Observer {          return 200;      } +    /** +     * Get the desired low bound on x of the model +     * @return  +     */      protected double getStartX() {          double width = 1 / Double.parseDouble(field_scale.getText());          double center = Double.parseDouble(field_centerX.getText());          return center - width / 2;      } +    /** +     * Get the desired high bound on x of the model +     * @return  +     */      protected double getEndX() {          double width = 1 / Double.parseDouble(field_scale.getText());          double center = Double.parseDouble(field_centerX.getText());          return center + width / 2;      } +    /** +     * Get the desired low bound on y of the model +     * @return  +     */      protected double getStartY() {          double height = 1 / Double.parseDouble(field_scale.getText());          double center = Double.parseDouble(field_centerY.getText());          return center - height / 2;      } +    /** +     * Get the desired high bound on y of the model +     * @return  +     */      protected double getEndY() {          double height = 1 / Double.parseDouble(field_scale.getText());          double center = Double.parseDouble(field_centerY.getText());          return center + height / 2;      } +    /** +     * Update the text fields based on the FractalModel's bounds +     * @param o +     * @param o1  +     */      @Override      public final void update(Observable o, Object o1) {          field_centerX.setText(Float.toString((float)  diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/ZoomFrame.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/ZoomFrame.java deleted file mode 100644 index 071bb8c..0000000 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/ZoomFrame.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * The MIT License (MIT) - *  - * Copyright (c) 2015 Camil Staps <info@camilstaps.nl> - *  - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - *  - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - *  - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package com.camilstaps.mandelbrot; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.Graphics; -import javax.swing.JFrame; - -/** - * A frame in which zooming is possible - * @author Camil Staps, s4498062 - */ -public class ZoomFrame extends JFrame { -     -    private Graphics graphics; -     -    private final Grid drawView; -     -    public ZoomFrame(String s, Grid drawView) { -        super(s); -         -        this.drawView = drawView; -         -        setLayout(new BorderLayout()); -        getContentPane().setPreferredSize(new Dimension(drawView.getWidth(), drawView.getHeight())); -        pack(); -        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); -        setResizable(false); -        setLocationRelativeTo(null); -        setVisible(true); -         -        add(drawView, BorderLayout.CENTER); -    } -     -} | 
