import java.awt.Color
import java.awt.Dimension
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.WindowConstants
// complex numbers
case class Complex(val a: Double, val b: Double) {
// represents the complex number a + b*i
def +(that: Complex) = Complex(this.a + that.a, this.b + that.b)
def -(that: Complex) = Complex(this.a - that.a, this.b - that.b)
def *(that: Complex) = Complex(this.a * that.a - this.b * that.b,
this.a * that.b + that.a * this.b)
def *(that: Double) = Complex(this.a * that, this.b * that)
def abs() = Math.sqrt(this.a * this.a + this.b * this.b)
}
// some customn colours
val colours = List(
new Color(66, 30, 15), new Color(25, 7, 26),
new Color(9, 1, 47), new Color(4, 4, 73),
new Color(0, 7, 100), new Color(12, 44, 138),
new Color(24, 82, 177), new Color(57, 125, 209),
new Color(134, 181, 229), new Color(211, 236, 248),
new Color(241, 233, 191), new Color(248, 201, 95),
new Color(255, 170, 0), new Color(204, 128, 0),
new Color(153, 87, 0), new Color(106, 52, 3))
// the viewer panel
class Viewer(width: Int, height: Int) extends JPanel {
val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
clearCanvas(Color.black)
override def paintComponent(g: Graphics) =
g.asInstanceOf[Graphics2D].drawImage(canvas, null, null)
override def getPreferredSize() =
new Dimension(width, height)
def clearCanvas(color: Color) = {
for(x <- 0 to width - 1;
y <- 0 to height - 1) canvas.setRGB(x, y, color.getRGB())
repaint()
}
}
def openViewer(width: Int, height: Int) = {
val frame = new JFrame("XYPlane")
val viewer = new Viewer(width, height)
frame.add(viewer)
frame.pack()
frame.setVisible(true)
frame.setResizable(false)
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
viewer
}
val W = 900
val H = 800
val black = Color.black
val viewer = openViewer(W, H)
def pixel(x: Int, y: Int, color: Color) =
viewer.canvas.setRGB(x, y, color.getRGB())
def mandelbrot(start: Complex, end: Complex, level: Int) : Unit = {
viewer.clearCanvas(black)
val delta_x = (end.a - start.a) / W
val delta_y = (end.b - start.b) / H
for (y0 <- (0 until H).par) {
for (x0 <- (0 until W).par) {
val c = start + Complex(x0 * delta_x, y0 * delta_y)
def iters(z: Complex, it: Int) : Color = {
if (it < level && z.abs < 2) iters(z * z + c, it + 1) else
if (it == level) black else colours(it % 16)
}
pixel(x0, y0, iters(Complex(0, 0), 0))
}
viewer.updateUI()
}
}
// Examples
//==========
//for measuring time
def time_needed[T](code: => T) = {
val start = System.nanoTime()
code
val end = System.nanoTime()
(end - start) / 1.0e9
}
// example 1
val exa1 = Complex(-2.0, -1.5)
val exa2 = Complex( 1.0, 1.5)
time_needed(mandelbrot(exa1, exa2, 1000))
// example 2
val exb1 = Complex(-0.37465401, 0.659227668)
val exb2 = Complex(-0.37332410, 0.66020767)
time_needed(mandelbrot(exb1, exb2, 1000))
// example 3
val exc1 = Complex(0.435396403, 0.367981352)
val exc2 = Complex(0.451687191, 0.380210061)
time_needed(mandelbrot(exc1, exc2, 1000))
// some more computations with example 3
val delta = (exc2 - exc1) * 0.0333
time_needed(
for (i <- (0 to 12))
mandelbrot(exc1 + delta * i,
exc2 - delta * i, 1000))