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