Draw a colored Sierpinski pentagon in C#

[example]

The example Draw a Sierpinski pentagon in C# shows how to draw a Sierpinski pentagon. This example modifies that one so the user can click on pentagons to change their colors. To do that, this program takes a very different approach to managing its pentagons. It performs three main tasks: managing pentagons, defining pentagons, and changing pentagon colors.

Managing Pentagons

This example uses a Pentagon class to represent a pentagon that it will draw. The main program keeps a list of Pentagon objects that it can search when the user clicks on the program’s PictureBox.

The following code shows the Pentagon class.

class Pentagon
{
    public PointF[] Points = null;
    public Color FillColor = Color.Black;

    // Constructor.
    public Pentagon(PointF[] points, Color fill_color)
    {
        Points = points;
        FillColor = fill_color;
    }

    // Draw.
    public void Draw(Graphics gr)
    {
        using (Brush brush = new SolidBrush(FillColor))
        {
            gr.FillPolygon(brush, Points);
        }
    }

    // Return true if the pentagon inclides this point.
    public bool Contains(PointF point)
    {
        return PointInPolygon(point);
    }

    #region PointInPolygon Code
    ...
    #endregion PointInPolygon Code
}

The class has a public Points array that holds the points needed to draw the pentagon. Its public FillColor value is the color that should be used to draw the pentagon.

The constructor simply initializes the Points and FillColor values.

The Draw method creates a brush with the correct FillColor and then uses it to fill the pentagon.

The Contains method calls the PointInPolygon method to determine whether the pentagon contain the indicated point.

The PointInPolygon method and some helper methods that it uses are contained in a region that I won’t show here. You can see a full description of those methods in the post Determine whether a point is inside a polygon in C#.

Defining Pentagons

When you change the Depth value in the program’s NumericUpDown control, the program uses the following code to create the Pentagon objects.

// Redraw.
private void nudDepth_ValueChanged(object sender, EventArgs e)
{
    MakePentagons();
}

// The Pentagon objects.
private List Pentagons = new List();

// Make the Pentagon objects and redraw.
private void MakePentagons()
{
    // Build a new list of Pentagons.
    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);
    Pentagons = new List<Pentagon>();
    MakePentagons(depth, center, radius);

    // Redraw.
    picPentagon.Refresh();
}

The NumericUpDown control’s ValueChanged event handler simply calls the MakePentagons method. That method gets the depth selected by the user. It finds the center of the PictureBox and picks a radius that will make the pentagons fit nicely.

The method then calls an overloaded version of the same method to perform the recursion and generate the pentagons. The following code shows the new method.

// Scale factor for moving to smaller pentagons.
private float size_scale = (float)(1.0
    / (2.0 * (1 + Math.Cos(Math.PI / 180 * 72))));

// Recursively generate the Pentagons.
private void MakePentagons(int depth, PointF center, float radius)
{
    // If we are done recursing, add a new Pentagon to the list.
    if (depth <= 0)
    {
        // Find the pentagon's corners.
        Pentagons.Add(new Pentagon(
            GetPentagonPoints(center, radius),
            lblSelected.BackColor));
    }
    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)
        {
            MakePentagons(depth - 1, point, radius * size_scale);
        }
    }
}

The size_scale value indicates the amount by which the pentagons are scaled as the program moves into deeper levels of recursion. See the previous example for details about that value.

This version of the MakePentagons method checks the current depth of recursion. If depth is 0, the method calls the GetPentagonPoints method to define the points for a new pentagon. It passes those points and the currently selected background color into the Pentagon class’s constructor and then adds the new Pentagon object to the Pentagons list. See the previous example for information about the GetPentagonPoints method.

If depth is greater than 0, the method recursively calls itself to generate smaller pentagons.

Changing Pentagon Colors

This program keeps track of its current color by storing it in the Label control named lblSelected. When you click on one of the other color labels on the left side of the program, the following event handler executes.

// Set the selected color.
private void colorLabel_Click(object sender, EventArgs e)
{
    Label clicked_label = sender as Label;
    lblSelected.BackColor = clicked_label.BackColor;
}

This code simply sets the lblSelected control’s BackColor equal to the clicked Label control’s BackColor.

When you click on the form’s PictureBox, the following code sets the color for the selected pentagon.

// Color the clicked Pentagon.
private void picPentagon_MouseClick(object sender, MouseEventArgs e)
{
    // Get the clicked point.
    PointF point = e.Location;

    // Find the clicked Pentagon.
    foreach (Pentagon pentagon in Pentagons)
    {
        if (pentagon.Contains(point))
        {
            // Color this pentagon and redraw.
            pentagon.FillColor = lblSelected.BackColor;
            picPentagon.Refresh();
            return;
        }
    }
}

This code loops through the Pentagon objects in the Pentagons list. It calls each object’s Contains method to see if the clicked point is inside that pentagon. If the pentagon contains the point, the code sets the Pentagon object’s FillColor equal to the currently selected color, refreshes the PictureBox to redraw with the new color, and then returns.

Summary

That’s all there is to this example. The technique of using objects to represent things drawn on the program is fairly common in drawing applications. It’s easy to loop through the objects to see which one the user has clicked.

Note that if you change the program’s depth, it rebuilds its pentagon list from scratch so any previously colored pentagons are lost. You could restructure the example so it saved earlier pentagons. For example, if you previously placed a red pentagon in the upper left corner, then when you went to a deeper level of recursion, you could make that pentagon’s children start out red.

If a program has a lot of objects, it may make sense to use a different data structure to hold the objects. For example, a quadtree divides the drawing area in a hierarchical way so it can identify clicked objects more quickly. (That’s definitely overkill for this program.)

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.

5 Responses to Draw a colored Sierpinski pentagon in C#

  1. Eddie Bole says:

    Hi Rod. Thanks for the nice sample code. You would not believe it, but the hard drive on my computer with CSharp installed has stopped working. I need to get a replacement drive and then reinstall CSharp again. I also noticed that you had some examples of drawing lines and picking colours using a colour select dialogue so I will play around with those as well. What version of Csharp are you using? I had versions 2010 and 2012 installed so hopefully they will be working with your code, when I reinstall Csharp. Regards Eddie.

    • RodStephens says:

      I usually try to use Visual Studio 2008 because (1) it is much faster than more recent versions, (2) the more recent versions add features but very few of them are necessary for most examples, and (3) newer versions are backward compatible but no versions are forward compatible. In other words, VS 2012 will open a VS 2008 project but not vice versa.

  2. Eddie Bole says:

    I also had another idea about making the pattern even move colorful, like giving the biggest pentagon a semi transparent light blue background color, and then giving five smaller pentagons another semitransparent yellow color, giving a window on window see though type effect (with no white inside the biggest pentagon). I tend to experiment I Vb6 code (I upload to Planet Vb), but Csharp graphics looks quite nice and well structured so I’m hoping to learn some CSharp graphics skills. This example should help me with scaling and coloring, so I am very greatful for your good explanations. I am a self taught programmer, who had patiently learn t VB6 from others. Now I want to learn a bit about CSharp and VBnet. So, once again a big thanks for the help provided. Regards Eddie.

    • RodStephens says:

      I actually just wrote something sort of like that. Each Pentagon object has children that are references to the five smaller pentagons that it contains, and all of them have colors. I didn’t use semi-transparent colors, though. I’ll have to give that a try.

  3. Pingback: Draw a randomly 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.