Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Mandelbrot in C# and Windows forms

0.00/5 (No votes)
31 Jul 2006 1  
A project written in C# and Windows forms to interactively explore the Mandelbrot set

Introduction

This project written in C# allows the user to interactively explore the Mandelbrot set, and to keep the pictures generated as well as the coordinates of the view Window. The picture can be saved as a bitmap file, or copied to the clipboard. The view Window information can be saved to an XML file or copied to the clipboard.

Description of the user interface

When the program starts the main image has not been calculated yet. To have an overview of the Mandelbrot set, simply click the "Calculate" button located in the top toolstrip. Besides the main image, two panels are located in the top right of the window, "Preview" and "Bird eye".

Preview

The preview provides for a small image of the current view on the Mandelbrot set. The preview is always up to date, unlike the main picture, which, for performance reasons, needs a click on "Calculate" to be refreshed.

Bird eye

The bird eye gives an overview of the Mandelbrot set, and never changes. The mouse can be dragged on the Bird eye Window to select a new view Window. At this time the preview is updated, as well as the numerical values of the view Window. The main picture still requires a click on "Calculate" to reflect the changes. The bird eye is useful to zoom out, or to quickly zoom to another part of the set.

Main picture

It shows the image of the view Window, automatically scaled to fit inside the available space. The user can drag the mouse on the main picture to select a new view Window. Unlike for the bird eye, the new view Window, is relative to the currently selected view Window.

Numerical values

The top of the Window has a number of controls to edit numerical values. Here is a description of those.

Image size

The "Width" and "Height" give the size in pixels of the bitmap created by the Mandelbrot routines. To avoid strange looking images, the ratio between "Width" and "Height" must be the same as for the view Window on the Mandelbrot set. The checkbox "Auto adjust" lets the program change the "Height" value when the user changes the view Window (if needed). As the user can manually change the values of "Width" and "Height", the button "Adjust ratio" forces the update of the "Height" value. A comment gives the current ratio, for those who would want to do some calculations by hand.

Mandelbrot set Window

XMin, XMax, YMin and YMax define the view Window on the Mandelbrot set. The values can be edited manually, the other controls are automatically updated. A comment shows the ratio of the current view Window.

Calculation parameters

Maximum iteration count sets the number of iterations after which a given point is classified as being outside the Mandelbrot set. It can be interesting to try a few different values. Note the image generated is 24 bits per pixels, so the max iteration count will give similar results modulo 256.

Toolstrip commands

  • Calculate: regenerate the main picture
  • Save: saves the main picture to a file, as a Windows bitmap. Also saves the parameters into an XML file, carrying the same name
  • Reset: restores all the parameters to default values
  • Copy: copies the main picture to the clipboard
  • Copy parameters: copies the parameters to the clipboard

Some code

The bitmap generation is done as follows:

public System.Drawing.Bitmap GetBitmap(
    int pImageWidth,
    int pImageHeight,
    double pXMin,
    double pXMax,
    double pYMin,
    double pYMax,
    int pMaxIterationCount)
{
    try
    {
        int i = 0;
        int j = 0;
        int[][] lValues = new int[pImageHeight][];

        for (i = 0; i < pImageHeight; i++)
        {
            lValues[i] = new int[pImageWidth];
        }

        CalculateLevels(
            lValues,
            pImageWidth,
            pImageHeight,
            pXMin,
            pXMax,
            pYMin,
            pYMax,
            pMaxIterationCount);

        System.Drawing.Bitmap lBitmap = new System.Drawing.Bitmap
        (pImageWidth, pImageHeight, 
        System.Drawing.Imaging.PixelFormat.Format32bppArgb);

        byte lRed = 0;
        byte lGreen = 0;
        byte lBlue = 0;
        int lPixelByteCount = 4;
        int lAlphaPos = 3;
        int lRedPos = 2;
        int lGreenPos = 1;
        int lBluePos = 0;
        int lPixelCount = pImageHeight * pImageWidth;
        int lPixelPos = 0;

        m_ProgressBar.Minimum = 0;
        m_ProgressBar.Maximum = lPixelCount;
        m_ProgressBar.Value = 0;

        System.Drawing.Imaging.BitmapData lBitmapData = 
                new System.Drawing.Imaging.BitmapData();

        lBitmap.LockBits(
            new System.Drawing.Rectangle(
            0,
            0,
            pImageWidth,
            pImageHeight),
            System.Drawing.Imaging.ImageLockMode.WriteOnly,
            System.Drawing.Imaging.PixelFormat.Format32bppArgb,
            lBitmapData);

        unsafe
        {
            System.Byte* lStartPtr = (System.Byte*)((void*)lBitmapData.Scan0);
            int lEndToStart = lBitmapData.Stride - 
                    pImageWidth * lPixelByteCount;
            System.Byte* lPtr = lStartPtr;

            for (j = 0; j < pImageHeight; j++)
            {
                for (i = 0; i < pImageWidth; i++)
                {
                    GetColorFromLevel(
                        lValues[j][i],
                        pMaxIterationCount,
                        out lRed,
                        out lGreen,
                        out lBlue);

                    lPtr[lAlphaPos] = 255;
                    lPtr[lRedPos] = lRed;
                    lPtr[lGreenPos] = lGreen;
                    lPtr[lBluePos] = lBlue;

                    lPtr += lPixelByteCount;
                    lPixelPos++;
                }

                lPtr += lEndToStart;

                m_ProgressBar.Value = lPixelPos;
                System.Windows.Forms.Application.DoEvents();
            }
        }

        lBitmap.UnlockBits(lBitmapData);

        return lBitmap;
    }
    catch
    {
        return null;
    }
}            

This function gets an array of values describing the view Window on the Mandelbrot set. It then creates a bitmap of the appropriate size, locks it and fills it after inferring colors from the initial array.

The color inference

A very simple algorithm is used here. The potential is compared to the maximum available. The ratio is then divided into 8 intervals that will progress as follows: Black to Red - Red to Yellow - Yellow to Green - Green to Cyan - Cyan to Blue - Blue to Magenta - Magenta to White - White to Black

Sample XML file

<?xml version="1.0" encoding="utf-16"?>
<State xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance 
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <XMin>-0.040557444936069775</XMin>
  <XMax>-0.040016368521333778</XMax>
  <YMin>0.98423178672189038</YMin>
  <YMax>0.98491968869265967</YMax>
  <ImageWidth>1200</ImageWidth>
  <ImageHeight>1526</ImageHeight>
  <MaxIterationCount>100</MaxIterationCount>
</State>      

Sample images calculated with this piece of software

Note: These images have been scaled down to meet size requirements.

Known issues

When the user clicks on the NumericUpDown control to increase or decrease the values of XMin, XMax, YMin or YMax, the preview is updated. An exception is thrown when the user keeps the mouse down. After a number of milliseconds, the control starts speeding up, and the NumericUpDown control complains about some internal timer. I did not deem it appropriate to spend time fixing this problem.

References

For those interested in digging deeper into fractals, I recommend the book "The science of fractal images" written by Barnsley, Devaney, Mandelbrot (himself) Peitgen, Saupe and Voss. My copy was published in 1988 at Springer Verlag.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here