| author | Christian Urban <christian.urban@kcl.ac.uk> | 
| Sat, 23 Sep 2023 23:49:44 +0100 | |
| changeset 467 | 1b879b3e704e | 
| parent 461 | eda26fa6d3ec | 
| child 468 | c71ae4477e55 | 
| permissions | -rw-r--r-- | 
| 187 | 1  | 
// Mandelbrot pictures  | 
| 309 | 2  | 
//=====================  | 
| 266 | 3  | 
//  | 
| 187 | 4  | 
// see https://en.wikipedia.org/wiki/Mandelbrot_set  | 
| 266 | 5  | 
//  | 
| 467 | 6  | 
// needs to be called with  | 
| 309 | 7  | 
//  | 
| 
461
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
8  | 
// scala -cp scala-parallel-collections_3-1.0.4.jar  | 
| 
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
9  | 
//  | 
| 
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
10  | 
// !! UPDATE: On my faster Mac-M1 machine the times  | 
| 
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
11  | 
// !! are ca. 4 secs for the sequential version and  | 
| 
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
12  | 
// !! around 0.7 secs for the par-version.  | 
| 
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
13  | 
|
| 187 | 14  | 
|
| 124 | 15  | 
import java.awt.Color  | 
16  | 
import java.awt.Dimension  | 
|
17  | 
import java.awt.Graphics  | 
|
18  | 
import java.awt.Graphics2D  | 
|
19  | 
import java.awt.image.BufferedImage  | 
|
20  | 
import javax.swing.JFrame  | 
|
21  | 
import javax.swing.JPanel  | 
|
22  | 
import javax.swing.WindowConstants  | 
|
| 186 | 23  | 
import scala.language.implicitConversions  | 
| 266 | 24  | 
import scala.collection.parallel.CollectionConverters._  | 
| 265 | 25  | 
|
| 124 | 26  | 
// complex numbers  | 
| 186 | 27  | 
case class Complex(val re: Double, val im: Double) { 
 | 
28  | 
// represents the complex number re + im * i  | 
|
29  | 
def +(that: Complex) = Complex(this.re + that.re, this.im + that.im)  | 
|
30  | 
def -(that: Complex) = Complex(this.re - that.re, this.im - that.im)  | 
|
31  | 
def *(that: Complex) = Complex(this.re * that.re - this.im * that.im,  | 
|
32  | 
this.re * that.im + that.re * this.im)  | 
|
33  | 
def *(that: Double) = Complex(this.re * that, this.im * that)  | 
|
34  | 
def abs() = Math.sqrt(this.re * this.re + this.im * this.im)  | 
|
| 124 | 35  | 
}  | 
36  | 
||
| 187 | 37  | 
// to allow the notation n + m * i  | 
| 186 | 38  | 
object i extends Complex(0, 1)  | 
| 
461
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
39  | 
|
| 
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
40  | 
// implicit conversion from Doubles to Complex  | 
| 
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
41  | 
given Conversion[Double, Complex] = Complex(_, 0)  | 
| 186 | 42  | 
|
43  | 
||
44  | 
// some customn colours for the "sliding effect"  | 
|
| 124 | 45  | 
val colours = List(  | 
| 186 | 46  | 
new Color(66, 30, 15), new Color(25, 7, 26),  | 
47  | 
new Color(9, 1, 47), new Color(4, 4, 73),  | 
|
48  | 
new Color(0, 7, 100), new Color(12, 44, 138),  | 
|
49  | 
new Color(24, 82, 177), new Color(57, 125, 209),  | 
|
50  | 
new Color(134, 181, 229), new Color(211, 236, 248),  | 
|
51  | 
new Color(241, 233, 191), new Color(248, 201, 95),  | 
|
52  | 
new Color(255, 170, 0), new Color(204, 128, 0),  | 
|
53  | 
new Color(153, 87, 0), new Color(106, 52, 3))  | 
|
| 124 | 54  | 
|
| 266 | 55  | 
// the viewer panel with an image canvas  | 
| 124 | 56  | 
class Viewer(width: Int, height: Int) extends JPanel {
 | 
| 186 | 57  | 
val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)  | 
58  | 
||
59  | 
override def paintComponent(g: Graphics) =  | 
|
60  | 
g.asInstanceOf[Graphics2D].drawImage(canvas, null, null)  | 
|
61  | 
||
62  | 
override def getPreferredSize() =  | 
|
63  | 
new Dimension(width, height)  | 
|
| 124 | 64  | 
|
| 186 | 65  | 
  def clearCanvas(color: Color) = {
 | 
66  | 
for (x <- 0 to width - 1; y <- 0 to height - 1)  | 
|
67  | 
canvas.setRGB(x, y, color.getRGB())  | 
|
68  | 
repaint()  | 
|
69  | 
}  | 
|
| 124 | 70  | 
}  | 
71  | 
||
| 266 | 72  | 
// initialising the viewer panel  | 
| 186 | 73  | 
def openViewer(width: Int, height: Int) : Viewer = {
 | 
74  | 
  val frame = new JFrame("XYPlane")
 | 
|
75  | 
val viewer = new Viewer(width, height)  | 
|
76  | 
frame.add(viewer)  | 
|
77  | 
frame.pack()  | 
|
78  | 
frame.setVisible(true)  | 
|
79  | 
frame.setResizable(false)  | 
|
80  | 
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)  | 
|
81  | 
viewer  | 
|
| 124 | 82  | 
}  | 
83  | 
||
| 266 | 84  | 
// some hardcoded parameters  | 
| 186 | 85  | 
val W = 900 // width  | 
86  | 
val H = 800 // height  | 
|
| 124 | 87  | 
val black = Color.black  | 
88  | 
val viewer = openViewer(W, H)  | 
|
89  | 
||
| 266 | 90  | 
// draw a pixel on the canvas  | 
| 124 | 91  | 
def pixel(x: Int, y: Int, color: Color) =  | 
92  | 
viewer.canvas.setRGB(x, y, color.getRGB())  | 
|
| 186 | 93  | 
|
| 124 | 94  | 
|
| 266 | 95  | 
// calculates the number of iterations using lazy lists (streams)  | 
| 186 | 96  | 
// the iteration goes on for a maximum of max steps,  | 
97  | 
// but might leave early when the pred is satisfied  | 
|
98  | 
def iterations(c: Complex, max: Int) : Int = {
 | 
|
99  | 
def next(z: Complex) = z * z + c  | 
|
| 353 | 100  | 
def pred(z: Complex) = z.abs() < 2 // exit condition  | 
| 266 | 101  | 
LazyList.iterate(0.0 * i, max)(next).takeWhile(pred).size  | 
| 186 | 102  | 
}  | 
103  | 
||
104  | 
// main function  | 
|
| 266 | 105  | 
// start and end are the upper-left and lower-right corners,  | 
| 187 | 106  | 
// max is the number of maximum iterations  | 
| 186 | 107  | 
def mandelbrot(start: Complex, end: Complex, max: Int) : Unit = {
 | 
| 124 | 108  | 
viewer.clearCanvas(black)  | 
| 186 | 109  | 
|
110  | 
// deltas for each grid step  | 
|
111  | 
val d_x = (end.re - start.re) / W  | 
|
112  | 
val d_y = (end.im - start.im) / H  | 
|
| 124 | 113  | 
|
| 
461
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
114  | 
  for (y <- (0 until H)) {
 | 
| 
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
115  | 
    for (x <- (0 until W)) {
 | 
| 124 | 116  | 
|
| 186 | 117  | 
val c = start +  | 
118  | 
(x * d_x + y * d_y * i)  | 
|
119  | 
val iters = iterations(c, max)  | 
|
| 
461
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
120  | 
val colour =  | 
| 186 | 121  | 
if (iters == max) black  | 
122  | 
else colours(iters % 16)  | 
|
| 124 | 123  | 
|
| 
461
 
eda26fa6d3ec
updated to scala 3
 
Christian Urban <christian.urban@kcl.ac.uk> 
parents: 
441 
diff
changeset
 | 
124  | 
pixel(x, y, colour)  | 
| 143 | 125  | 
}  | 
126  | 
viewer.updateUI()  | 
|
| 186 | 127  | 
}  | 
| 124 | 128  | 
}  | 
129  | 
||
| 187 | 130  | 
|
| 124 | 131  | 
// Examples  | 
132  | 
//==========  | 
|
133  | 
||
134  | 
//for measuring time  | 
|
135  | 
def time_needed[T](code: => T) = {
 | 
|
136  | 
val start = System.nanoTime()  | 
|
137  | 
code  | 
|
138  | 
val end = System.nanoTime()  | 
|
139  | 
(end - start) / 1.0e9  | 
|
140  | 
}  | 
|
141  | 
||
142  | 
||
| 353 | 143  | 
|
| 124 | 144  | 
// example 1  | 
| 186 | 145  | 
val exa1 = -2.0 + -1.5 * i  | 
146  | 
val exa2 = 1.0 + 1.5 * i  | 
|
| 124 | 147  | 
|
| 309 | 148  | 
println(s"${time_needed(mandelbrot(exa1, exa2, 1000))} secs")
 | 
| 124 | 149  | 
|
| 136 | 150  | 
// example 2  | 
| 186 | 151  | 
val exb1 = -0.37465401 + 0.659227668 * i  | 
152  | 
val exb2 = -0.37332410 + 0.66020767 * i  | 
|
| 124 | 153  | 
|
| 186 | 154  | 
//time_needed(mandelbrot(exb1, exb2, 1000))  | 
| 124 | 155  | 
|
156  | 
// example 3  | 
|
| 186 | 157  | 
val exc1 = 0.435396403 + 0.367981352 * i  | 
158  | 
val exc2 = 0.451687191 + 0.380210061 * i  | 
|
| 124 | 159  | 
|
| 166 | 160  | 
//time_needed(mandelbrot(exc1, exc2, 1000))  | 
| 124 | 161  | 
|
| 266 | 162  | 
|
163  | 
||
| 124 | 164  | 
// some more computations with example 3  | 
| 186 | 165  | 
|
| 124 | 166  | 
val delta = (exc2 - exc1) * 0.0333  | 
167  | 
||
| 394 | 168  | 
//println(s"${time_needed(
 | 
169  | 
// for (n <- (0 to 12))  | 
|
170  | 
// mandelbrot(exc1 + delta * n,  | 
|
171  | 
// exc2 - delta * n, 100))} secs")  | 
|
| 266 | 172  | 
|
| 124 | 173  | 
|
174  | 
||
| 191 | 175  | 
// Larry Paulson's example  | 
176  | 
val exl1 = -0.74364990 + 0.13188170 * i  | 
|
177  | 
val exl2 = -0.74291189 + 0.13261971 * i  | 
|
| 189 | 178  | 
|
| 309 | 179  | 
//println(s"${time_needed(mandelbrot(exl1, exl2, 1000))} secs")
 | 
| 189 | 180  | 
|
| 191 | 181  | 
|
182  | 
// example by Jorgen Villadsen  | 
|
183  | 
val exj1 = 0.10284 - 0.63275 * i  | 
|
184  | 
val exj2 = 0.11084 - 0.64075 * i  | 
|
185  | 
||
| 195 | 186  | 
//time_needed(mandelbrot(exj1, exj2, 1000))  | 
| 394 | 187  | 
|
188  | 
||
189  | 
// another example  | 
|
190  | 
val exA = 0.3439274 + 0.6516478 * i  | 
|
191  | 
val exB = 0.3654477 + 0.6301795 * i  | 
|
192  | 
||
193  | 
//time_needed(mandelbrot(exA, exB, 1000))  |