Fractal Viewer Sample

Click to see align larger image.Extreme Numerics.NET contains extensive support for working with complex numbers. The FractalViewer sample illustrates these capabilities in spectacular fashion.
About fractals
The Mandelbrot set
Julia sets
Other variations
How to use the program
The sample code
Fractal providers
The main loop

About fractals

A fractal is a mathematical shape that is so irregular that its length is infinite even though it is bounded by a small region. Fractals are usually self-similar. This means that parts of the fractal will look like the whole. This self-similarity is independent of scale. You will find parts of any size, no matter how small, that are similar to the entire fractal.

Fractals get their name from the fact that a property called the Hausdorff dimension of a fractal is a fractional number, not an integer. A normal curve has Hausdorff dimension 1, while a normal two-dimensional area has Hausdorff dimension 2. Fractal curves have Haussdorff dimension somewhere between 1 and 2.

The Mandelbrot Set

One of the most famous fractals is the Mandelbrot set, named after IBM researcher Benoit Mandelbrot. The Mandelbrot set is the boundary of the set of complex numbers c for which the iteration formula

*zn*+1 = *zn*2 + *c*

with starting value z0 = 0 is bounded.

Once the iteration lies outside the complex circle with radius 2, the point c is known to lie outside the Mandelbrot set. The number of iterations needed to reach this threshold is called the escape time.

The Mandelbrot set can be visualized in the following manner. To every point (x, y) in a grid corresponds a complex number c = x + iy. The point (x,y) is colored according to the escape time of c. If a specified maximum number of iterations is reached, c is assumed to lie on or inside the Mandelbrot set and colored black.

Julia Sets

Julia sets, named after Pierre Fatou (1878-1929) are produced by a similar iteration scheme as the Mandelbrot set. The iteration formula for a Julia set with complex parameter k is:

*zn*+1 = *zn*2 + *k*

with starting value z0 = c is bounded. If k lies inside the Mandelbrot Set, then the points for which the above iteration is bounded define a fractal. Every value of *k generates a new shape, so the variations are endless.

Other variations

The standard Julia Sets are created using a quadratic iteration formula. Using polynomials of higher order gives rise to another class of fractals. The fractal viewer sample shows Julia sets for order 2, 3, 5, 6, and 7.

In the original coloring scheme, the inside of the fractals is colored black. Various schemes have been devised to make the inside look more interesting. The scheme used in this sample is to color the inside using the least distance from the origin produced during the iteration.

How to use the Program

The FractalViewer window is divided into two panes.

The left pane shows the current fractal image. The window represents a rectangle in the complex plane. The complex number corresponding to the top left corner of the left pain is shown in the status bar. You can zoom in on an area to view the fractal in greater detail.

When you click the Calculate button on the right pane, a view of the Mandelbrot set will appear in the image pane. You can zoom in by clicking on one corner of the area you want to see, and dragging the mouse to the other corner. This will zoom the view so the selected area fits into the image pane. Dragging with the right mouse button produces the opposite effect: it will zoom out so the current area fits into the selected box. If things get out of hand, the ‘Reset view’ button can come to the rescue.

The right pane shows the current settings. The fractal type combo box lets you choose which of 7 available fractal types you would like to generate. The options are:

Value Description
Mandelbrot set The standard mandelbrot set.
Julia set The Julia set for a given value.
Julia set (zn+c) The Julia set generated by z = zn+c (n = 3, 5, 6, or 7).
Lambda fractal A variation on the Julia set.

Below the fractal type combo is the parameters box. The meaning of the parameters depends on the type of fractal. The Mandelbrot set has no parameters. All Julia set fractals have two parameters: the real and imaginary parts of the constant k in the iteration formula.

The Lambda fractals are a variation of Julia sets. They use the iteration formula

*zn*+1 = λ *zn* (1 - *zn*),

with the parameter λ a complex number. Once again, the real and imaginary parts of this number are the two parameters of this fractal type. Every Lambda fractal is equivalent to a classical julia set.

Next on the right pane is a slide that lets you specify the maximum number of iterations. A low value will result in a faster plot, but will miss the more intricate detail at larger magnifications. It’s usually a good idea to start with a low value (100-200), and increase gradually as you zoom in.

Clicking the Plot button starts the calculation of the fractal. As the calculation progresses, the results are shown on the image pane line by line. You can cancel the calculation by hitting the Plot button again.

The most interesting Julia sets are generated by points close to the edge of the Mandelbrot set. If you have zoomed in on part of the Mandelbrot set, and then change the fractal type to one of the Julia set types, the parameter will be set to correspond to the center of the Mandelbrot image.

The sample code

The code involves a lot of user interface and threading issues that are beyond the scope of this sample. Instead, we will focus on the implementation of the fractal calculation, and its use of the [ DoubleComplex](../reference/extreme.mathematics.double-complex.html) structure.

The image pane represents a rectangular area of the complex plane. This rectangle is defined by two complex values: _zLocation and _zSize<code/>. Both variables are initialized by the ResetPlotAreaView method and changed by the ZoomIn and ZoomOut methods.

Fractal providers

The type of fractal to draw is represented by an object implementing the IFractalProvider interface. This interface defines methods and properties that determine the iteration process for a specific type of fractal, as well as its parameters. The interface definition as well as implementations for several fractal types are found in FractalProviders.cs. The methods and properties defined by the IFractalProvider interface are as follows:

C#
/// <summary>/// Defines the methods and properties for a class that/// provides the formula for a type of fractal./// </summary>public interface IFractalProvider
{
	/// <summary>	/// Returns the initial value for the iteration.	/// </summary>	/// <param name="z0">The value of the complex variable	/// in the formula.</param>	Complex Initialize(Complex z0);
	/// <summary>	/// Performs an iteration step of the fractal formula and 	/// returns the result.	/// </summary>	/// <param name="z">The current value of the iteration	/// formula.</param>	/// <returns>The next value of the iteration formula.</returns>	Complex Step(Complex z);
	/// <summary>	/// Gets the number of parameters that need to be specified	/// for this <see cref="IFractalProvider"/>.	/// </summary>	/// <value>The number of parameters.</value>	/// <remarks>Note that complex numbers count as two 	/// parameters: one for the real part and one for the	/// imaginary part.</remarks>	Int32 Parameters.Count { get; }
	/// <summary>	/// Gets a <see cref="String"/> array containing the	/// names of the parameters for this <see 	/// cref="IFractalProvider"/>.	/// </summary>	/// <value>A <see cref="String"/> array.</value>	String[] ParameterNames { get; }
	/// <summary>	/// Gets or sets the parameters associated with the 	/// iteration formula. Different parameters yield different 	/// fractals.	/// </summary>	Double[] Parameters { get; set; }
}

This interface is implemented by four different fractal providers. For example, for the MandelbrotFractalProvider, the Initialize method is very simple:

C#
public Complex Initialize(Complex z0)
{
	_c = z0;
	return Complex.Zero;
}

It simply assigns its only parameter to a local variable and returns complex zero. The Step method is possibly even simpler:

C#
public Complex Step(Complex z)
{
	return z*z + _c;
}

Complex numbers are treated just like any other value type. All arithmetic operators have been overloaded, allowing mathematical expressions involving complex numbers to be expressed in the familiar, compact form. Equivalent static methods are available for languages that don’t support operator overloading.

The main loop

The work of calculating the color value for each pixel is done in the Worker_Draw method in FractalViewerMain.cs. This method is executed on a separate worker thread to ensure that the user interface remains responsive. It uses three class-level variables:

  • _bitmap: bitmap that contains the image;
  • _maxIterations: the maximum number of iterations before a point is assumed to be inside the fractal;
  • _provider: the fractal provider;
  • _zLocation: the complex number corresponding to the upper left corner of the image.

The complete source code is given below:

C#
private void Worker_Draw()
{
	Double rMin;
	Int32 iMin = 0;

	Complex z0 = _zLocation;
	z0.Im += plotArea.ClientRectangle.Y * _scale;
	for(Int32 y = 0; y < _bitmap.Height; y++)
	{
		z0.Re = _zLocation.Re;
		for(Int32 x = 0; x < _bitmap.Width; x++)
		{
			Complex z = _provider.Initialize(z0);
			Int32 n;
			rMin = Double.MaxValue;
			for(n = 0; n < _maxIterations; n++)
			{
				z = _provider.Step(z);
				Double r = z.Modulus;
				if (r > 2)
					break;
				if (r < rMin)
				{
					iMin = n;
					rMin = r;
				}
			}
			if (n == _maxIterations)
				n = _maxColors - (Int32)(1536*rMin) % 1024;
			_bitmap.SetPixel(x, y, _colors[n]);
			z0.Re += _scale;
		}
		z0.Im += _scale;
		if (CancelRequested)
		{
			lock (this)
			{
				_cancelled = true;
				Monitor.Pulse(this);
			}
			return;
		}
		this.Invoke(_reportProgress, new Object[] {y});
	}
}

The two outer loops iterate over the pixels of the image. The local variable z0 contains the complex number corresponding to the current pixel. The iteration is line by line, so the real part is updated at the end of each x loop by adding the length per pixel scale factor, _scale, to it. This value is reset at the beginning of every y loop. The imaginary part of z0 is incremented at the end of every y loop.

Of interest to us is mainly the inner loop, which calculates the color value for the current point in the image. z0, the complex number corresponding to the current pixel, is passed to the Initialize method of the fractal provider to retrieve the starting value for the iteration. z contains the current value of zn. The inner loop then calls the provider’s Step method.

The inner loop terminates for one of two reasons. The pixel color is calculated differently for each reason. If the modulus of the current iteration value was greater than two, the color value is determined by the number of iterations. If the maximum nu When the color value of the pixel has been determined, it is set in the bitmap using the SetPixel method.