Draw a Mandelbrot set fractal with smoothly shaded colors in C#

smooth Mandelbrot setsmooth Mandelbrot setsmooth Mandelbrot setsmooth Mandelbrot setsmooth Mandelbrot setsmooth Mandelbrot set

The example Use a Complex number class to draw a Mandelbrot set fractal easily in C# explains how to draw a Mandelbrot set by iterating the equation:

    Z(n) = Z(n-1)2 + C

Where Z(n) and C are complex numbers. The program iterates this equation until the magnitude of Z(n) is at least 2 or the program performs a maximum number of iterations.

At that point it uses the number of iterations to determine a color for the pixel. For example, if the program is using K colors and it performed I iterations, then it assigns the point color number I mod K.

This example modifies the coloring algorithm to produce smoothly varying colors. First note that the following value mu approximates the fractional number of iterations that would be needed before the magnitude of Z(n) is at least 2.

mu = iterations + 1 - Math.Log(Math.Log(Z.Magnitude)) / log_escape;

Here iterations is the number of iterations actually performed, Z.Magnitude is the magnitude of Z right after the magnitude is greater than 2, and log_escape is the logarithm of the escape radius 2.

This value only approximates the actual expected fractional number of iterations and there is a noticeable error where the colors don’t blend smoothly. Fortunately it’s easy to reduce the error by using a later value of Z(n). For example, if you use Z(n + 3), then the error isn’t noticeable. The following shows the code used by the program to calculate mu.

// Reduce the error in mu.
for (int i = 0; i < 3; i++)
{
    Z = Z * Z + C;
    iteration++;
}

double mu = iteration + 1 -
    Math.Log(Math.Log(Z.Magnitude)) / log_escape;

This program also provides one other smooth color model. If you use the second smooth model in the configuration form, the program scales the resulting value of mu so it varies only once over the available colors as the number of iterations varies from 0 to the maximum number of iterations. That means the colors do not repeat and you get a very gradual smoothing.

if (ColorStyle == ColorStyles.Smooth2)
{
    mu = mu / MaxIterations * Colors.Count;
}

Finally after calculating mu with whichever method, the code calls the following GetColor method to return an appropriate color for the pixel.

// Get a color for this pixel.
private Color GetColor(double mu)
{
    int clr1 = (int)mu;
    double t2 = mu - clr1;
    double t1 = 1 - t2;
    clr1 = clr1 % Colors.Count;
    int clr2 = (clr1 + 1) % Colors.Count;

    byte r = (byte)(Colors[clr1].R * t1 + Colors[clr2].R * t2);
    byte g = (byte)(Colors[clr1].G * t1 + Colors[clr2].G * t2);
    byte b = (byte)(Colors[clr1].B * t1 + Colors[clr2].B * t2);

    return Color.FromArgb(255, r, g, b);
}

The GetColor method truncates mu to find an integer color index value and a fractional value. It then returns a color that uses the weighted average of the integer color and the following color.

See the code for additional details.

For more information on fractals, including information about the fascinating Julia set that uses the Mandelbrot set as a map, see my book Visual Basic Graphics Programming. The code is in Visual Basic 6 so you’ll have to do some translating but the math still works. (I’ve had no luck trying to get my publisher, Wiley, to let me make a .NET version of the book. If you would buy a C# version of the book, let me know.)




This entry was posted in algorithms, fractals, graphics and tagged , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *