Detailed Explanation of the Winmand Fractal Generator



This page explains how the Winmand program generates the mandelbrot set, ie a fractal.

The basic idea is that we want to place a dot of colour at every spot on the screen. The colour of the dot should indicate the behaviour of the equation at that point.

A fractal is a pictorial representation of the behaviour of a mathematical equation. How can this be done? Essentially, each position on the screen is assigned a number, which is then fed into the equation. The resulting number is then used to select the colour of the dot that is placed at that position. These coloured dots make up the image.

Each position on the screen is described using the notation (x, y), where x is the horizontal distance from a given position to the centre of the screen and y is the vertical distance. The co-ordinate
(x, y) is fed into the Mandelbrot equation and the results are calculated. The resulting value is also in the form (a, b). Instead of treating this as a point, we convert it to distance from the origin. The image created, ie the fractal, is a pictorial representation of how quickly - if at all - each point "escapes" ie the calculated value (a, b) is very far from the origin.

The Mandelbrot formula consists of multiplying a number by itself, adding the results to the original number and repeating the process about 200 times. If we were to do this with ordinary numbers such as 0.5 or 5 then the results would be easily predictable. Numbers below 0.5 would produce output of 1.4 or less. Numbers greater than 2 would produce enormous numbers (greater than 10 to the 60 th power). This behaviour is not very interesting. However, because the screen co-ordinates come as pairs of numbers, ie (x, y), we need to find a way of multiplying such pairs together. The way to do this is to use what are called complex numbers. It turns out that the behaviour of complex numbers is much more interesting.

Note that the asterisk (*) denotes multiplication.

Each point on the screen represents a complex number x + i*y (where i*i = -1). Thus grid point (-1.2, 1.4) is taken to be -1.2 + 1.4*i. In other words, the x axis is the real axis and the y axis is the imaginary axis.

Multiplication of complex numbers works as follows:
If z = x + i*y then z*z = (x + i*y) * (x + i*y) and on expansion
(x + i*y) * (x + i*y) = x2 - y2 + 2*x*y*i.

In other words (x, y) * (x, y) = (x2 - y2, 2*x*y).

Substituting x = 2, y = 3 in the formula above gives us:
(2, 3) * (2, 3) = (4 - 9, 2*6) = (-5, 12).

The range of values covered by this program is for x = -2 to +1 and y = 1.3 down to about -1.3, depending on the relative vertical size of the current window. These values are chosen because if x or y are much larger than 2 then the point flies off pretty quickly, being part of a large band of flat colour, which is not interesting. What is interesting is 'chaotic' ie very complex behaviour, where a small change in input alters the result drastically.

The equation is repeated for all points within a given area of the screen. More precisely, the equation is iterated for all pixels (dots) on the screen, where these are made to correspond (by scaling) to the points within the given area (-2 to +1, -1.3 to +1.3).

The Mandelbrot equation is the repetition of z(n+1) = z(n)*z(n) + k where z and k are complex numbers. The notation "z(n)" refers to the n th value of z, ie the value of z after n repetitions of the equation. Each time that z(n) is calculated the new value is fed back into the equation on the next repetition.

Note that k is the point for which the result is being calculated and that z(0) is set to 0.

After the first substitution z(1) = k.
After the second substitution z(2) = k2 + k.
After the third substitution z(3) = (k2 + k) * (k2 + k) + k
                                                = k4 + 2*k3 + k2 + k
After the fourth substitution z(4) = (k4 + 2*k3 + k2 + k) * (k4 + 2*k3 + k2 + k) + k
                                                  = k8 + 4*k7 + 6*k6 + 6*k5 + 5*k4 + 2*k3 + 2*k2 + k
and so on.

On each subsequent pass this process is repeated. The program loop that starts with the "for" statement and the 4 lines that follow it (shown below in bold) iterates the equation z(n+1) = z(n)*z(n) + k for each k.

What interests us are not so much the actual values, but how long it takes for z to "escape". Escaping means that z becomes large, ie goes off to infinity (eventually). What we are interested in is the number of times that we need to repeat the calculation for the absolute value (distance of the point from the origin, ie from (0,0) ) of z to be 2 or more, ie z_real_squared + z_imag_squared > 4. This escape value is the number that our function returns for points that escape, with a slight modification.

If the absolute value of z does not exceed 2 within MAX_ITERATIONS = 255 iterations of the equation, then we return a value based on how large the x and y components are after MAX_ITERATIONS. This is done to make the image look more interesting.

In either case, the number returned by our function is then used to give a colour to the given point on the screen, ie a value for each of the red, green and blue components of that pixel. This enables you to see different bands of escape values as distinct bands of colour.

Here is the heart of the program. Essentially it says, keep adding the original number and squaring the result until the result exceeds 4 or we have done it 255 times.

int mandfunc( int x, int y)
{
// set the initial conditions for the loop below
z_real = 0.0;
z_imag = 0.0;
z_real_squared = 0.0;
z_imag_squared = 0.0;

// Now we get to the point at last: we compute the Mandelbrot equation MAX_ITERATIONS times.
// Note k_imag and k_real are the vertical and horizontal co-ordinates of the point we are calculating.
// The statement that follows says to repeat the 4 lines of code in curly braces until iters reaches MAX_ITERATIONS or the sum of the squares of the real and imaginary parts of z exceeds 4.

for ( iters = 0; (z_real_squared + z_imag_squared) %lt; 4.0 && iters %lt; MAX_ITERATIONS; iters++ )
{
        z_real_squared = z_real * z_real;      // this is an intermediate variable
        z_imag_squared = z_imag * z_imag; // this is an intermediate variable
                                                                    // taking z = x + i*y we get
        z_imag = 2 * z_real * z_imag + k_imag;         // the new y = 2*x*y + k_imag
        z_real = z_real_squared - z_imag_squared + k_real; // the new x = x*x - y*y + k_real
}
// The first two lines above calculate two intermediate variables, followed by the calculation of the real
// and imaginary parts of z for the current iteration.

// Now we tell the caller what the result was for this point.
// Note that iters is the number of times we went through the above loop - in the first case the point did not escape, in the second it did
if ( iters >= MAX_ITERATIONS )
        return ( (int) ((z_real_squared + z_imag_squared) * 33424) % MAX_COLOURS );// the numbers here are arbitrary
        // NB If the above line were "return 0" then the interior of the shape would be a single-colour lake
else
        return( (iters % MAX_COLOURS) * 128 ); // the point escaped ie it lies outside the shape
        // This gives the bands of slightly changing colour outside the closed shape

The calling function paints a dot of colour based on the returned value.