| 124 |      1 | import java.awt.Color
 | 
|  |      2 | import java.awt.Dimension
 | 
|  |      3 | import java.awt.Graphics
 | 
|  |      4 | import java.awt.Graphics2D
 | 
|  |      5 | import java.awt.image.BufferedImage
 | 
|  |      6 | import javax.swing.JFrame
 | 
|  |      7 | import javax.swing.JPanel
 | 
|  |      8 | import javax.swing.WindowConstants
 | 
|  |      9 | 
 | 
|  |     10 | // complex numbers
 | 
|  |     11 | case class Complex(val a: Double, val b: Double) { 
 | 
|  |     12 |     // represents the complex number a + b*i
 | 
|  |     13 |     def +(that: Complex) = Complex(this.a + that.a, this.b + that.b)
 | 
|  |     14 |     def -(that: Complex) = Complex(this.a - that.a, this.b - that.b)
 | 
|  |     15 |     def *(that: Complex) = Complex(this.a * that.a - this.b * that.b,
 | 
|  |     16 |                                    this.a * that.b + that.a * this.b)
 | 
|  |     17 |     def *(that: Double) = Complex(this.a * that, this.b * that)
 | 
|  |     18 |     def abs() = Math.sqrt(this.a * this.a + this.b * this.b)
 | 
|  |     19 | }
 | 
|  |     20 | 
 | 
|  |     21 | // some customn colours
 | 
|  |     22 | val colours = List(
 | 
|  |     23 |     new Color(66, 30, 15),    new Color(25, 7, 26),
 | 
|  |     24 |     new Color(9, 1, 47),      new Color(4, 4, 73),
 | 
|  |     25 |     new Color(0, 7, 100),     new Color(12, 44, 138),
 | 
|  |     26 |     new Color(24, 82, 177),   new Color(57, 125, 209),
 | 
|  |     27 |     new Color(134, 181, 229), new Color(211, 236, 248),
 | 
|  |     28 |     new Color(241, 233, 191), new Color(248, 201, 95),
 | 
|  |     29 |     new Color(255, 170, 0),   new Color(204, 128, 0),
 | 
|  |     30 |     new Color(153, 87, 0),    new Color(106, 52, 3))
 | 
|  |     31 | 
 | 
|  |     32 | // the viewer panel
 | 
|  |     33 | class Viewer(width: Int, height: Int) extends JPanel {
 | 
|  |     34 |     val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
 | 
|  |     35 |     clearCanvas(Color.black)
 | 
|  |     36 | 
 | 
|  |     37 |     override def paintComponent(g: Graphics) = 
 | 
|  |     38 |       g.asInstanceOf[Graphics2D].drawImage(canvas, null, null)
 | 
|  |     39 | 
 | 
|  |     40 |     override def getPreferredSize() = 
 | 
|  |     41 |        new Dimension(width, height)
 | 
|  |     42 | 
 | 
|  |     43 |     def clearCanvas(color: Color) = {
 | 
|  |     44 |       for(x <- 0 to width - 1;
 | 
|  |     45 |           y <- 0 to height - 1) canvas.setRGB(x, y, color.getRGB())
 | 
|  |     46 |       repaint()
 | 
|  |     47 |     }  
 | 
|  |     48 | 
 | 
|  |     49 | }
 | 
|  |     50 | 
 | 
|  |     51 | def openViewer(width: Int, height: Int) = {
 | 
|  |     52 |     val frame = new JFrame("XYPlane")
 | 
|  |     53 |     val viewer = new Viewer(width, height)
 | 
|  |     54 |     frame.add(viewer)
 | 
|  |     55 |     frame.pack()
 | 
|  |     56 |     frame.setVisible(true)
 | 
|  |     57 |     frame.setResizable(false)
 | 
|  |     58 |     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
 | 
|  |     59 |     viewer
 | 
|  |     60 | }
 | 
|  |     61 | 
 | 
|  |     62 | val W = 900
 | 
|  |     63 | val H = 800 
 | 
|  |     64 | val black = Color.black
 | 
|  |     65 | val viewer = openViewer(W, H)
 | 
|  |     66 | 
 | 
|  |     67 | 
 | 
|  |     68 | def pixel(x: Int, y: Int, color: Color) = 
 | 
|  |     69 |   viewer.canvas.setRGB(x, y, color.getRGB())
 | 
|  |     70 |   
 | 
|  |     71 | 
 | 
|  |     72 | def mandelbrot(start: Complex, end: Complex, level: Int) : Unit = {
 | 
|  |     73 |   viewer.clearCanvas(black)
 | 
|  |     74 |    
 | 
|  |     75 |   val delta_x = (end.a - start.a) / W
 | 
|  |     76 |   val delta_y = (end.b - start.b) / H
 | 
|  |     77 |    
 | 
| 137 |     78 |   for (y0 <- (0 until H)) {
 | 
|  |     79 |     for (x0 <- (0 until W)) {
 | 
| 124 |     80 |     
 | 
|  |     81 |      val c = start + Complex(x0 * delta_x, y0 * delta_y)
 | 
|  |     82 | 
 | 
|  |     83 |      def iters(z: Complex, it: Int) : Color = {
 | 
|  |     84 |        if (it < level && z.abs < 2) iters(z * z + c, it + 1) else 
 | 
|  |     85 |         if (it == level) black else colours(it % 16) 
 | 
|  |     86 |      }
 | 
|  |     87 | 
 | 
|  |     88 |      pixel(x0, y0, iters(Complex(0, 0), 0))
 | 
|  |     89 |    }
 | 
|  |     90 |    viewer.updateUI()
 | 
|  |     91 |  }
 | 
|  |     92 | }
 | 
|  |     93 | 
 | 
|  |     94 | // Examples
 | 
|  |     95 | //==========
 | 
|  |     96 | 
 | 
|  |     97 | //for measuring time
 | 
|  |     98 | def time_needed[T](code: => T) = {
 | 
|  |     99 |   val start = System.nanoTime()
 | 
|  |    100 |   code
 | 
|  |    101 |   val end = System.nanoTime()
 | 
|  |    102 |   (end - start) / 1.0e9
 | 
|  |    103 | }
 | 
|  |    104 | 
 | 
|  |    105 | 
 | 
|  |    106 | // example 1
 | 
|  |    107 | val exa1 = Complex(-2.0, -1.5)
 | 
|  |    108 | val exa2 = Complex( 1.0,  1.5)
 | 
|  |    109 | 
 | 
|  |    110 | time_needed(mandelbrot(exa1, exa2, 1000))
 | 
|  |    111 | 
 | 
| 136 |    112 | // example 2
 | 
|  |    113 | val exb1 = Complex(-0.37465401, 0.659227668)
 | 
| 124 |    114 | val exb2 = Complex(-0.37332410, 0.66020767)
 | 
|  |    115 | 
 | 
|  |    116 | time_needed(mandelbrot(exb1, exb2, 1000))
 | 
|  |    117 | 
 | 
|  |    118 | // example 3
 | 
|  |    119 | val exc1 = Complex(0.435396403, 0.367981352)
 | 
|  |    120 | val exc2 = Complex(0.451687191, 0.380210061)
 | 
|  |    121 | 
 | 
|  |    122 | time_needed(mandelbrot(exc1, exc2, 1000))
 | 
|  |    123 | 
 | 
|  |    124 | // some more computations with example 3
 | 
|  |    125 | val delta = (exc2 - exc1) * 0.0333
 | 
|  |    126 | 
 | 
|  |    127 | time_needed(
 | 
| 136 |    128 |   for (i <- (0 to 12)) 
 | 
|  |    129 |      mandelbrot(exc1 + delta * i, 
 | 
|  |    130 |                 exc2 - delta * i, 1000))
 | 
| 124 |    131 | 
 | 
|  |    132 | 
 | 
|  |    133 | 
 |