Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / C#
Article

Mandelbrot Set with Smooth Drawing

Rate me:
Please Sign up or sign in to vote.
4.97/5 (12 votes)
11 Apr 2007CPOL2 min read 63.4K   1.7K   37   10
This program implements a simple way to see a Mandelbrot set
Screenshot - capture_12042007_045304.png

Introduction

This program implements a simple way to see a Mandelbrot set. There are tons of programs like this one. But because of a Science library, SCI[1] , it allows us to calculate f(z) = z * z + C directly.

Main Loop of Code

There is a pseudo-code on Wikipedia[2].

C#
Sci.Math.ComplexNumber C = z;
int iteration = 0;
while (z.Modulus < escapeRadius && iteration < maxIteration)
{
    z = z * z + C;
    iteration++;
}

int colorIndex = 0;
if (iteration < maxIteration)
{
    z = z * z + C; iteration++;
    z = z * z + C; iteration++;
    double mu = iteration - (Math.Log(Math.Log(z.Modulus))) / logEscapeRadius;
    colorIndex = (int)(mu / maxIteration * 768);
    if (colorIndex >= 768) colorIndex = 0;
    if (colorIndex < 0) colorIndex = 0;
}

For each point C in viewport, C would be substituted into f(z) = z * z + C as z, then the result would be substituted again, iteratively until iteration steps reaching maxIteration or modulus of the result is greater than escapeRadius. mu
is for smoothing image result. Please refer to [3] for more details about mu.

Then, according to the iteration steps, decide what color would be used for this coordinate. There is a Color array for speeding the look up. Colors[] is generated
by using:

C#
for (int i=0;i<768;i++)
{
    int colorValueR=0;
    int colorValueG=0;
    int colorValueB=0;
    if (i >= 512)
    {
        colorValueR = i - 512;
        colorValueG = 255 - colorValueR;
    }
    else if (i >= 256)
    {
        colorValueG = i - 256;
        colorValueB = 255 - colorValueG;
    }
    else
    {
        colorValueB = i;
    }
    Colors[i] = Color.FromArgb(colorValueR, colorValueG, colorValueB);
}

The values inside Colors look like:

index Red Green Blue
  0    0    0    0
  .    0    0    .
 255   0    0   255
 256   0    0   255
  .    0    .    .
 384   0   128  127
  .    0    .    .
 511   0   255   0
 512   0   255   0
  .    .    .    0
 640  128  127   0
  .    .    .    0
 767  255   0    0

Screen Point To Coordinate On Plane Conversion

When the cursor moves on an image, there is a label showing the current coordinate on the plane:

C#
int x = e.X;
int y = (control.Height - 1) - e.Y;
Sci.Math.ComplexNumber P = P2 + new Sci.Math.ComplexNumber((double)x * 
            (P1.Re - P2.Re) / (p.Width - 1),
            (double)y * (P1.Im - P2.Im) / (p.Height - 1));
pos.Text = String.Format("({0:E},{1:E})", P.Re, P.Im); 

The e.X and e.Y are points on the screen, the unit of which is pixels. The origin is at the top and left-most. In other words, the x-axis on the screen and plane have the same direction. But the y-axis is not the same.

Scaling Image

When the user double-clicks, the clicked point on the image has to be converted into the coordinate of plane. The coordinate of the clicked point would be the center point of the new image, with scaling factor 2.

C#
int x = e.X;
int y = (control.Height - 1) - e.Y;
Sci.Math.ComplexNumber P = P2 + new Sci.Math.ComplexNumber((double)x * 
           (P1.Re - P2.Re) / (p.Width - 1),
           (double)y * (P1.Im - P2.Im) / (p.Height - 1));
Sci.Math.ComplexNumber diff = new Sci.Math.ComplexNumber();
switch (e.Button)
{
    case MouseButtons.Left:
        // Zoom in
        diff = (P1 - P2) / 2 / 2;
        break;
    case MouseButtons.Right:
        // Zoom out
        diff = (P1 - P2) / 2 * 2;
        break;
}

// New Region
P1 = P + diff;
P2 = P - diff;

This program use two ComplexNumbers to define the viewport. P1 is at the topmost and rightmost, and P2 at the bottommost and leftmost. P is the center coordinate in the original viewpoint bounded by P1 and P2. The diff represents the vector from P to new P1, which is P1 of viewport after zooming.

Some Screenshots

All are with maximal iteration=150 and escape radius=2:

Screenshot - 150-1.png

Screenshot - 150-2.png

Screenshot - 150-3.png

Screenshot - 150-4.png

Screenshot - 150-5.png

Conclusion

The implementation of the Mandelbrot set is really easy. But the drawing part might meet with a bottleneck. Therefore, the code uses a lower-level method to draw, instead of calling SetPixel directly. You can also try to change the polynomial f(z) to get some interesting images.

Links

  1. SCI
  2. Mandelbrot set
  3. Smooth Shading for the Mandelbrot Exterior

History

  • 2007/04/12 - First edition

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Taiwan Taiwan
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionDetail / Precision Pin
M-Badger17-Apr-18 2:21
M-Badger17-Apr-18 2:21 
GeneralNice Pin
Nicholas Butler10-Jan-08 1:40
sitebuilderNicholas Butler10-Jan-08 1:40 
Hi
Thanks for a nice article. You got my 5 Smile | :)

I spent some time yesterday trying to improve performance and thought you might be interested in the results. I managed to speed up the main loop by a factor of about 2.5 to 6 depending on MaxIterations. The main problem was the maths library you use: it is not efficient. I have not changed the algorithm at all.

	double xStep = ( P1.Re - P2.Re ) / ( p.Width - 1 );
	double yStep = ( P1.Im - P2.Im ) / ( p.Height - 1 );
	double logEscapeRadius = Math.Log( escapeRadius );
#if NICK
	double squaredEscapeRadius = escapeRadius * escapeRadius;
	double r, i;
#endif
	Sci.Math.ComplexNumber z = P2;
	progress.Maximum = p.Width;

	for ( int x = 0 ; x < p.Width ; x++ )
	{
		z.Im = P1.Im;
		z.Re += xStep;
		for ( int y = 0 ; y < p.Height ; y++ )
		{
			z.Im -= yStep;
			Sci.Math.ComplexNumber C = z;
			int iteration = 0;
#if !NICK
			while ( z.Modulus < escapeRadius && iteration < maxIteration )
			{
				z = z * z + C;
#else
			while ( ( z.Re * z.Re ) + ( z.Im * z.Im ) < squaredEscapeRadius && iteration < maxIteration )
			{
				r = ( z.Re * z.Re ) - ( z.Im * z.Im ) + C.Re;
				i = ( 2 * z.Im * z.Re ) + C.Im;
				z.Re = r;
				z.Im = i;
#endif
				iteration++;
			}

			int colorIndex = 0;
			int index = ( y * p.Width + x ) * 4;
			if ( iteration < maxIteration )
			{
#if !NICK
				z = z * z + C; iteration++;
				z = z * z + C; iteration++;
				double mu = iteration - ( Math.Log( Math.Log( z.Modulus ) ) ) / logEscapeRadius;
#else
				r = ( z.Re * z.Re ) - ( z.Im * z.Im ) + C.Re; i = ( 2 * z.Im * z.Re ) + C.Im; z.Re = r; z.Im = i;
				r = ( z.Re * z.Re ) - ( z.Im * z.Im ) + C.Re; i = ( 2 * z.Im * z.Re ) + C.Im; z.Re = r; z.Im = i;
				iteration += 2;
				double modSquared = ( ( z.Re * z.Re ) + ( z.Im * z.Im ) );
				double mu = iteration - ( Math.Log( 0.5 * Math.Log( modSquared ) ) ) / logEscapeRadius;
#endif
				colorIndex = ( int ) ( mu / maxIteration * 768 );
				if ( colorIndex >= 768 ) colorIndex = 0;
				if ( colorIndex < 0 ) colorIndex = 0;
			}
			argb[ index ] = Colors[ colorIndex ].B;
			argb[ index + 1 ] = Colors[ colorIndex ].G;
			argb[ index + 2 ] = Colors[ colorIndex ].R;
			argb[ index + 3 ] = 255;

			z = C;
		}
		progress.Value = x;
	}


----------------------------
Be excellent to each other Smile | :)


GeneralVery nice! Pin
Marc Clifton7-Jan-08 15:16
mvaMarc Clifton7-Jan-08 15:16 
GeneralCongrats Pin
T1TAN17-Jul-07 4:23
T1TAN17-Jul-07 4:23 
GeneralThe zip file can't be opened by 7-zip Pin
sunwei16886-May-07 22:31
sunwei16886-May-07 22:31 
GeneralRe: The zip file can't be opened by 7-zip Pin
livibetter6-May-07 22:46
livibetter6-May-07 22:46 
GeneralRe: The zip file can't be opened by 7-zip Pin
sunwei16886-May-07 23:09
sunwei16886-May-07 23:09 
Generalmsg from friederbluemle Pin
Sean Ewington19-Apr-07 9:35
staffSean Ewington19-Apr-07 9:35 
GeneralRe: msg from friederbluemle Pin
livibetter19-Apr-07 17:17
livibetter19-Apr-07 17:17 
GeneralHint Pin
fwsouthern11-Apr-07 14:22
fwsouthern11-Apr-07 14:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.