Draw a recursive snowflake fractal curve in C#

This program draws a recursive snowflake by using an initiator and a generator to define the fractal's shape.

The initiator is a curve that represent the fractal's basic shape. In this case, the initiator is the triangle shown in the second picture above where the depth of recursion is zero.

The generator is a curve that shows how to break up a line segment to go to the next level of recursion. In this example, the generator is shown in the third picture above. To draw a line segment, the program draws 1/3 of the distance along the segment, turns -60 degrees, draws 1/3 of the original distance, turns 120 degrees, draws 1/3 of the original distance, turns -60 degrees, and finishes by drawing another 1/3 of the original distance. The result is a series of line segments that start and end at the same points as the original line segment (which would have been drawn directly for a smaller depth of recursion) but that includes a copy of the generator. To go to greater depths of recursion, all of the line segments just drawn would be replaced by smaller copies of the generator.

(You can make a simpler snowflake generator that simply turns as required. By using a separate initiator and generator, this program is more general and can draw other fractals, which I'll show in later blog entries.)

The following code shows how the program declares its initiator and generator variables. Look at the code to see how these are initialized.

// Coordinates of the points in the initiator.
private List Initiator;

// Angles and distances for the generator.
private float ScaleFactor;
private List GeneratorDTheta;

The following code shows the DrawSnowflake method that starts drawing a snowflake.

// Draw the complete snowflake.
private void DrawSnowflake(Graphics gr, int depth)
{
gr.Clear(picCanvas.BackColor);

// Draw the snowflake.
for (int i = 1; i < Initiator.Count; i++)
{
PointF p1 = Initiator[i - 1];
PointF p2 = Initiator[i];

float dx = p2.X - p1.X;
float dy = p2.Y - p1.Y;
float length = (float)Math.Sqrt(dx * dx + dy * dy);
float theta = (float)Math.Atan2(dy, dx);
DrawSnowflakeEdge(gr, depth, ref p1, theta, length);
}
}

This code loops through the points in the initiator and calls DrawSnowflakeEdge to draw each of the edges. Note that in this example to make a closed curve the first initiator point should be repeated as the last point.

The following code shows the DrawSnowflakeEdge method.

// Recursively draw a snowflake edge starting at
// (x1, y1) in direction theta and distance dist.
// Leave the coordinates of the endpoint in
// (x1, y1).
private void DrawSnowflakeEdge(Graphics gr, int depth, ref PointF p1, float theta, float dist)
{
if (depth == 0)
{
PointF p2 = new PointF(
(float)(p1.X + dist * Math.Cos(theta)),
(float)(p1.Y + dist * Math.Sin(theta)));
gr.DrawLine(Pens.Blue, p1, p2);
p1 = p2;
return;
}

// Recursively draw the edge.
dist *= ScaleFactor;
for (int i = 0; i < GeneratorDTheta.Count; i++)
{
theta += GeneratorDTheta[i];
DrawSnowflakeEdge(gr, depth - 1, ref p1, theta, dist);
}
}

If the depth of recursion is 0, the code simply draws a line segment starting at the start point with the indicated direction and distance.

For higher depths of recursion, the method multiples the length that it should draw by the scale factor (in this example 1/3) and then loops through the angles in the generator. For each angle, DrawSnowflakeEdge recursively calls itself to draw a segment in the new direction with the new length.

   

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments
  • No comments exist for this post.
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.