Sample Applications
Fractal Viewer Sample
The Extreme Optimization Numerical Libraries for .NET contain
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 = zn2
+ 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 = zn2
+ 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 = lambda zn
(1 - zn),
with the parameter lambda 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
structure.
The image pane represents a rectangular area of the complex plane. This
rectangle is defined by two complex values: _zLocation
and _zSize
.
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:
/// <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:
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:
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:
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.
Copyright © 2003-2023, Extreme Optimization. All rights reserved.
Extreme Optimization, Complexity made simple, M#, and M Sharp are trademarks of ExoAnalytics Inc.
Microsoft, Visual C#, Visual Basic, Visual Studio, Visual Studio.NET, and the Optimized for Visual Studio logo
are registered trademarks of Microsoft Corporation.