Learning never exhausts the mind
Home >  Coding > Legacy Code > Fractals: Infinity Made Visible

Published 8th May 2003 by

Fractals are like alien worlds, strange yet familiar. You can zoom into a fractal endlessly, producing fascinating variations of colour, texture and shape.

Most mathematical formulae when plotted produce wave-like lines, curves, arcs and other simple shapes. Fractals by contrast produce extremely complex lacy colourful patterns. You never really see the same thing twice. They are often natural looking, imitating patterns produced in nature such as ferns or coastlines. Despite this, they are extremely simple to program, as we'll see here.

What is a fractal?

A fractal can be described as "the adventures of a small number in a complex plane." A fractal is a peculiar and very dense graph generated by a mathematical process. Although the graph are infinitely complex, the underlying algorithms are short and relatively simple.

When you want to see the relationship between numbers, to see mathematical expressions, you can put them into a chart or plot. A simple plot is shown below, distance against elevation.

A simple elevation plot showing distance in the x-axis and elevation in the y-axis

A simple elevation plot showing distance in the x-axis and elevation in the y-axis

By going along the line you can see the altitude of various points change with distance.

Not all numbers used my mathematicians are those we are familiar with. For example to translate waveforms into audio, or to analyse radio signals it is necessary to use "imaginary" numbers.

Complex and Imaginary Numbers

In the 16th century, mathematicians began trying to describe the square root of minus one. When you multiply a number by itself, you square it; 4 * 4 is 16. You can describe this relationship in the opposite way, 4 is the square root of 16. It seems obvious that there is a square root for any number. Squares and roots have been used for thousands of years, but attention was focused on -1 which although paradoxical, eventually proved quite useful.

What is the square root of -1? When you square a positive number, you always get a positive number. When you multiply a negative number by a negative number, you also get a positive number. So, the number you need to multiply by itself to get -1 cannot be positive, nor can it be negative. In other words it is not a real number, it is imaginary.

When you combine imaginary numbers with real nu,bers, you get points on a grid. Real numbers describe the horizontal axis, while imaginary numbers describe the vertical distance. This creates a complex number, and they are an important concept, used in almost every field of mathematics and applied science, from voice processing, image compression, radar analysis, even earthquake monitoring.

A fractal is built pixel by pixel via the repeated transformation of a complex number. Several things can happen when you transform a complex number by multiplying it by itself.

  1. If it is a real number larger than 1, it will grow towards an infinitely large number.
  2. If it is less than one (a fraction) it will shrink towards an infinitely small number close to zero, but never reaching zero.
  3. If it is exactly one, it will remain one. 1 * 1 is 1 and always will be.

The Mandelbrot Set

All numbers that are negative are described as a "set" of negative numbers. The Mandelbrot set is a group of numbers which when plugged into the Mandelbrot equation remain bounded, that is they don't escape to infinity. The numbers which move towards zero, or those which oscillate, when painted in, create the classic Mandelbrot shape.

z = z^2 + c

We want to display these fractals on the screen. We can think of the complex plane as a grid, with real numbers shown horizontally and imaginary numbers shown vertically. To calculate the Mandelbrot set, we'll first choose a zone within the grid to paint in. For each point in this area we assign the corrsponding complex number to c and starting with z = 0, we repeat the transformation against this value enough times to discover whether or not this particular position will escape to infinity. it if will, we colour it white to show it is outside of the Mandelbrot set. If it won't escape we colour it black to show that it is part of the set. The number of times we run this test is the number of iterations.

The result is an intricate shape known as the Mandelbrot set. The irregular line between the black and the white areas is the boundary between the two attractors (infinity and zero). As you can see, it is by no means as simple as a circle.

Classic Black and White Mandelbrot Set

Classic Black and White Mandelbrot Set

Programming the Mandelbrot Set

Let's first start with the black and white Mandelbrot set. The only parameters to define are:

  1. The area of the complex plane (xMin, xMax, yMin, yMax)
  2. The resolution of the image (nx, ny)
  3. The maximum iterations required to determine if the value escapes or is bounded (maxIter)

Thirty two iterations is a good starting point. Larger values produce more accurate results, however require more calculation time.

I know this is in VisualBasic, it's the language I was using at the time. Later examples use Delphi and Pascal.

Sub GenerateMAndelbrot ()
  BLACK = RGB(0,0,0): WHITE = RGB(255,255,255)
  Dim X As Double, Y As Double
  Dim ix As Integer, iy As Integer 
  Dim MaxIter As Integer
  ' Define picture resolution
  nx = 256
  ny = 256

  ' Select area of the complex plane to map
  xmin = -1.5
  xmax = .5
  ymin = -1
  ymax = 1
  ' Maximum number of iterations
  MaxIter = 32

  ' For each point in the selected area
  For iy = 0 To ny - 1
    Y = (ymin + iy * (ymax - ymin) / (ny - 1))

    For ix = 0 To nx - 1
      X = (xmin + ix * (xmax - xmin) / (nx - 1))
      ' Now check if it escapes to infinity
      M - Mandelbrot(X, Y, MaxIter)
      If M = MaxIter Then
        Picture1.PSet(ix,iy), BLACK
        Picture1.PSet(ix,iy), WHITE
      End If
End Sub

Function Mandelbrot (ByVal cx As Double, ByVal cy As Double, ByVal MaxIter As Integer) As Integer
  Dim x2 As Double, y2 As Double
  Dim temp As Double
  Dim X As Double, Y As Double

  X = 0
  Y = 0
  x2 = 0
  y2 = 0
  iter = 0

  While ((iter < MaxIter) And (Abs(x2) + Abs(y2) < 100000))
    temp = x2 - y2 + cx
    Y = 2 * X * Y + cy
    X = temp
    x2 = X * X
    y2 = Y * Y
    iter = iter + 1
  Mandelbrot = iter
End Function

The first function essentially goes through each pixel in the complex plain and runs the Mandelbrot calculation. If the value returned from this function does not escape to infinity it assigns that pixel the black colour, while if it does escape to infinity it is assigned the WHITE colour.

The actual calculation is done by the Mandelbrot function. The loop tests to see if the calculated value from the Mandelbrot equation escapes to infinity before the maximum iterations is reached. If it is, it returns the number of iterations, if it doesn't then it returns the max iterations. For this example, the value of 100000 is good enough to represent infinity.

Colouring Fractals

Black and White fractals are all well and good, but coloured fractals are often breathtaking. Where does the colour come from?

Different colours represent different escape velocities. That is, the calculation not only tests to see if a point escapes to infinity, it also tests how fast it escapes. These mysterious variations of escape velocity give fractals their beauty and complexity.

These code examples show code written in Borland Delphi. The fully working example can be downloaded at the end of the article.

procedure TfrmMain.DrawMandel;
   x,y: real;
   ix,iy,nx,ny,ipx,px: integer;
   aColor: TColor;
     nx := frmMain.FractalImage.Width;
     ny := frmMain.FractalImage.Height;
     px := 0;
       nx := frmMain.FractalImage.Width;
       ny := frmMain.FractalImage.Height;
       for iy := 0 to ny-1 do
            if STOPNOW = TRUE then break;
            Y := (ymin +iy * (ymax -ymin) / (ny - 1));
            for ix := 0 to nx-1 do
                   X := (xmin + ix * (xmax - xmin) / (nx - 1));
                   aColor := CalcColorMandel(X,Y,maxiter);
                   frmMain.FractalImage.Canvas.Pixels[ix,iy] := aColor;
                   frmMain.Progress.Progress := trunc(((iy/frmMain.FractalImage.Height)*100)+1);
                   frmMain.ProgressShow.Progress := frmMain.Progress.Progress;
     btnStop.Visible := FALSE;
     btnDraw.Visible := TRUE;
     px := 64;

function TfrmMain.CalcColorMandel(cx,cy: double; maxiter: integer): TColor;
   x2,y2,temp,x,y : double;
   iter: integer;
     x := 0;
     y := 0;
     x2 := 0;
     y2 := 0;
     iter := 0;
     while ((iter < maxiter) and ((abs(x2) + abs(y2)) < 100000)) do
          temp := x2 - y2 + cx;
          y := (2 * x * y) + cy;
          x := temp;
          x2 := x * x;
          y2 := y * y;
     if PaletteType = 2 then result := trunc((iter+80)*colorFactor);
     if PaletteType = 1 then
          if ((iter > 60) AND (iter < maxiter)) then result := 255 * 255 * 255
          else if ((iter < 56) AND (iter > 53)) then result := 240 * 255 * 255
          else if ((iter < 52) AND (iter > 47)) then result := 244 * 255 * 255
          else if ((iter < 48) AND (iter > 45)) then result := 192 * 255 * 255
          else if ((iter < 44) AND (iter > 41)) then result := 176 * 255 * 255
          else if ((iter < 40) AND (iter > 37)) then result := 160 * 255 * 255
          else if ((iter < 36) AND (iter > 33)) then result := 144 * 255 * 255
          else if ((iter < 32) AND (iter > 29)) then result := 128 * 255 * 255
          else if ((iter < 28) AND (iter > 25)) then result := 112 * 255 * 255
          else if ((iter < 24) AND (iter > 21)) then result := 96 * 255 * 255
          else if ((iter < 20) AND (iter > 17)) then result := 80 * 255 * 255
          else if ((iter < 16) AND (iter > 13)) then result := 64 * 255 * 255
          else if ((iter < 12) AND (iter > 9)) then result  := 32 * 255 * 255
          else if ((iter < &nbsp;<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACf0lEQVQ4y4WRX0hTcRTHDxVBSyjBFHvpDyxtFKkz/AsNJmU2JtKs1IdAqLAifJB6qZcoEiMKUnJKiUo4CqIFEtXuNLWkRRGGkIJZjmU5sSUW3UvtdL6/66SHoAtfvvec8/l+X3401UUU7iaa7iH67CPLzB1yi7yigCi06JjduIMDjxwzmz+LBVaRby6YO6Z/OB/7Pdtp8Py9OBwz9riD+1eBNeJbpX0fPRblWS/zjCjayTzbbTpm2eMODvxSwcRNskx2kG9hpCb66WkVP2ncrBQZOsHx6RvKEzvcwYFHThWMt5E78iBzPP6+jmMvPBx+XM7hgIe/varj+OQ55Zixxx0ceORUwWgLea80ZMbstrXc2nyJXS4XW1I2cFZmMl+u36b8YtNVtc/KWMPgwCOnCt5co4D+tsoozEnjQxXF7HQ6OSO7iPN3pPGU33Sny6P2jryNDA48cqog1EShXyOVce8ZG+8vsy8ql7Xrdp57VKgcc+LWenorg0dOFQxdoMCP4TLj68Od/PFutihHZOcFrZiNQYdyzOY+m8GBR04VBM6S90tvQUzX8lnvK2Kj38HGQImEd4v2mI5Z9riDA4+cKrhfT+6hxtRxvW8X60GHeq55bR8bwwdEB01/VsH6wF51BwceOVVw+whZumrJ9+7W9qjeX8ozvaVL755Q2F8qJZWMOzjwyKmC9hqitmqydhxeoY2226P68xo2Xh5l4/VJ0SnTZcYed3DgkVMF+LakEtU7yNpSST5/Q/rYRE9JLBasNn4O18bhmLHHHRx4fImC5SKsbClJVODJouNN5dTV7CFNAiE4ZuzlnifcJlHS3wXLRKtF60Tr/6N0UbJoZaLgD4alKxSgG6GYAAAAAElFTkSuQmCC" alt="8)" title="8)" width="16" height="16" style="top:3px;position:relative" />&nbsp; AND (iter > 5)) then result   := 16 * 255 * 255
          else if (iter < 4) then result  :=     0
          else result := 0;

What we see here is the colour method testing how many iterations were needed before the value escapes to infinity. Based on this it assigns a colour value (R*G*B)

You can download the code from GitHub

Download from GitHub

Leave a Reply

Fields marked with * are mandatory.

We respect your privacy, and will not make your email public. Hashed email address may be checked against Gravatar service to retrieve avatars. This site uses Akismet to reduce spam. Learn how your comment data is processed.