Draw a Sierpinski pentagon in C#


[Sierpinski pentagon]

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.


[Sierpinski pentagon]

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.


[Sierpinski pentagon]

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.


[Sierpinski pentagon]

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.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in drawing, fractals, graphics, recursion and tagged , , , , , , , , , , . Bookmark the permalink.

6 Responses to Draw a Sierpinski pentagon in C#

  1. Marino Liranzo says:

    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.

    • RodStephens says:

      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.

  2. Eddie Bole says:

    Hi Rod
    Thanks for replying in the other posts. Much appreciated (Sorry about calling you Steven in the earlier posts, I had a brain fuzz. Silly me). In this pentagon fractal example, how could one add a change fill color functionality by left clicking one of say 6 small square arrayed picture boxes of different colors (like a small selective palette). Also by right clicking on one of the 6 squares one changes the border color of the pentagons (I know,in Vb6 one detects the point pixel color clicked and then assigns the fore color and back color of the picture box to the one clicked and then perform a refresh). I feel like I’m asking some really basic questions, but I’m slowly gaining knowledge of Csharp. Your explanation of labeling the top columns of the histogram was done well. I need to play around with that code and experiment some more. Regard Eddie.

  3. Pingback: Draw a colored Sierpinski pentagon in C# - C# HelperC# Helper

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.