This program draws a recursive snowflake fractal 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 across the top of 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 less complicated 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 posts.

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<PointF> Initiator; // Angles and distances for the generator. private float ScaleFactor; private List<float> GeneratorDTheta;

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

// 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 recursive `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.

Download the example to see additional details.

Marvellous ! Keep working Rod , I’m want to learn more 😀 .

Pingback: Draw a recursive overlapping snowflake fractal curve in C#C# Helper

Hi Rods, is there any way to automatically redraw the image when I change the window size (by pressing maximize and minimize button), if yes, please show me, Thank you a lot.

And there is more, when your program is running, If I change the depth value and press Enter, it will redraw instantly. But when I do the same(I had copied exactly every piece of code in your program ), It make the sound “pring”. I can only redraw when I press Button “Go”, not when press “Enter”. I have studied your program for a while but I could not find out why. I hope you can help me, thanks.

At design time I set the form’s AcceptButton property to the Go button. When you press Enter, the form triggers its AcceptButton. If there isn’t a button assigned to the property, it beeps. (It’s a small thing but doesn’t appear in the code so it can be hard to figure out.)

You would need to create a new event handler to detect the change in size and redraw. For example, you could catch the PictureBox’s Resize event and make it call the same code that the button executes.

To make that cleaner, you could move that code into a new method and then call the method from both the button’s Click event handler and the new Resize event handler.

Thanks, those are helpful.

Pingback: Draw a recursive overlapping snowflake fractal curve in C#C# Helper

Hi Rod,

Seems like the example project is no longer available – you mind fixing the link?

Thanks!

My bad. I’ve fixed it. Thanks for pointing this out!

hi Rod , can u show me code of Go button please ,

I would post it here but it’s pretty long. Mostly it does some calculations to create the initiator and generator, and then it calls

DrawSnowflake.Click the Download button at the end of the post to download the complete solution. Then you can look at the code to see all of the button’s details.

Hey Rod,

How you choose these coordinates at the begining? Why you define them in this odd mathematical formula?

float height = 0.75f * (Math.Min(

picCanvas.ClientSize.Width,

picCanvas.ClientSize.Height) – 20);

float width = (float)(height / Math.Sqrt(3.0) * 2);

float y3 = picCanvas.ClientSize.Height – 10;

float y1 = y3 – height;

float x3 = picCanvas.ClientSize.Height / 2;

float x1 = x3 – width / 2;

float x2 = x1 + width;

Initiator.Add(new PointF(x1, y1));

Initiator.Add(new PointF(x2, y1));

Initiator.Add(new PointF(x3, y3));

Initiator.Add(new PointF(x1, y1));

The

Initiator.Addstatements create the three line segments that make the triangle you see when depth = 0. I think form height is chosen to make the triangle fit on the form nicely. It usesMath.Minso it fits whether the form is tall and skinny or short and wide.The formula for width calculates the width based on the height and the fact that the triangle is equilateral. The right and left halves of the triangle are 30-60-90 triangles, so if their longer non-hypotenuse sides are

heightlong, then their shorter sides areheight/Sqrt(3)long. There are two of them, so the larger triangle’s width is twice that.The rest of this code positions the points so the triangle is centered horizontally and moved close to the bottom of the drawing area.