[C# Helper]
Index Books FAQ Contact About Rod
[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

[C# 24-Hour Trainer]

[C# 5.0 Programmer's Reference]

[MCSD Certification Toolkit (Exam 70-483): Programming in C#]

Title: Draw a colored Sierpinski pentagon in C#

[Draw a colored Sierpinski pentagon in C#]

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 experiment with it and to see additional details.

© 2009-2023 Rocky Mountain Computer Consulting, Inc. All rights reserved.