In a Sierpinski pentagon, larger pentagons are recursively divided into five smaller pentagons with a sixth uncolored pentagon in the center. The following picture shows the first four levels of the resulting fractal.

For this example, I decided to think about a pentagon as centered on a point and with a particular radius. The following method finds the corners of a pentagon centered at a particular point and with a given radius.

// Find the pentagon's corners. private PointF[] GetPentagonPoints(PointF center, float radius) { PointF[] points = new PointF[5]; double theta = -Math.PI / 2.0; double dtheta = 2.0 * Math.PI / 5.0; for (int i = 0; i < 5; i++) { points[i] = new PointF( center.X + (float)(radius * Math.Cos(theta)), center.Y + (float)(radius * Math.Sin(theta))); theta += dtheta; } return points; }

This method simply loops variable `theta` through the angles that determine the pentagon's points. It uses sines and cosines to find the points' positions, adding an offset so the shape is centered over the center point.

The only trick here is that `theta` starts at -90° so the pentagon's first point is on the top of the shape.

Drawing the Sierpinski pentagon is actually fairly easy in principle. To subdivide a pentagon, you simply scale the larger pentagon to make a smaller copy and then position it correctly. The following sections explain how to calculate the scale factor and the smaller pentagons' positions.

# Scale Factor

To calculate the scale for the smaller pentagons, look at the picture below.

Let S be the side length of the big pentagon and let s be the side length of the smaller pentagons.

Next, look at the smaller pentagon's inner triangle shown in blue. The five inner triangles' central angles add up to 360, so each of those inner angles must be 360 / 5 = 72°.

Because the angles in any triangle must add up to 180°, and because the two other angles in the inner triangle are the same, those angles are (180 - 72) / 2 = 54°.

If you look at the picture, you can see that two instances of that angle 54° plus the pentagon's outer angle are colinear, so they must add up to 180°. That means the outer angle is 180 - 2 * 54 = 180 - 108 = 72° as shown on the picture.

The little horizontal space occupied below the left pentagon is s * cos(72) wide. That means the total width of the big pentagon is the following.

S = 2 * (s + s * cos(72)) = 2 * s * (1 + cos(72))

Now we can solve for s in terms of S to get the following.

s = S / (2 * (1 + cos(72)))

That means when we move from a bigger pentagon to a smaller one, we need to scale the pentagon by a factor of:

scale = 1 / (2 * (1 + cos(72)))

# Radius Change

We still need to figure out where to put the smaller pentagons. Because I'm treating them as centered over a point, we need to figure out where the centers of the new pentagons should be.

To make that calculation, consider the following picture.

Suppose the radius of the larger pentagon is R. Then the radius of the smaller pentagon is r = R * scale. That means the distance d from the center of the original pentagon to the center of the smaller pentagons is d = R - r.

Now we can use the `GetPentagonPoints` method to find the centers for the smaller pentagons.

# Drawing the Pentagons

The program uses the following code to draw Sierpinski pentagons.

// Scale factor for moving to smaller pentagons. private float size_scale = (float)(1.0 / (2.0 * (1 + Math.Cos(Math.PI/180*72)))); // Draw a sierpinski pentagon. private void DrawPentagon(int depth, Graphics gr, Brush brush, PointF center, float radius) { // If we are done recursing, draw the pentagon. if (depth <= 0) { // Find the pentagon's corners. PointF[] points = GetPentagonPoints(center, radius); gr.FillPolygon(brush, points); } else { // Find the smaller pentagons' centers. float d = radius - radius * size_scale; PointF[] centers = GetPentagonPoints(center, d); // Recursively draw the smaller pentagons. foreach (PointF point in centers) { DrawPentagon(depth - 1, gr, brush, point, radius * size_scale); } } }

The code first uses the formula shown earlier to define the size scale factor.

The `DrawPentagon` method actually draws a pentagon. The method first checks its depth of recursion. If `depth` is less than or equal to zero, then the recursion is finished. In that case, the method uses `GetPentagonPoints` to get the current pentagon's vertices and then uses the `Graphics` object's `FillPolygon` method to draw the pentagon.

If `depth` is greater than zero, the method must recursively draw smaller pentagons. It calculates the distance `d` from the original pentagon's center to the centers of the smaller pentagons. It then calls `GetPentagonPoints` to find the centers of the smaller pentagons. The code loops through those points and calls the `DrawPentagon` method recursively to draw the smaller pentagons centered at those points.

The following code shows how the program redraws the Sierpinski pentagon when you change the depth or resize the form.

// Draw. private void picPentagon_Paint(object sender, PaintEventArgs e) { int depth = (int)nudDepth.Value; PointF center = new PointF( picPentagon.ClientSize.Width / 2, picPentagon.ClientSize.Height / 2); float radius = (float)Math.Min(center.X, center.Y); DrawPentagon(depth, e.Graphics, Brushes.Red, center, radius); }

This code gets the depth that you selected in the form's `NumericUpDown` control. It finds the `PictureBox` control's center, and uses the smaller of the center point's coordinates as the pentagon's radius.

The code then calls the `DrawPentagon` method passing it the center point and radius.

Download the example to see additional details.

Hello Mr. Rod Stephens,

Receive a warm greeting. I have tried to adjust the code to implement the functionality of Undo and Redo, which you kindly supplied me with. My intention is to do Undo and Redo in the drawing of straight lines, however, after drawing the lines, when I press the Undo button, all lines except the last line are deleted. On the other hand when I press the Redo button, no action is taken.

You could help me in that regard?.

Greeting

Marine Liranzo.

Please post comments in the relevant article or follow up from previous comments so we can figure out what the context is.

You’re going to have to work through the example. Make some sort of way to store a snapshot so you can undo and redo them. You may want to use the following example as a template:

Provide undo and redo in C#

You will need to look at the posts that this one is based on so you can see how to save snapshots.