4 import java.awt.Graphics2D |
4 import java.awt.Graphics2D |
5 import java.awt.image.BufferedImage |
5 import java.awt.image.BufferedImage |
6 import javax.swing.JFrame |
6 import javax.swing.JFrame |
7 import javax.swing.JPanel |
7 import javax.swing.JPanel |
8 import javax.swing.WindowConstants |
8 import javax.swing.WindowConstants |
|
9 import scala.language.implicitConversions |
9 |
10 |
10 // complex numbers |
11 // complex numbers |
11 case class Complex(val a: Double, val b: Double) { |
12 case class Complex(val re: Double, val im: Double) { |
12 // represents the complex number a + b * i |
13 // represents the complex number re + im * i |
13 def +(that: Complex) = Complex(this.a + that.a, this.b + that.b) |
14 def +(that: Complex) = Complex(this.re + that.re, this.im + that.im) |
14 def -(that: Complex) = Complex(this.a - that.a, this.b - that.b) |
15 def -(that: Complex) = Complex(this.re - that.re, this.im - that.im) |
15 def *(that: Complex) = Complex(this.a * that.a - this.b * that.b, |
16 def *(that: Complex) = Complex(this.re * that.re - this.im * that.im, |
16 this.a * that.b + that.a * this.b) |
17 this.re * that.im + that.re * this.im) |
17 def *(that: Double) = Complex(this.a * that, this.b * that) |
18 def *(that: Double) = Complex(this.re * that, this.im * that) |
18 def abs() = Math.sqrt(this.a * this.a + this.b * this.b) |
19 def abs() = Math.sqrt(this.re * this.re + this.im * this.im) |
19 } |
20 } |
20 |
21 |
21 // some customn colours |
22 object i extends Complex(0, 1) |
|
23 |
|
24 implicit def double2complex(re: Double): Complex = Complex(re, 0) |
|
25 |
|
26 // some customn colours for the "sliding effect" |
22 val colours = List( |
27 val colours = List( |
23 new Color(66, 30, 15), new Color(25, 7, 26), |
28 new Color(66, 30, 15), new Color(25, 7, 26), |
24 new Color(9, 1, 47), new Color(4, 4, 73), |
29 new Color(9, 1, 47), new Color(4, 4, 73), |
25 new Color(0, 7, 100), new Color(12, 44, 138), |
30 new Color(0, 7, 100), new Color(12, 44, 138), |
26 new Color(24, 82, 177), new Color(57, 125, 209), |
31 new Color(24, 82, 177), new Color(57, 125, 209), |
27 new Color(134, 181, 229), new Color(211, 236, 248), |
32 new Color(134, 181, 229), new Color(211, 236, 248), |
28 new Color(241, 233, 191), new Color(248, 201, 95), |
33 new Color(241, 233, 191), new Color(248, 201, 95), |
29 new Color(255, 170, 0), new Color(204, 128, 0), |
34 new Color(255, 170, 0), new Color(204, 128, 0), |
30 new Color(153, 87, 0), new Color(106, 52, 3)) |
35 new Color(153, 87, 0), new Color(106, 52, 3)) |
31 |
36 |
32 // the viewer panel |
37 // the viewer panel with a canvas |
33 class Viewer(width: Int, height: Int) extends JPanel { |
38 class Viewer(width: Int, height: Int) extends JPanel { |
34 val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) |
39 val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) |
35 clearCanvas(Color.black) |
40 |
|
41 override def paintComponent(g: Graphics) = |
|
42 g.asInstanceOf[Graphics2D].drawImage(canvas, null, null) |
|
43 |
|
44 override def getPreferredSize() = |
|
45 new Dimension(width, height) |
36 |
46 |
37 override def paintComponent(g: Graphics) = |
47 def clearCanvas(color: Color) = { |
38 g.asInstanceOf[Graphics2D].drawImage(canvas, null, null) |
48 for (x <- 0 to width - 1; y <- 0 to height - 1) |
39 |
49 canvas.setRGB(x, y, color.getRGB()) |
40 override def getPreferredSize() = |
50 repaint() |
41 new Dimension(width, height) |
51 } |
42 |
|
43 def clearCanvas(color: Color) = { |
|
44 for(x <- 0 to width - 1; |
|
45 y <- 0 to height - 1) canvas.setRGB(x, y, color.getRGB()) |
|
46 repaint() |
|
47 } |
|
48 |
|
49 } |
52 } |
50 |
53 |
51 def openViewer(width: Int, height: Int) = { |
54 // initialising the viewer |
52 val frame = new JFrame("XYPlane") |
55 def openViewer(width: Int, height: Int) : Viewer = { |
53 val viewer = new Viewer(width, height) |
56 val frame = new JFrame("XYPlane") |
54 frame.add(viewer) |
57 val viewer = new Viewer(width, height) |
55 frame.pack() |
58 frame.add(viewer) |
56 frame.setVisible(true) |
59 frame.pack() |
57 frame.setResizable(false) |
60 frame.setVisible(true) |
58 frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE) |
61 frame.setResizable(false) |
59 viewer |
62 frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE) |
|
63 viewer |
60 } |
64 } |
61 |
65 |
62 val W = 900 |
66 // some hardcoded data |
63 val H = 800 |
67 val W = 900 // width |
|
68 val H = 800 // height |
64 val black = Color.black |
69 val black = Color.black |
65 val viewer = openViewer(W, H) |
70 val viewer = openViewer(W, H) |
66 |
71 |
67 |
72 // drawing a pixel on the canvas |
68 def pixel(x: Int, y: Int, color: Color) = |
73 def pixel(x: Int, y: Int, color: Color) = |
69 viewer.canvas.setRGB(x, y, color.getRGB()) |
74 viewer.canvas.setRGB(x, y, color.getRGB()) |
|
75 |
|
76 |
|
77 // calculating the number of iterations using lazy streams |
|
78 // the iteration goes on for a maximum of max steps, |
|
79 // but might leave early when the pred is satisfied |
|
80 def iterations(c: Complex, max: Int) : Int = { |
|
81 def next(z: Complex) = z * z + c |
|
82 def pred(z: Complex) = z.abs < 2 // exit condition |
|
83 Stream.iterate(0.0 * i, max)(next).takeWhile(pred).size |
|
84 } |
|
85 |
|
86 // main function |
|
87 def mandelbrot(start: Complex, end: Complex, max: Int) : Unit = { |
|
88 viewer.clearCanvas(black) |
70 |
89 |
|
90 // deltas for each grid step |
|
91 val d_x = (end.re - start.re) / W |
|
92 val d_y = (end.im - start.im) / H |
|
93 |
|
94 for (y <- (0 until H).par) { |
|
95 for (x <- (0 until W).par) { |
|
96 |
|
97 val c = start + |
|
98 (x * d_x + y * d_y * i) |
|
99 val iters = iterations(c, max) |
|
100 val col = |
|
101 if (iters == max) black |
|
102 else colours(iters % 16) |
71 |
103 |
72 def mandelbrot(start: Complex, end: Complex, level: Int) : Unit = { |
104 pixel(x, y, col) |
73 viewer.clearCanvas(black) |
|
74 |
|
75 val delta_x = (end.a - start.a) / W |
|
76 val delta_y = (end.b - start.b) / H |
|
77 |
|
78 for (y0 <- (0 until H)) { |
|
79 for (x0 <- (0 until W)) { |
|
80 |
|
81 val c = start + Complex(x0 * delta_x, y0 * delta_y) |
|
82 |
|
83 def iters(z: Complex, it: Int) : Color = { |
|
84 if (it < level && z.abs < 2) iters(z * z + c, it + 1) else |
|
85 if (it == level) black else colours(it % 16) |
|
86 } |
|
87 |
|
88 pixel(x0, y0, iters(Complex(0, 0), 0)) |
|
89 } |
105 } |
90 viewer.updateUI() |
106 viewer.updateUI() |
91 } |
107 } |
92 } |
108 } |
93 |
109 |
94 // Examples |
110 // Examples |
95 //========== |
111 //========== |
96 |
112 |
102 (end - start) / 1.0e9 |
118 (end - start) / 1.0e9 |
103 } |
119 } |
104 |
120 |
105 |
121 |
106 // example 1 |
122 // example 1 |
107 val exa1 = Complex(-2.0, -1.5) |
123 val exa1 = -2.0 + -1.5 * i |
108 val exa2 = Complex( 1.0, 1.5) |
124 val exa2 = 1.0 + 1.5 * i |
109 |
125 |
110 time_needed(mandelbrot(exa1, exa2, 1000)) |
126 time_needed(mandelbrot(exa1, exa2, 1000)) |
111 |
127 |
112 // example 2 |
128 // example 2 |
113 val exb1 = Complex(-0.37465401, 0.659227668) |
129 val exb1 = -0.37465401 + 0.659227668 * i |
114 val exb2 = Complex(-0.37332410, 0.66020767) |
130 val exb2 = -0.37332410 + 0.66020767 * i |
115 |
131 |
116 time_needed(mandelbrot(exb1, exb2, 1000)) |
132 //time_needed(mandelbrot(exb1, exb2, 1000)) |
117 |
133 |
118 // example 3 |
134 // example 3 |
119 val exc1 = Complex(0.435396403, 0.367981352) |
135 val exc1 = 0.435396403 + 0.367981352 * i |
120 val exc2 = Complex(0.451687191, 0.380210061) |
136 val exc2 = 0.451687191 + 0.380210061 * i |
121 |
137 |
122 //time_needed(mandelbrot(exc1, exc2, 1000)) |
138 //time_needed(mandelbrot(exc1, exc2, 1000)) |
123 |
139 |
124 // some more computations with example 3 |
140 // some more computations with example 3 |
|
141 |
125 val delta = (exc2 - exc1) * 0.0333 |
142 val delta = (exc2 - exc1) * 0.0333 |
126 |
143 |
127 time_needed( |
|
128 for (i <- (0 to 12)) |
|
129 mandelbrot(exc1 + delta * i, |
|
130 exc2 - delta * i, 1000)) |
|
131 |
|
132 val exc1 = Complex(0.435396403, 0.367981352) |
|
133 val exc2 = Complex(0.451687191, 0.380210061) |
|
134 |
|
135 //time_needed(mandelbrot(exc1, exc2, 1000)) |
|
136 |
|
137 // some more computations with example 3 |
|
138 val delta = (exc2 - exc1) * 0.0333 |
|
139 |
144 |
140 time_needed( |
145 time_needed( |
141 for (i <- (0 to 12)) |
146 for (n <- (0 to 12)) |
142 mandelbrot(exc1 + delta * i, |
147 mandelbrot(exc1 + delta * n, |
143 exc2 - delta * i, 1000)) |
148 exc2 - delta * n, 100)) |
|
149 |
|
150 /* |
|
151 time_needed( |
|
152 for (n <- (0 to 12)) |
|
153 mandelbrot(exc1 + delta * n, |
|
154 exc2 - delta * n, 1000)) |
|
155 */ |
144 |
156 |
145 |
157 |
146 |
|