// Mandelbrot pictures+ −
//=====================+ −
//+ −
// see https://en.wikipedia.org/wiki/Mandelbrot_set+ −
// + −
// under scala 2.13.1 needs to be called with+ −
// + −
// scala -cp scala-parallel-collections_2.13-0.2.0.jar mandelbrot.scala+ −
//+ −
// scala-cli run --jar scala-parallel-collections_3-1.0.4.jar mandelbrot3.sc+ −
+ −
+ −
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+ −
import scala.language.implicitConversions + −
import scala.collection.parallel.CollectionConverters._+ −
+ −
object Test {+ −
+ −
def main() = {+ −
+ −
// complex numbers+ −
case class Complex(val re: Double, val im: Double) { + −
// represents the complex number re + im * i+ −
def +(that: Complex) = Complex(this.re + that.re, this.im + that.im)+ −
def -(that: Complex) = Complex(this.re - that.re, this.im - that.im)+ −
def *(that: Complex) = Complex(this.re * that.re - this.im * that.im,+ −
this.re * that.im + that.re * this.im)+ −
def *(that: Double) = Complex(this.re * that, this.im * that)+ −
def abs() = Math.sqrt(this.re * this.re + this.im * this.im)+ −
}+ −
+ −
// to allow the notation n + m * i+ −
object i extends Complex(0, 1)+ −
+ −
given Conversion[Double, Complex] = Complex(_, 0)+ −
+ −
+ −
// some customn colours for the "sliding effect"+ −
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 with an image canvas+ −
class Viewer(width: Int, height: Int) extends JPanel {+ −
val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)+ −
+ −
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()+ −
} + −
}+ −
+ −
// initialising the viewer panel+ −
def openViewer(width: Int, height: Int) : Viewer = {+ −
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+ −
}+ −
+ −
// some hardcoded parameters+ −
val W = 900 // width+ −
val H = 800 // height+ −
val black = Color.black+ −
val viewer = openViewer(W, H)+ −
+ −
// draw a pixel on the canvas+ −
def pixel(x: Int, y: Int, color: Color) = + −
viewer.canvas.setRGB(x, y, color.getRGB())+ −
+ −
+ −
// calculates the number of iterations using lazy lists (streams)+ −
// the iteration goes on for a maximum of max steps,+ −
// but might leave early when the pred is satisfied+ −
def iterations(c: Complex, max: Int) : Int = {+ −
def next(z: Complex) = z * z + c + −
def pred(z: Complex) = z.abs() < 2 // exit condition+ −
LazyList.iterate(0.0 * i, max)(next).takeWhile(pred).size+ −
}+ −
+ −
// main function + −
// start and end are the upper-left and lower-right corners, + −
// max is the number of maximum iterations+ −
def mandelbrot(start: Complex, end: Complex, max: Int) : Unit = {+ −
viewer.clearCanvas(black)+ −
+ −
// deltas for each grid step + −
val d_x = (end.re - start.re) / W+ −
val d_y = (end.im - start.im) / H+ −
+ −
for (y <- (0 until H).par) {+ −
for (x <- (0 until W).par) {+ −
+ −
val c = start + + −
(x * d_x + y * d_y * i)+ −
val iters = iterations(c, max) + −
val col = + −
if (iters == max) black + −
else colours(iters % 16)+ −
+ −
pixel(x, y, col)+ −
}+ −
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 = -2.0 + -1.5 * i+ −
val exa2 = 1.0 + 1.5 * i+ −
+ −
println(s"${time_needed(mandelbrot(exa1, exa2, 1000))} secs")+ −
+ −
// example 2+ −
val exb1 = -0.37465401 + 0.659227668 * i+ −
val exb2 = -0.37332410 + 0.66020767 * i+ −
+ −
//time_needed(mandelbrot(exb1, exb2, 1000))+ −
+ −
// example 3+ −
val exc1 = 0.435396403 + 0.367981352 * i+ −
val exc2 = 0.451687191 + 0.380210061 * i+ −
+ −
//time_needed(mandelbrot(exc1, exc2, 1000))+ −
+ −
+ −
+ −
// some more computations with example 3+ −
+ −
val delta = (exc2 - exc1) * 0.0333+ −
+ −
println(s"${time_needed(+ −
for (n <- (0 to 12)) + −
mandelbrot(exc1 + delta * n, + −
exc2 - delta * n, 100))} secs") + −
+ −
+ −
+ −
// Larry Paulson's example+ −
val exl1 = -0.74364990 + 0.13188170 * i+ −
val exl2 = -0.74291189 + 0.13261971 * i+ −
+ −
//println(s"${time_needed(mandelbrot(exl1, exl2, 1000))} secs")+ −
+ −
+ −
// example by Jorgen Villadsen+ −
val exj1 = 0.10284 - 0.63275 * i+ −
val exj2 = 0.11084 - 0.64075 * i+ −
+ −
//time_needed(mandelbrot(exj1, exj2, 1000))+ −
+ −
+ −
// another example+ −
val exA = 0.3439274 + 0.6516478 * i+ −
val exB = 0.3654477 + 0.6301795 * i+ −
+ −
//time_needed(mandelbrot(exA, exB, 1000))+ −
+ −
+ −
}+ −
+ −
}+ −
+ −
Test.main()+ −