diff options
8 files changed, 537 insertions, 227 deletions
diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/DrawView.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/DrawView.java deleted file mode 100644 index 4d73434..0000000 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/DrawView.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2015 Camil Staps - */ -package com.camilstaps.mandelbrot; - -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.image.BufferedImage; -import java.awt.image.WritableRaster; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Observable; -import java.util.Observer; -import javax.swing.JPanel; -import javax.swing.SwingWorker; - -/** - * - * @author camilstaps - */ -public class DrawView extends JPanel implements Observer { - - private final FractalModel fractalModel; - - private final BufferedImage image; - private final WritableRaster raster; - private final int width, height; - private static final int DEFAULT_WIDTH = 500, DEFAULT_HEIGHT = 500; - - private int pixelCounter; - private static final int COUNTER_MAX = 1000; - - private static final int REPETITIONS_MAX = 150; - - public DrawView(FractalModel fractalModel) { - this.fractalModel = fractalModel; - fractalModel.addObserver(this); - - width = DEFAULT_WIDTH; - height = DEFAULT_HEIGHT; - - image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); - raster = image.getRaster(); - - setPreferredSize(new Dimension(width, height)); - - update(fractalModel, null); - } - - @Override - public int getWidth() { - return width; - } - - @Override - public int getHeight() { - return height; - } - - @Override - public void update(Observable o, Object o1) { - Updater task = new Updater(); - task.execute(); - } - - @Override - public void paintComponent(Graphics g) { - super.paintComponent(g); - g.drawImage(image, 0, 0, null); - } - - public void setPixel(int x, int y, int[] rgb) { - raster.setPixel(x, y, rgb); - pixelCounter++; - if (pixelCounter > COUNTER_MAX) { - pixelCounter = 0; - repaint(); - } - } - - protected double getRealX(int pixel_x) { - return ((double) pixel_x * (fractalModel.getEndX() - fractalModel.getStartX()) / width) + fractalModel.getStartX(); - } - - protected double getRealY(int pixel_y) { - return ((double) pixel_y * (fractalModel.getEndY() - fractalModel.getStartY()) / height) + fractalModel.getStartY(); - } - - protected class Updater extends SwingWorker<Map<Point,Double>, Map<Point,Double>> { - private boolean doneProcessing = true; - - @Override - protected Map<Point, Double> doInBackground() throws Exception { - Map<Point,Double> results = new HashMap<>(); - - for (int repetitions = 1; repetitions < REPETITIONS_MAX; repetitions++) { - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - // Convert mandel number to some number in [0,pi] to let the colourise below work nicely - double mandel = ((double) fractalModel.getMandelNumber(getRealX(x), getRealY(y), repetitions) * Math.PI) / repetitions; - - //System.err.println("x,y : " + getRealX(x) + "," + getRealY(y) + "; " + mandel); - - Point p = new Point(x, y); - results.put(p, mandel); - } - } - - System.out.println("Rep " + repetitions); - - if (doneProcessing) { - doneProcessing = false; - publish(results); - } - setProgress(repetitions * 100 / REPETITIONS_MAX); - } - - return results; - } - - @Override - protected synchronized void process(List<Map<Point,Double>> results) { - for (Map<Point,Double> resultMap : results) { - for (Entry<Point,Double> result : resultMap.entrySet()) { - double value = result.getValue(); - - // Different mandel numbers have different colours -// int[] rgb = { -// (int) (30 + 220 * Math.sin(value)), -// (int) (30 + 220 * Math.sin(value + 2 * Math.PI / 3)), -// (int) (30 + 220 * Math.sin(value + 4 * Math.PI / 3))}; - // This is a grayscale version: - int[] rgb = {(int) (255 * value), (int) (255 * value), (int) (255 * value)}; - - setPixel(result.getKey().x, result.getKey().y, rgb); - } - } - doneProcessing = true; - } - } - - protected class Point { - int x, y; - - public Point(int x, int y) { - this.x = x; - this.y = y; - } - - @Override - public boolean equals(Object another) { - if (another == null || another.getClass() != Point.class) { - return false; - } - Point that = (Point) another; - return that.x == x && that.y == y; - } - - @Override - public int hashCode() { - return (x << 8) | y; - } - } - -} diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/FractalModel.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/FractalModel.java index 106c967..03a8908 100644 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/FractalModel.java +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/FractalModel.java @@ -44,23 +44,52 @@ public class FractalModel extends Observable { return end_y; } - public void setStartX(double x) { + public synchronized void setBorders(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; + + this.start_x = start_x; + this.end_x = end_x; + this.start_y = start_y; + this.end_y = end_y; + + setChanged(); + notifyObservers(); + } + + public synchronized void setStartX(double x) { + if (start_x == x) + return; + start_x = x; + setChanged(); notifyObservers(); } - public void setStartY(double y) { + public synchronized void setStartY(double y) { + if (start_y == y) + return; + start_y = y; + setChanged(); notifyObservers(); } - public void setEndX(double x) { + public synchronized void setEndX(double x) { + if (end_x == x) + return; + end_x = x; + setChanged(); notifyObservers(); } - public void setEndY(double y) { + public synchronized void setEndY(double y) { + if (end_y == y) + return; + end_y = y; + setChanged(); notifyObservers(); } diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java new file mode 100644 index 0000000..bf21b43 --- /dev/null +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Grid.java @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2015 Camil Staps + */ +package com.camilstaps.mandelbrot; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.image.BufferedImage; +import java.awt.image.WritableRaster; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Observable; +import java.util.Observer; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.SwingWorker; + +/** + * + * @author camilstaps + */ +public class Grid extends JPanel implements Observer, MouseListener, MouseMotionListener { + + private final FractalModel fractalModel; + + private final BufferedImage image; + private final WritableRaster raster; + private final int width, height; + private static final int DEFAULT_WIDTH = 400, DEFAULT_HEIGHT = 400; + + private int pixelCounter; + private static final int COUNTER_MAX = 1000; + + private static final int REPETITIONS_MAX = 1000, STEPS = 50; + + private final int[] colors; + + private Updater updater; + + private int start_x, start_y, old_x, old_y; + private boolean dragging = false; + Graphics graphics; + + private ProgressView progressView; + + public Grid(FractalModel fractalModel) { + this.fractalModel = fractalModel; + fractalModel.addObserver(this); + + width = DEFAULT_WIDTH; + height = DEFAULT_HEIGHT; + + image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + raster = image.getRaster(); + + setPreferredSize(new Dimension(width, height)); + + colors = new int[REPETITIONS_MAX + 1]; + for (int i = 0; i <=REPETITIONS_MAX; i++) { + colors[i] = Color.HSBtoRGB( + 0.07f, + 0.5f + 0.5f * (float) i / (float) REPETITIONS_MAX, + 1); + } + + addMouseListener(this); + addMouseMotionListener(this); + + update(fractalModel, null); + } + + public ProgressView getProgressView() { + if (progressView == null) { + progressView = new ProgressView(); + } + return progressView; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public synchronized void update(Observable o, Object o1) { + if (updater != null) { + updater.stop(); + updater.cancel(true); + } + updater = new Updater(); + updater.execute(); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + g.drawImage(image, 0, 0, null); + } + + public void setPixel(int x, int y, int[] rgb) { + raster.setPixel(x, y, rgb); + pixelCounter++; + if (pixelCounter > COUNTER_MAX) { + pixelCounter = 0; + repaint(); + } + } + + protected double getRealX(int pixel_x) { + return ((double) pixel_x * (fractalModel.getEndX() - fractalModel.getStartX()) / (double) width) + fractalModel.getStartX(); + } + + protected double getRealY(int pixel_y) { + return ((double) pixel_y * (fractalModel.getEndY() - fractalModel.getStartY()) / (double) height) + fractalModel.getStartY(); + } + + @Override + public void mouseClicked(MouseEvent me) { + if ((me.getModifiers() & InputEvent.SHIFT_MASK) != 0) { + zoomOut(me); + } else { + zoomIn(me); + } + } + + @Override + public void mousePressed(MouseEvent me) { + old_x = start_x = me.getX(); + old_y = start_y = me.getY(); + } + + @Override + public synchronized void mouseReleased(MouseEvent me) { + if (me.getX() != start_x || me.getY() != start_y) { + zoomBox(me); + } + + dragging = false; + notifyAll(); + } + + /** + * Zoom in with factor 2 + * @param me + */ + private void zoomIn(MouseEvent me) { + double offset_x = (fractalModel.getEndX() - fractalModel.getStartX()) / 4; + double offset_y = (fractalModel.getEndY() - fractalModel.getStartY()) / 4; + + double newCenterX = getRealX(me.getX()); + fractalModel.setStartX(newCenterX - offset_x); + fractalModel.setEndX(newCenterX + offset_x); + + double newCenterY = getRealY(me.getY()); + fractalModel.setStartY(newCenterY - offset_y); + fractalModel.setEndY(newCenterY + offset_y); + } + + /** + * Zoom out with factor 2 + * @param me + */ + private void zoomOut(MouseEvent me) { + double offset_x = fractalModel.getEndX() - fractalModel.getStartX(); + double offset_y = fractalModel.getEndY() - fractalModel.getStartY(); + + double newCenterX = getRealX(me.getX()); + fractalModel.setStartX(newCenterX - offset_x); + fractalModel.setEndX(newCenterX + offset_x); + + double newCenterY = getRealY(me.getY()); + fractalModel.setStartY(newCenterY - offset_y); + fractalModel.setEndY(newCenterY + offset_y); + } + + /** + * Zoom to the selected box. + * Intentionally chose to let the user only select squares, otherwise it's too easy to mess up the scale + * @param me + */ + private void zoomBox(MouseEvent me) { + double newStartX = getRealX(start_x), + newEndX = getRealX(me.getX()), + newStartY = getRealY(start_y), + newEndY = getRealY(start_y + me.getX() - start_x); + + fractalModel.setStartX(newStartX); + fractalModel.setEndX(newEndX); + fractalModel.setStartY(newStartY); + fractalModel.setEndY(newEndY); + } + + @Override + public void mouseEntered(MouseEvent me) {} + + @Override + public void mouseExited(MouseEvent me) {} + + @Override + public void mouseDragged(MouseEvent me) { + Graphics g = getSafeGraphics(); + if (g != null) { + if (dragging) { + eraseZoombox(); + } + g.drawRect(start_x, start_y, me.getX() - start_x, me.getX() - start_x); + old_y = old_x = me.getX(); + dragging = true; + } + } + + /** + * Semi-singleton construction for graphics + * @return + */ + private Graphics getSafeGraphics() { + if (graphics == null) { + graphics = getGraphics(); + graphics.setXORMode(Color.white); + } + return graphics; + } + + /** + * Erase the old zoombox if it exists + */ + private void eraseZoombox() { + if (start_x < 0 || start_y < 0 || old_x < 0 || old_y < 0) + return; + getSafeGraphics().drawRect(start_x, start_y, old_x - start_x, old_y - start_x); + } + + @Override + public void mouseMoved(MouseEvent me) {} + + protected class Updater extends SwingWorker<Map<Point,Integer>, Map<Point,Integer>> { + private boolean doneProcessing = true, stop = false; + + @Override + protected Map<Point, Integer> doInBackground() throws Exception { + Map<Point,Integer> results = new HashMap<>(); + + for (int repetitions = REPETITIONS_MAX / STEPS; repetitions <= REPETITIONS_MAX && !stop; repetitions += REPETITIONS_MAX / STEPS) { + for (int x = 0; x < width && !stop; x++) { + for (int y = 0; y < height && !stop; y++) { + Point p = new Point(x, y); + results.put(p, fractalModel.getMandelNumber(getRealX(x), getRealY(y), repetitions)); + } + } + + // Since we're always publishing the same object, there's no point in calling publish if process() wasn't called yet + if (doneProcessing && !stop) { + doneProcessing = false; + publish(results); + } + setProgress(repetitions * 100 / REPETITIONS_MAX); + } + + return results; + } + + @Override + protected synchronized void process(List<Map<Point,Integer>> results) { + if (progressView != null) { + progressView.setValue(getProgress()); + } + + for (Map<Point,Integer> resultMap : results) { + if (stop || dragging) + break; + + for (Entry<Point,Integer> result : resultMap.entrySet()) { + int rgbValue = colors[result.getValue()]; + int[] rgb = { (rgbValue >> 16) & 0xff, (rgbValue >> 8) & 0xff, rgbValue & 0xff }; + setPixel(result.getKey().x, result.getKey().y, rgb); + } + } + + doneProcessing = true; + } + + @Override + public void done() { + updater = null; + } + + public void stop() { + stop = true; + } + } + + protected class Point { + int x, y; + + public Point(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public boolean equals(Object another) { + if (another == null || another.getClass() != Point.class) { + return false; + } + Point that = (Point) another; + return that.x == x && that.y == y; + } + + @Override + public int hashCode() { + return (x << 8) | y; + } + } + + protected class ProgressView extends JProgressBar { + + public ProgressView() { + setMaximum(100); + setMinimum(0); + } + + @Override + public void setValue(int n) { + setVisible(n != 100); + super.setValue(n); + } + + } + +} diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotController.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotController.java deleted file mode 100644 index adca194..0000000 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotController.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2015 Camil Staps - */ -package com.camilstaps.mandelbrot; - -/** - * - * @author camilstaps - */ -public class MandelbrotController { - - private final MandelbrotFractal fractal; - - public MandelbrotController(MandelbrotFractal fractal) { - this.fractal = fractal; - } - -} diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotFractal.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotFractal.java index e0c0520..1c608fd 100644 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotFractal.java +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotFractal.java @@ -72,7 +72,7 @@ public class MandelbrotFractal { protected static void calculateMandelNumber(Point p, int repetitions, Result start) { int n = start.repetitions; - while (start.x * start.x + start.y * start.y <= 4 && n <= repetitions) { + while (start.x * start.x + start.y * start.y <= 4 && n < repetitions) { double new_x = start.x * start.x - start.y * start.y + p.x; start.y = 2 * start.x * start.y + p.y; start.x = new_x; @@ -114,7 +114,7 @@ public class MandelbrotFractal { } protected static class Result { - int mandelNumber = -1, repetitions = -1; + 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 225347a..9322be9 100644 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotWindow.java +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/MandelbrotWindow.java @@ -23,33 +23,40 @@ */ package com.camilstaps.mandelbrot; -import javax.swing.JButton; -import javax.swing.JTextField; +import java.awt.BorderLayout; +import java.awt.Dimension; +import javax.swing.JFrame; /** * Solutions to week 15 * @author Camil Staps */ -public class MandelbrotWindow { - - private final DrawView drawView; - - private final String INITIAL_CENTER_X = "0", - INITIAL_CENTER_Y = "0", - INITIAL_SCALE = "100", - INITIAL_REPETITIONS = "100"; - - 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); - private final JTextField field_repetitions = new JTextField(INITIAL_REPETITIONS, 6); - private final JButton button_redraw = new JButton("Redraw"); +public class MandelbrotWindow extends JFrame { private MandelbrotWindow() { + super("Mandelbrot"); + FractalModel fm = new FractalModel(); - drawView = new DrawView(fm); - ZoomFrame frame = new ZoomFrame("Mandelbrot", drawView); + setLayout(new BorderLayout()); + + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + setVisible(true); + + Grid grid = new Grid(fm); + Textfields textfields = new Textfields(fm); + + add(grid, BorderLayout.CENTER); + add(grid.getProgressView(), BorderLayout.SOUTH); + add(textfields, BorderLayout.EAST); + + getContentPane().setPreferredSize(new Dimension( + grid.getWidth() + textfields.getWidth(), + Math.max(grid.getHeight(), textfields.getHeight()) + grid.getProgressView().getHeight() + )); + pack(); + setResizable(false); } public static void main(String[] args) { diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Textfields.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Textfields.java new file mode 100644 index 0000000..78098d1 --- /dev/null +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/Textfields.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015 Camil Staps + */ +package com.camilstaps.mandelbrot; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Observable; +import java.util.Observer; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/** + * + * @author camilstaps + */ +public class Textfields extends JPanel implements Observer { + + private final FractalModel fractalModel; + + private final String INITIAL_CENTER_X = "0", + INITIAL_CENTER_Y = "0", + INITIAL_SCALE = "0.5", + INITIAL_REPETITIONS = "100"; + + 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); + private final JTextField field_repetitions = new JTextField(INITIAL_REPETITIONS, 6); + private final JButton button_redraw = new JButton("Redraw"); + private final JButton button_reset = new JButton("Reset"); + + public Textfields(FractalModel fractalModel) { + super(new BorderLayout()); + + this.fractalModel = fractalModel; + fractalModel.addObserver(this); + + setupControls(); + } + + private void setupControls() { + JPanel panel = new JPanel(new GridLayout(10,1)); + + panel.add(new JLabel("Center x:")); + panel.add(field_centerX); + + panel.add(new JLabel("Center y:")); + panel.add(field_centerY); + + panel.add(new JLabel("Scale:")); + panel.add(field_scale); + + panel.add(new JLabel("Repetitions:")); + panel.add(field_repetitions); + + button_redraw.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ae) { + if (ae.getActionCommand().equals("Redraw")) { + fractalModel.setBorders(getStartX(), getEndX(), getStartY(), getEndY()); + } + } + }); + panel.add(button_redraw); + + button_reset.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ae) { + field_centerX.setText(INITIAL_CENTER_X); + field_centerY.setText(INITIAL_CENTER_Y); + field_scale.setText(INITIAL_SCALE); + field_repetitions.setText(INITIAL_REPETITIONS); + + button_redraw.doClick(); + } + }); + panel.add(button_reset); + + add(panel, BorderLayout.NORTH); + add(new JPanel(), BorderLayout.CENTER); + } + + @Override + public int getWidth() { + return 100; + } + + protected double getStartX() { + double width = 1 / Double.parseDouble(field_scale.getText()); + double center = Double.parseDouble(field_centerX.getText()); + return center - width / 2; + } + + protected double getEndX() { + double width = 1 / Double.parseDouble(field_scale.getText()); + double center = Double.parseDouble(field_centerX.getText()); + return center + width / 2; + } + + protected double getStartY() { + double height = 1 / Double.parseDouble(field_scale.getText()); + double center = Double.parseDouble(field_centerY.getText()); + return center - height / 2; + } + + protected double getEndY() { + double height = 1 / Double.parseDouble(field_scale.getText()); + double center = Double.parseDouble(field_centerY.getText()); + return center + height / 2; + } + + @Override + public void update(Observable o, Object o1) { + field_centerX.setText(Float.toString((float) + ((fractalModel.getEndX() - fractalModel.getStartX()) / 2 + fractalModel.getStartX()))); + field_centerY.setText(Float.toString((float) + ((fractalModel.getEndY() - fractalModel.getStartY()) / 2 + fractalModel.getStartY()))); + field_scale.setText(Float.toString((float) + (1 / (fractalModel.getEndX() - fractalModel.getStartX())))); + } + +} diff --git a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/ZoomFrame.java b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/ZoomFrame.java index 38d16fc..071bb8c 100644 --- a/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/ZoomFrame.java +++ b/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/ZoomFrame.java @@ -23,7 +23,8 @@ */ package com.camilstaps.mandelbrot; -import java.awt.Color; +import java.awt.BorderLayout; +import java.awt.Dimension; import java.awt.Graphics; import javax.swing.JFrame; @@ -35,33 +36,22 @@ public class ZoomFrame extends JFrame { private Graphics graphics; - private final DrawView drawView; + private final Grid drawView; - public ZoomFrame(String s, DrawView drawView) { + public ZoomFrame(String s, Grid drawView) { super(s); this.drawView = drawView; - setSize(drawView.getWidth(), drawView.getHeight()); + 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); - } - - /** - * Semi-singleton construction for graphics - * @return - */ - private Graphics getSafeGraphics() { - if (graphics == null) { - graphics = getGraphics(); - graphics.setXORMode(Color.white); - } - - return graphics; + add(drawView, BorderLayout.CENTER); } } |