Title: Draw an escape time polynomial fractal in C#
This example lets you draw escape time fractals similar to the Mandelbrot set and the Julia set. It's a fairly complicated program, but I'm going to be lazy and refer you to a few other examples for details. Here are the main pieces.
Mandelbrot Sets
To draw any escape time fractal, you iterate one or more equations until the value reaches some kind of limit. For example, in the Mandelbrot set, the value tends toward infinity. At that point, you use the number of iterations that it took to reach the limit to decide what color to give the pixel.
To create a Mandelbrot set, you iterate the following equations.
To draw the fractal, you loop over each point (x, y) and repeatedly calculate Zn. It can be shown that, if |Zn| ever exceeds 2, then it eventually heads towards infinity. You calculate Zn a set number of times (for example, 64 times) or until |Zn| exceeds 2. Then you color the corresponding pixel according to the number of iterations it took to exceed 2.
In the picture above, the maximum number of iterations is 64 and the escape limit is 2. Use larger number of iterations to color more black pixels. Change the escape limit if you're working with other equations and want to experiment.
See these posts for information on drawing the Mandelbrot set.
For more information on the Mandelbrot set in general, see Wikipedia: Mandelbrot set.
Julia Sets
You generate a Julia set in much the same way you draw a Mandelbrot set except you use these equations:
For more information on the Julia set, see Wikipedia: Julia set.
Complex Class
To evaluate the Zn values, the program uses the Complex class described in the following post.
See that post for details.
The following list describes the Complex class's most important details for this post.
- The Re and Im properties return the number's real and imaginary parts.
- The class overloads the +, -, *, and / operators so you can use them in calculations.
- The Sqrt method returns the number's square root.
- The Pow method raises the number to a (real number) power.
You can use those methods in calculations. For example, if Zn is a Complex number, then the following code works.
Complex ZnPlus1 = 1 - Zn.Pow(2) + Zn.Pow(5) / (2 + 4 * Zn) + 0.2;
Parsing Equations
One of the more interesting things about this program is that it lets you enter at runtime the equations that the program iterates. You could parse the expressions entered at runtime, but this progam takes a different approach.
To evaluate the equations that you enter, the program creates a string that defines a static class named Evaluator. That class contains a single static method named EvaluatePoint that finds information about a pixel's color. The program plugs in the values that you enter on the progam's form to create an EvaluatePoint method.
The following code shows an example of the EvaluatePoint method. The pieces that were plugged in from values entered on the form are highlighted in blue.
using howto_escape_time_polynomial;
public static class Evaluator
{
public static PointInfo EvaluatePoint(float x, float y)
{
Complex i = new Complex(0, 1);
Complex Zn = x + y * i;
int step_num;
for (step_num = 0; step_num < 64; step_num++)
{
if (Zn.Magnitude > 2f) break;
// Calculate Zn+1.
Complex ZnPlus1 = Zn.Pow(2) + (-0.4 + 0.6 * i);
Zn = ZnPlus1;
}
return new PointInfo(Zn, step_num);
}
};
This code initializes Zn to x + y˙i, which was entered on the form. It then iterates to calculate values of Zn until it either reaches the maximum number of iterations (entered on the form) or the value exceeds the maximum magnitude (also entered on the form).
The program compiles this function and then calls it to find colors for the fractal's points. See the example Evaluate numeric expressions that are entered by the user in C# for information about compiling the method.
The GetMethodInfo method defines and compiles the method. It then returns a MethodInfo object representing the compiled method.
PointInfo Class
The EvaluatePoint method iterates a function for a point. The program then needs to use the final value and the number of iterations that were performed to determine the point's color. To return that information, the EvaluatePoint method uses the following PointInfo class.
public class PointInfo
{
public Complex Z;
public int StepNum;
public PointInfo(Complex z, int step_num)
{
Z = z;
StepNum = step_num;
}
}
This class simply stores the final value of Zn and the number of iterations that the method performed.
Drawing the Fractal
The following code shows how the program draws the fractal.
private Bitmap DrawFractal(float xmin, float ymin,
float dx, float dy, int width, int height)
{
// Compile the evaluator function.
MethodInfo method_info = GetMethodInfo(
txtMaxSteps.Text, txtMaxMagnitude.Text,
txtZ0.Text, txtZn1.Text);
if (method_info == null) return null;
// Make a Bitmap and associated Bitmap32 object.
Bitmap bm = new Bitmap(width, height);
Bitmap32 bm32 = new Bitmap32(bm);
bm32.LockBitmap();
// Loop over the pixels.
object[] method_params = new object[2];
float y = ymin;
for (int iy = 0; iy < bm32.Height; iy++)
{
float x = xmin;
for (int ix = 0; ix < bm32.Width; ix++)
{
// Device coordinate point (ix, iy) corresponds
// to world coordinate point (x, y).
// Calculate the color for this point.
// Make the parameter list.
method_params[0] = x;
method_params[1] = y;
// Execute the method.
PointInfo point_info = (PointInfo)
method_info.Invoke(null, method_params);
// Set the pixel's color.
ColorPixel(bm32, ix, iy,
method_info, point_info.Z, point_info.StepNum);
// Move to the next point in world coordinates.
x += dx;
}
// Move to the next row in world coordinates.
y += dy;
}
// Unlock the Bitmap32.
bm32.UnlockBitmap();
return bm;
}
This code calls GetMethodInfo to build and compile the EvaluatePoint method. It then creates a result bitmap and loops through its pixels.
For each pixel, the code saves the coordinates of the fractal point that correspond to the pixel in the method_params array. Then then invokes the EvaluatePoint method, passing it the parameters. That method returns a PointInfo object indicating the final value of Zn and the number ot iterations performed. The code passes those values to the ColorPixel method to set the pixel's color. See the earlier post Draw a Mandelbrot set fractal with smoothly shaded colors in C# for information about how the pixels are colored.
Presets
In addition to letting you enter the equations that should be iterated, the program's Presets menu lets you select pre-defined values that produce interesting results. Most of these are shown on the webpage Wikipedia: Julia set.
Here's a gallery showing many of those preset images, mostly drawn with the second smooth shading style.
Give the program a try. Experiment with the equations and other parameters and see what interesting fractals you can create.
Download the example to experiment with it and to see additional details.
|