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