| 
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) { 
 | 
| 
143
 | 
    12  | 
    // represents the complex number a + b * i
  | 
| 
124
 | 
    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  | 
   
  | 
| 
167
 | 
    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))
  | 
| 
143
 | 
    89  | 
    }
  | 
| 
 | 
    90  | 
    viewer.updateUI()
  | 
| 
 | 
    91  | 
  }
  | 
| 
124
 | 
    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  | 
  | 
| 
174
 | 
   116  | 
time_needed(mandelbrot(exb1, exb2, 1000))
  | 
| 
124
 | 
   117  | 
  | 
| 
 | 
   118  | 
// example 3
  | 
| 
 | 
   119  | 
val exc1 = Complex(0.435396403, 0.367981352)
  | 
| 
 | 
   120  | 
val exc2 = Complex(0.451687191, 0.380210061)
  | 
| 
 | 
   121  | 
  | 
| 
166
 | 
   122  | 
//time_needed(mandelbrot(exc1, exc2, 1000))
  | 
| 
124
 | 
   123  | 
  | 
| 
 | 
   124  | 
// some more computations with example 3
  | 
| 
 | 
   125  | 
val delta = (exc2 - exc1) * 0.0333
  | 
| 
 | 
   126  | 
  | 
| 
167
 | 
   127  | 
time_needed(
  | 
| 
 | 
   128  | 
  for (i <- (0 to 12)) 
  | 
| 
 | 
   129  | 
     mandelbrot(exc1 + delta * i, 
  | 
| 
174
 | 
   130  | 
                exc2 - delta * i, 1000))
  | 
| 
 | 
   131  | 
  | 
| 
 | 
   132  | 
val exc1 = Complex(0.435396403, 0.367981352)
  | 
| 
167
 | 
   133  | 
val exc2 = Complex(0.451687191, 0.380210061)
  | 
| 
 | 
   134  | 
  | 
| 
 | 
   135  | 
//time_needed(mandelbrot(exc1, exc2, 1000))
  | 
| 
 | 
   136  | 
  | 
| 
 | 
   137  | 
// some more computations with example 3
  | 
| 
 | 
   138  | 
val delta = (exc2 - exc1) * 0.0333
  | 
| 
 | 
   139  | 
  | 
| 
 | 
   140  | 
time_needed(
  | 
| 
 | 
   141  | 
  for (i <- (0 to 12)) 
  | 
| 
 | 
   142  | 
     mandelbrot(exc1 + delta * i, 
  | 
| 
 | 
   143  | 
                exc2 - delta * i, 1000))
  | 
| 
124
 | 
   144  | 
  | 
| 
 | 
   145  | 
  | 
| 
 | 
   146  | 
  |