progs/mandelbrot.scala
changeset 186 f211d9cb856e
parent 174 90e0b1cc460b
child 187 4d300409f2fe
--- a/progs/mandelbrot.scala	Sat Jun 16 20:55:51 2018 +0100
+++ b/progs/mandelbrot.scala	Fri Jun 22 13:34:05 2018 +0100
@@ -6,89 +6,105 @@
 import javax.swing.JFrame
 import javax.swing.JPanel
 import javax.swing.WindowConstants
+import scala.language.implicitConversions    
 
 // complex numbers
-case class Complex(val a: Double, val b: Double) { 
-    // represents the complex number a + b * i
-    def +(that: Complex) = Complex(this.a + that.a, this.b + that.b)
-    def -(that: Complex) = Complex(this.a - that.a, this.b - that.b)
-    def *(that: Complex) = Complex(this.a * that.a - this.b * that.b,
-                                   this.a * that.b + that.a * this.b)
-    def *(that: Double) = Complex(this.a * that, this.b * that)
-    def abs() = Math.sqrt(this.a * this.a + this.b * this.b)
+case class Complex(val re: Double, val im: Double) { 
+  // represents the complex number re + im * i
+  def +(that: Complex) = Complex(this.re + that.re, this.im + that.im)
+  def -(that: Complex) = Complex(this.re - that.re, this.im - that.im)
+  def *(that: Complex) = Complex(this.re * that.re - this.im * that.im,
+                                 this.re * that.im + that.re * this.im)
+  def *(that: Double) = Complex(this.re * that, this.im * that)
+  def abs() = Math.sqrt(this.re * this.re + this.im * this.im)
 }
 
-// some customn colours
+object i extends Complex(0, 1)
+
+implicit def double2complex(re: Double): Complex = Complex(re, 0)
+
+// some customn colours for the "sliding effect"
 val colours = List(
-    new Color(66, 30, 15),    new Color(25, 7, 26),
-    new Color(9, 1, 47),      new Color(4, 4, 73),
-    new Color(0, 7, 100),     new Color(12, 44, 138),
-    new Color(24, 82, 177),   new Color(57, 125, 209),
-    new Color(134, 181, 229), new Color(211, 236, 248),
-    new Color(241, 233, 191), new Color(248, 201, 95),
-    new Color(255, 170, 0),   new Color(204, 128, 0),
-    new Color(153, 87, 0),    new Color(106, 52, 3))
+  new Color(66, 30, 15),    new Color(25, 7, 26),
+  new Color(9, 1, 47),      new Color(4, 4, 73),
+  new Color(0, 7, 100),     new Color(12, 44, 138),
+  new Color(24, 82, 177),   new Color(57, 125, 209),
+  new Color(134, 181, 229), new Color(211, 236, 248),
+  new Color(241, 233, 191), new Color(248, 201, 95),
+  new Color(255, 170, 0),   new Color(204, 128, 0),
+  new Color(153, 87, 0),    new Color(106, 52, 3))
 
-// the viewer panel
+// the viewer panel with a canvas
 class Viewer(width: Int, height: Int) extends JPanel {
-    val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
-    clearCanvas(Color.black)
-
-    override def paintComponent(g: Graphics) = 
-      g.asInstanceOf[Graphics2D].drawImage(canvas, null, null)
+  val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
+  
+  override def paintComponent(g: Graphics) = 
+    g.asInstanceOf[Graphics2D].drawImage(canvas, null, null)
+  
+  override def getPreferredSize() = 
+    new Dimension(width, height)
 
-    override def getPreferredSize() = 
-       new Dimension(width, height)
-
-    def clearCanvas(color: Color) = {
-      for(x <- 0 to width - 1;
-          y <- 0 to height - 1) canvas.setRGB(x, y, color.getRGB())
-      repaint()
-    }  
-
+  def clearCanvas(color: Color) = {
+    for (x <- 0 to width - 1; y <- 0 to height - 1) 
+      canvas.setRGB(x, y, color.getRGB())
+    repaint()
+  }  
 }
 
-def openViewer(width: Int, height: Int) = {
-    val frame = new JFrame("XYPlane")
-    val viewer = new Viewer(width, height)
-    frame.add(viewer)
-    frame.pack()
-    frame.setVisible(true)
-    frame.setResizable(false)
-    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
-    viewer
+// initialising the viewer
+def openViewer(width: Int, height: Int) : Viewer = {
+  val frame = new JFrame("XYPlane")
+  val viewer = new Viewer(width, height)
+  frame.add(viewer)
+  frame.pack()
+  frame.setVisible(true)
+  frame.setResizable(false)
+  frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
+  viewer
 }
 
-val W = 900
-val H = 800 
+// some hardcoded data
+val W = 900   // width
+val H = 800   // height
 val black = Color.black
 val viewer = openViewer(W, H)
 
-
+// drawing a pixel on the canvas
 def pixel(x: Int, y: Int, color: Color) = 
   viewer.canvas.setRGB(x, y, color.getRGB())
-  
+
 
-def mandelbrot(start: Complex, end: Complex, level: Int) : Unit = {
+// calculating the number of iterations using lazy streams
+//   the iteration goes on for a maximum of max steps,
+//   but might leave early when the pred is satisfied
+def iterations(c: Complex, max: Int) : Int = {
+  def next(z: Complex) = z * z + c    
+  def pred(z: Complex) = z.abs < 2    // exit condition
+  Stream.iterate(0.0 * i, max)(next).takeWhile(pred).size
+}
+
+// main function 
+def mandelbrot(start: Complex, end: Complex, max: Int) : Unit = {
   viewer.clearCanvas(black)
-   
-  val delta_x = (end.a - start.a) / W
-  val delta_y = (end.b - start.b) / H
+  
+  // deltas for each grid step 
+  val d_x = (end.re - start.re) / W
+  val d_y = (end.im - start.im) / H
    
-  for (y0 <- (0 until H)) {
-    for (x0 <- (0 until W)) {
+  for (y <- (0 until H).par) {
+    for (x <- (0 until W).par) {
     
-     val c = start + Complex(x0 * delta_x, y0 * delta_y)
+     val c = start + 
+      (x * d_x + y * d_y * i)
+     val iters = iterations(c, max) 
+     val col = 
+       if (iters == max) black 
+       else colours(iters % 16)
 
-     def iters(z: Complex, it: Int) : Color = {
-       if (it < level && z.abs < 2) iters(z * z + c, it + 1) else 
-        if (it == level) black else colours(it % 16) 
-     }
-
-     pixel(x0, y0, iters(Complex(0, 0), 0))
+     pixel(x, y, col)
     }
     viewer.updateUI()
-  }
+  }   
 }
 
 // Examples
@@ -104,43 +120,38 @@
 
 
 // example 1
-val exa1 = Complex(-2.0, -1.5)
-val exa2 = Complex( 1.0,  1.5)
+val exa1 = -2.0 + -1.5 * i
+val exa2 =  1.0 +  1.5 * i
 
 time_needed(mandelbrot(exa1, exa2, 1000))
 
 // example 2
-val exb1 = Complex(-0.37465401, 0.659227668)
-val exb2 = Complex(-0.37332410, 0.66020767)
+val exb1 = -0.37465401 + 0.659227668 * i
+val exb2 = -0.37332410 + 0.66020767 * i
 
-time_needed(mandelbrot(exb1, exb2, 1000))
+//time_needed(mandelbrot(exb1, exb2, 1000))
 
 // example 3
-val exc1 = Complex(0.435396403, 0.367981352)
-val exc2 = Complex(0.451687191, 0.380210061)
+val exc1 = 0.435396403 + 0.367981352 * i
+val exc2 = 0.451687191 + 0.380210061 * i
 
 //time_needed(mandelbrot(exc1, exc2, 1000))
 
 // some more computations with example 3
+
 val delta = (exc2 - exc1) * 0.0333
 
+
 time_needed(
-  for (i <- (0 to 12)) 
-     mandelbrot(exc1 + delta * i, 
-                exc2 - delta * i, 1000))
-
-val exc1 = Complex(0.435396403, 0.367981352)
-val exc2 = Complex(0.451687191, 0.380210061)
+  for (n <- (0 to 12)) 
+     mandelbrot(exc1 + delta * n, 
+                exc2 - delta * n, 100)) 
 
-//time_needed(mandelbrot(exc1, exc2, 1000))
-
-// some more computations with example 3
-val delta = (exc2 - exc1) * 0.0333
-
+/*
 time_needed(
-  for (i <- (0 to 12)) 
-     mandelbrot(exc1 + delta * i, 
-                exc2 - delta * i, 1000))
+  for (n <- (0 to 12)) 
+     mandelbrot(exc1 + delta * n, 
+                exc2 - delta * n, 1000))
+*/
 
 
-