exupero's blog
RSSApps

Drawing the Mandelbrot set

For all its infinite complexity, I was surprised to learn how simple it is to generate the Mandelbrot set. All it requires is complex addition, multiplication, distance, and some iteration. Here's a worked example.

We'll represent complex numbers as two-element vectors, where the first element is the real part and the second is the imaginary part. Complex addition is simply componentwise addition:

(defn complex-add [a b]
  (mapv + a b))

Complex multiplication is more elaborate, but not difficult:

(defn complex-multiply [[r1 i1] [r2 i2]]
  [(- (* r1 r2) (* i1 i2))
   (+ (* r1 i2) (* r2 i1))])

The distance from a point in the complex plane to the origin is regular Euclidean distance:

(defn distance-to-origin [[r i]]
  (Math/sqrt (+ (* r r) (* i i))))

Now for the hard work. The mathematical definition of the Mandelbrot set is f(z) = z2 + c, but that omits a lot of the actual algorithm. To create the traditional image of the Mandelbrot set, we need to call that function over and over and see how quickly the result diverges from its starting point:

(defn mandelbrot [c]
  (loop [i 0
         z [0 0]]
    (if (and (< i 20)
             (< (distance-to-origin z) 2))
      (recur (inc i) (complex-add c (complex-multiply z z)))
      i)))

The above function loops up to 20 times, or quits as soon as the point moves more than two units from its starting point. To draw the set, we'll color points in an image based on how quickly they diverged past a two-unit radius. Here's a list of hexadecimal RGB colors that walk up through blues, then add increasing amounts of green, then red:

(def color
  [nil
   0x00002a
   0x000055
   0x00007f
   0x0000aa
   0x0000d4
   0x0000ff
   0x0025ff
   0x0049ff
   0x006eff
   0x0092ff
   0x00b7ff
   0x00dbff
   0x00ffff
   0x25ffff
   0x49ffff
   0x6effff
   0x92ffff
   0xb7ffff
   0xdbffff
   0x000000])

Mapping each pixel in an image to the complex plane with corners -2 - i and 1 + i produces this:

mandelbrot.png

While the bright, complex edge is what usually draws our attention in images like these, the colored points above don't actually belong to the Mandelbrot set. Only those points that don't diverge to infinity when iterated through f(z) = z2 + c are part of the set; that is, the points shown in black. The Mandelbrot set is usually illustrated as negative space.