/* * 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.ArrayList; 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> { private boolean doneProcessing = true; @Override protected Map doInBackground() throws Exception { Map 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> results) { for (Map resultMap : results) { for (Entry 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; } } }