progs/mandelbrot.scala
changeset 490 4778fefecd0c
parent 481 e03a0100ec46
equal deleted inserted replaced
489:d51cacc92480 490:4778fefecd0c
     1 // Mandelbrot pictures
     1 // Mandelbrot pictures
     2 //=====================
     2 //=====================
     3 //
     3 //
     4 //   see https://en.wikipedia.org/wiki/Mandelbrot_set
     4 //   see https://en.wikipedia.org/wiki/Mandelbrot_set
     5 // 
     5 //
     6 // needs to be called with
     6 // needs to be called with
     7 // 
       
     8 // scala-cli --extra-jars scala-parallel-collections_3-1.0.4.jar
       
     9 //
     7 //
    10 // the library is also uploaded to KEATS 
     8 //   scala-cli --extra-jars scala-parallel-collections_3-1.0.4.jar
    11 //
     9 //
    12 // !! UPDATE: On my faster Mac-M1 machine the times
    10 // the jar-file is uploaded to KEATS
    13 // !! are ca. 4 secs for the sequential version and
    11 //
    14 // !! around 0.7 secs for the par-version.
    12 //
       
    13 // !! UPDATE ON TIMING: On my faster Mac-M1 machine 
       
    14 // !! the times for the first example are ca. 4 secs for 
       
    15 // !! the sequential version and around 0.7 secs for the 
       
    16 // !! par-version.
    15 
    17 
    16 
    18 
    17 import java.awt.Color
    19 import javax.swing.{JFrame, JPanel, WindowConstants}
    18 import java.awt.Dimension
    20 import java.awt.{Color, Dimension, Graphics, Graphics2D}
    19 import java.awt.Graphics
       
    20 import java.awt.Graphics2D
       
    21 import java.awt.image.BufferedImage
    21 import java.awt.image.BufferedImage
    22 import javax.swing.JFrame
    22 
    23 import javax.swing.JPanel
    23 import scala.language.implicitConversions
    24 import javax.swing.WindowConstants
    24 import scala.collection.parallel.CollectionConverters.*
    25 import scala.language.implicitConversions    
       
    26 import scala.collection.parallel.CollectionConverters._
       
    27 
    25 
    28 // complex numbers
    26 // complex numbers
    29 case class Complex(val re: Double, val im: Double) { 
    27 // represents the complex number re + im * i
    30   // represents the complex number re + im * i
    28 case class Complex(val re: Double, val im: Double) {
       
    29   
    31   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)
    32   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)
    33   def *(that: Complex) = Complex(this.re * that.re - this.im * that.im,
    32   def *(that: Complex) = Complex(this.re * that.re - this.im * that.im,
    34                                  this.re * that.im + that.re * this.im)
    33                                  this.re * that.im + that.re * this.im)
    35   def *(that: Double) = Complex(this.re * that, this.im * that)
    34   def *(that: Double) = Complex(this.re * that, this.im * that)
    36   def abs() = Math.sqrt(this.re * this.re + this.im * this.im)
    35   def abs() = Math.sqrt(this.re * this.re + this.im * this.im)
    37 }
    36 }
    38 
    37 
    39 // to allow the notation n + m * i
    38 // to allow the usual mathmo notation n + m * i
    40 object i extends Complex(0, 1)
    39 object i extends Complex(0, 1)
    41 
    40 
    42 // implicit conversion from Doubles to Complex
    41 // implicit conversion from Doubles to Complex
    43 given Conversion[Double, Complex] = Complex(_, 0)
    42 given Conversion[Double, Complex] = Complex(_, 0)
    44 
    43 
    45 
       
    46 // some customn colours for the "sliding effect"
    44 // some customn colours for the "sliding effect"
    47 val colours = List(
    45 val colours = List(
    48   new Color(66, 30, 15),    new Color(25, 7, 26),
    46   Color(66, 30, 15),    Color(25, 7, 26),
    49   new Color(9, 1, 47),      new Color(4, 4, 73),
    47   Color(9, 1, 47),      Color(4, 4, 73),
    50   new Color(0, 7, 100),     new Color(12, 44, 138),
    48   Color(0, 7, 100),     Color(12, 44, 138),
    51   new Color(24, 82, 177),   new Color(57, 125, 209),
    49   Color(24, 82, 177),   Color(57, 125, 209),
    52   new Color(134, 181, 229), new Color(211, 236, 248),
    50   Color(134, 181, 229), Color(211, 236, 248),
    53   new Color(241, 233, 191), new Color(248, 201, 95),
    51   Color(241, 233, 191), Color(248, 201, 95),
    54   new Color(255, 170, 0),   new Color(204, 128, 0),
    52   Color(255, 170, 0),   Color(204, 128, 0),
    55   new Color(153, 87, 0),    new Color(106, 52, 3))
    53   Color(153, 87, 0),    Color(106, 52, 3))
    56 
    54 
    57 // the viewer panel with an image canvas
    55 // the viewer panel with an image canvas
    58 class Viewer(width: Int, height: Int) extends JPanel {
    56 class Viewer(width: Int, height: Int) extends JPanel {
    59   val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
    57   val canvas = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
    60   
    58 
    61   override def paintComponent(g: Graphics) = 
    59   override def paintComponent(g: Graphics) =
    62     g.asInstanceOf[Graphics2D].drawImage(canvas, null, null)
    60     g.asInstanceOf[Graphics2D].drawImage(canvas, null, null)
    63   
    61 
    64   override def getPreferredSize() = 
    62   override def getPreferredSize() =
    65     new Dimension(width, height)
    63     Dimension(width, height)
    66 
    64 
    67   def clearCanvas(color: Color) = {
    65   def clearCanvas(color: Color) = {
    68     for (x <- 0 to width - 1; y <- 0 to height - 1) 
    66     for (x <- 0 to width - 1; y <- 0 to height - 1)
    69       canvas.setRGB(x, y, color.getRGB())
    67       canvas.setRGB(x, y, color.getRGB())
    70     repaint()
    68     repaint()
    71   }  
    69   }
    72 }
    70 }
    73 
    71 
    74 // initialising the viewer panel
    72 // initialising the viewer panel
    75 def openViewer(width: Int, height: Int) : Viewer = {
    73 def openViewer(width: Int, height: Int) : Viewer = {
    76   val frame = new JFrame("XYPlane")
    74   val frame = JFrame("XYPlane")
    77   val viewer = new Viewer(width, height)
    75   val viewer = Viewer(width, height)
    78   frame.add(viewer)
    76   frame.add(viewer)
    79   frame.pack()
    77   frame.pack()
    80   frame.setVisible(true)
    78   frame.setVisible(true)
    81   frame.setResizable(false)
    79   frame.setResizable(false)
    82   frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
    80   frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
    88 val H = 800   // height
    86 val H = 800   // height
    89 val black = Color.black
    87 val black = Color.black
    90 val viewer = openViewer(W, H)
    88 val viewer = openViewer(W, H)
    91 
    89 
    92 // draw a pixel on the canvas
    90 // draw a pixel on the canvas
    93 def pixel(x: Int, y: Int, color: Color) = 
    91 def pixel(x: Int, y: Int, color: Color) =
    94   viewer.canvas.setRGB(x, y, color.getRGB())
    92   viewer.canvas.setRGB(x, y, color.getRGB())
    95 
    93 
    96 
    94 
    97 // calculates the number of iterations using lazy lists 
    95 // calculates the number of iterations using lazy lists (streams)
    98 // (streams)
       
    99 //   the iteration goes on for a maximum of max steps,
    96 //   the iteration goes on for a maximum of max steps,
   100 //   but might leave early when the pred is satisfied
    97 //   but might leave early when the pred is satisfied
   101 def iterations(c: Complex, max: Int) : Int = {
    98 def iterations(c: Complex, max: Int) : Int = {
   102   def next(z: Complex) = z * z + c    
    99   def next(z: Complex) = z * z + c
   103   def pred(z: Complex) = z.abs() < 2    // exit condition
   100   def pred(z: Complex) = z.abs() < 2    // exit condition
   104   LazyList.iterate(0.0 * i, max)(next).takeWhile(pred).size
   101   LazyList.iterate(0.0 * i, max)(next).takeWhile(pred).size
   105 }
   102 }
   106 
   103 
   107 // main function 
   104 // main function
   108 //    start and end are the upper-left and lower-right corners, 
   105 //    start and end are the upper-left and lower-right corners,
   109 //    max is the number of maximum iterations
   106 //    max is the number of maximum iterations
   110 def mandelbrot(start: Complex, end: Complex, max: Int) : Unit = {
   107 def mandelbrot(start: Complex, end: Complex, max: Int) : Unit = {
   111   viewer.clearCanvas(black)
   108   viewer.clearCanvas(black)
   112   
   109 
   113   // deltas for each grid step 
   110   // deltas for each grid step
   114   val d_x = (end.re - start.re) / W
   111   val d_x = (end.re - start.re) / W
   115   val d_y = (end.im - start.im) / H
   112   val d_y = (end.im - start.im) / H
   116    
   113 
   117   for (y <- (0 until H).par) {
   114   for (y <- (0 until H).par) {
   118     for (x <- (0 until W).par) {
   115     for (x <- (0 until W).par) {
   119     
   116 
   120      val c = start + 
   117      val c = start + x * d_x + y * d_y * i
   121       (x * d_x + y * d_y * i)
   118      val iters = iterations(c, max)
   122      val iters = iterations(c, max) 
       
   123      val colour = 
   119      val colour = 
   124        if (iters == max) black 
   120         if (iters == max) black
   125        else colours(iters % 16)
   121         else colours(iters % 16)
   126 
   122 
   127      pixel(x, y, colour)
   123      pixel(x, y, colour)
   128     }
   124     }
   129     viewer.updateUI()
   125     viewer.updateUI()
   130   }   
   126   }
   131 }
   127 }
   132 
   128 
   133 
   129 
   134 // Examples
   130 // Examples
   135 //==========
   131 //==========
   167 // some more computations with example 3
   163 // some more computations with example 3
   168 
   164 
   169 val delta = (exc2 - exc1) * 0.0333
   165 val delta = (exc2 - exc1) * 0.0333
   170 
   166 
   171 println(s"${time_needed(
   167 println(s"${time_needed(
   172   for (n <- (0 to 12)) 
   168   for (n <- (0 to 25))
   173      mandelbrot(exc1 + delta * n, 
   169      mandelbrot(exc1 + delta * n,
   174                 exc2 - delta * n, 100))} secs") 
   170                 exc2 - delta * n, 1000))} secs")
   175 
   171 
   176 
   172 
   177 
   173 
   178 // Larry Paulson's example
   174 // Larry Paulson's example
   179 val exl1 = -0.74364990 + 0.13188170 * i
   175 val exl1 = -0.74364990 + 0.13188170 * i