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