My post Draw an Archimedes spiral in C# explained how to draw multiple Archimedes spirals starting at a common center point. This post shows how you can fill the spaces between the spirals with colors.

The basic idea is to make a polygon that follows the points along one spiral and then comes back along the reversed points from an adjacent spiral. That much would be interesting enough, but there are three details involving alignment, ending distance, and filling that make this extra tricky.

# Alignment

In the previous example, each spiral expands until it its radius was larger than the distance from the spiral’s center to the farthest corner of the drawing area. Because the spirals use different offsets, they span different angles. That means the end of one spiral may be far from the beginning of the next. The picture on the right shows the result. It’s interesting, but not what we’re after.

One solution is to calculate the largest angle `max_theta` that any spiral must reach to surround the drawing area. Then you can generate points on each spiral until the variable `theta` plus the spiral’s offset exceeds `max_theta`.

The equation for an Archimedes spiral is r = A * θ, so solving for θ gives θ = r / A. If we plug in the distance to the farthest corner of the drawing area, we get `max_theta = max_distance / A`.

Now if each spiral ends when its `theta + offset` values exceeds `max_theta`, then the spiral ends will be near each other so they should form usable polygons. That leads to the second problem.

# Ending Distance

In the earlier example, a spiral expanded until its radius was larger than the distance from the spiral’s center to the farthest corner of the drawing area. The picture on the right shows what happens if the spirals are only extended that far. This problem actually has two features.

First, some of the spirals don’t go far enough. If you look closely at the picture, you can probably guess that purple band should extend and cut into the drawing rectangle again in its lower left corner. The problem is that this spiral starts pointing upward, so doesn’t go as far around the circle as the red band, which starts pointing to the right.

A second potential problem is that the filled areas must include the area outside of the outermost spiral. If not only need to extend the second spiral to define the outer

edge of the purple band, we also need to extend the second and possibly third spirals to define the outer edges of the other bands.

We can do all of that quite easily by adding 2 π to `max_theta` to make every spiral take another lap around the center.

# Filling

The final problem deals with the way the bands are filled. The obvious approach is to fill the area between spiral i and spiral i + 1. You can use the modulus operator (%) to wrap around to the beginning when you reach the last spiral. Unfortunately, even though the spirals all start at the same center, the first spiral isn’t adjacent to the last one. If you fill the area between those two spirals, you get the picture on the right.

The example solves this problem by simply creating one extra spiral outside all of the others. For example, if you want to draw three spirals, then the program creates four. Then, instead of filling the area between the first and last spirals, it fills the area between spiral number three and spiral number four.

# The Program

This example uses the `GetSpiralPoints` method to generate the points on a spiral. That method is similar to the one used by the previous example. The only difference is that the previous version expands the spiral until r is big enough to cover the drawing rectangle, while the new version expands the spiral until `theta` reaches `max_theta`. See the previous example for a description of that method.

This example’s other main piece of code is the following `Paint` event handler.

// Draw the spiral(s). private void picSpiral_Paint(object sender, PaintEventArgs e) { e.Graphics.Clear(picSpiral.BackColor); e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; try { float A = float.Parse(txtA.Text); int num_spirals = int.Parse(txtNumSpirals.Text); // Angular spacing between different spirals. float d_start = (float)(2 * Math.PI / num_spirals); // The angle where the next spiral starts. float start_angle = 0; // Center point. PointF center = new PointF( picSpiral.ClientSize.Width / 2, picSpiral.ClientSize.Height / 2); // Draw axes. e.Graphics.DrawLine(Pens.Black, center.X, 0, center.X, picSpiral.ClientSize.Height); e.Graphics.DrawLine(Pens.Black, 0, center.Y, picSpiral.ClientSize.Width, center.Y); // Draw the spiral on only part of the PictureBox. Rectangle rect = new Rectangle(25, 50, 150, 150); // Find the maximum distance to the rectangle's corners. float max_dist = Distance(center, rect); // Calculate the maximum theta value that we need to go to. float max_theta = max_dist / A + 2 * (float)Math.PI; // Get points defining the spirals. List<List<PointF>> spiral_points = new List<List<PointF>>(); // Get the spirals' points. for (int i = 0; i < num_spirals + 1; i++) { spiral_points.Add(GetSpiralPoints( center, A, start_angle, max_theta)); start_angle += d_start; } // Fill the areas between the spirals. for (int i = 0; i <= num_spirals; i++) { // Make a list holding the next spiral's points. List<PointF> points = new List<PointF>(spiral_points[i]); // Add the following spiral's points reversed. List<PointF> points2 = new List<PointF>(spiral_points[i + 1]); points2.Reverse(); points.AddRange(points2); e.Graphics.FillPolygon( SpiralBrushes[i % SpiralBrushes.Length], points.ToArray()); // Optional: Outline the spiral's polygon. using (Pen pen = new Pen(Color.Black, 1)) { e.Graphics.DrawLines(pen, points.ToArray()); } } // Draw the target rectangle. e.Graphics.DrawRectangle(Pens.Black, rect); } catch (Exception ex) { MessageBox.Show(ex.Message); } }

This method reads the parameters that you entered and calculates the angular offsets between the spirals as in the previous example. It also finds the drawing area’s center point, draws axes, and creates a drawing rectangle as before.

The program then finds the maximum distance from the spiral’s center to the corners of the drawing area. Next, it plugs `r = max_dist` in the equation `r = a * theta` and solves for `theta`. That gives it the largest value of `theta` that it needs to use. It saves that value in `max_theta`.

Next, the code creates a list of lists to hold the points for the spirals. It then enters a loop where it calls `GetSpiralPoints` to find each spiral’s points and it adds them to the list. Notice that in this loop `i` runs from 0 to `num_spirals` so it creates `num_spirals + 1` spirals.

After it has all of the spirals’ points, the method uses a loop to fill the spaces between them as. As described earlier, it fills the areas between adjacent spirals up to the extra spiral that it created at the end of the others.

To fill an area, the code makes a new `points` list initialized with the points for spiral number `i`. It then creates another list `points2` holding the next spiral’s points, reverses that list, and add it to the end of the `points` list. The code then fills that area and optionally outlines it.

The program includes a few other details that are relatively straightforward. Download the example program to see those details.

Hi Rod.

I made and uploaded a clockwise and anticlockwise vb6 spiral maker to planet vb (CodeId=75155) [the page is here–Rod] but yours is much more colorful. I was wondering how could one slightly alter the fill color effect to have a slight color gradient ( light colored in the middle of each line stroke and darker at both side edges of each line in the spriral?

That’s a tough one. I would like to use the

PathGradientBrush, but it assumes that the the gradient flows from a center point to the edges of a path and here we want a gradient that flows from one curve to another.All I can think of off hand would be to do the fill yourself one pixel at a time, and that would be really slow. It would also be really cool, though, so I may give it a try when I have time.

Pingback: Sprialize an image in C# - C# HelperC# Helper