progs/mandelbrot.sc
changeset 484 c4561fc667b7
child 488 5deaf53c2faa
equal deleted inserted replaced
483:1a51207780e6 484:c4561fc667b7
       
     1 // Mandelbrot pictures
       
     2 //=====================
       
     3 //
       
     4 //   see https://en.wikipedia.org/wiki/Mandelbrot_set
       
     5 // 
       
     6 // needs to be called with
       
     7 // 
       
     8 // scala -cp scala-parallel-collections_3-1.0.4.jar
       
     9 //
       
    10 // !! UPDATE: On my faster Mac-M1 machine the times
       
    11 // !! are ca. 4 secs for the sequential version and
       
    12 // !! around 0.7 secs for the par-version.
       
    13 
       
    14 
       
    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
       
    23 import scala.language.implicitConversions    
       
    24 import scala.collection.parallel.CollectionConverters._
       
    25 
       
    26 // complex numbers
       
    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)
       
    35 }
       
    36 
       
    37 // to allow the notation n + m * i
       
    38 object i extends Complex(0, 1)
       
    39 
       
    40 // implicit conversion from Doubles to Complex
       
    41 implicit def d2c(d: Double) : Complex = Complex(d, 0)
       
    42 
       
    43 
       
    44 // some customn colours for the "sliding effect"
       
    45 val colours = List(
       
    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))
       
    54 
       
    55 // the viewer panel with an image canvas
       
    56 class Viewer(width: Int, height: Int) extends JPanel {
       
    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)
       
    64 
       
    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   }  
       
    70 }
       
    71 
       
    72 // initialising the viewer panel
       
    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
       
    82 }
       
    83 
       
    84 // some hardcoded parameters
       
    85 val W = 900   // width
       
    86 val H = 800   // height
       
    87 val black = Color.black
       
    88 val viewer = openViewer(W, H)
       
    89 
       
    90 // draw a pixel on the canvas
       
    91 def pixel(x: Int, y: Int, color: Color) = 
       
    92   viewer.canvas.setRGB(x, y, color.getRGB())
       
    93 
       
    94 
       
    95 // calculates the number of iterations using lazy lists (streams)
       
    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    
       
   100   def pred(z: Complex) = z.abs() < 2    // exit condition
       
   101   LazyList.iterate(0.0 * i, max)(next).takeWhile(pred).size
       
   102 }
       
   103 
       
   104 // main function 
       
   105 //    start and end are the upper-left and lower-right corners, 
       
   106 //    max is the number of maximum iterations
       
   107 def mandelbrot(start: Complex, end: Complex, max: Int) : Unit = {
       
   108   viewer.clearCanvas(black)
       
   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
       
   113    
       
   114   for (y <- (0 until H).par) {
       
   115     for (x <- (0 until W).par) {
       
   116     
       
   117      val c = start + 
       
   118       (x * d_x + y * d_y * i)
       
   119      val iters = iterations(c, max) 
       
   120      val colour = 
       
   121        if (iters == max) black 
       
   122        else colours(iters % 16)
       
   123 
       
   124      pixel(x, y, colour)
       
   125     }
       
   126     viewer.updateUI()
       
   127   }   
       
   128 }
       
   129 
       
   130 
       
   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 
       
   143 
       
   144 // example 1
       
   145 val exa1 = -2.0 + -1.5 * i
       
   146 val exa2 =  1.0 +  1.5 * i
       
   147 
       
   148 println(s"${time_needed(mandelbrot(exa1, exa2, 1000))} secs")
       
   149 
       
   150 // example 2
       
   151 val exb1 = -0.37465401 + 0.659227668 * i
       
   152 val exb2 = -0.37332410 + 0.66020767 * i
       
   153 
       
   154 //time_needed(mandelbrot(exb1, exb2, 1000))
       
   155 
       
   156 // example 3
       
   157 val exc1 = 0.435396403 + 0.367981352 * i
       
   158 val exc2 = 0.451687191 + 0.380210061 * i
       
   159 
       
   160 //time_needed(mandelbrot(exc1, exc2, 1000))
       
   161 
       
   162 
       
   163 
       
   164 // some more computations with example 3
       
   165 
       
   166 val delta = (exc2 - exc1) * 0.0333
       
   167 
       
   168 println(s"${time_needed(
       
   169   for (n <- (0 to 25)) 
       
   170      mandelbrot(exc1 + delta * n, 
       
   171                 exc2 - delta * n, 1000))} secs") 
       
   172 
       
   173 
       
   174 
       
   175 // Larry Paulson's example
       
   176 val exl1 = -0.74364990 + 0.13188170 * i
       
   177 val exl2 = -0.74291189 + 0.13261971 * i
       
   178 
       
   179 //println(s"${time_needed(mandelbrot(exl1, exl2, 1000))} secs")
       
   180 
       
   181 
       
   182 // example by Jorgen Villadsen
       
   183 val exj1 = 0.10284 - 0.63275 * i
       
   184 val exj2 = 0.11084 - 0.64075 * i
       
   185 
       
   186 //time_needed(mandelbrot(exj1, exj2, 1000))
       
   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))