aboutsummaryrefslogtreecommitdiff
path: root/Week15 Mandelbrot/src/com/camilstaps/mandelbrot/DrawView.java
blob: 4d73434476e32894ba67b6b7af1e8108dfe8fd0b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
 * 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;
        }
    }
    
}