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