Title: Draw an Archimedes spiral in C#
An archimedes spiral is defined by the polar coordinate equation r = A * θ. It's just as simple as that, and this would be a significantly shorter post except for one question: how do you know how big you need to make θ to make the spiral fill the drawing area? You cannot simply continue drawing the spiral until it leaves the drawing area because it comes back as it cuts through the drawing area's corners.
For example, in the picture at the top of this post, the red spiral leaves the black rectangle, cuts back into it before reaching the next corner, leaves again shortly after that corner, misses a corner entirely, cuts the next two corners, and then grows too large to intersect the rectangle after that.
The distance from a point on the spiral to the spiral's center is simply r. In the Archimedes spiral, r increases as θ increases, so once r is too big to intersect the drawing area, it never goes back, and that gives us the test we need. Simply find the corner of the drawing area that is farthest from the spiral's center. When r is greater than the distance to that corner, the spiral has gone far enough.
The example program uses the following method to generate a spiral's points.
// Return points that define a spiral.
private List<PointF> GetSpiralPoints(
PointF center, float A,
float angle_offset, float max_r)
{
// Get the points.
List<PointF> points = new List<PointF>();
const float dtheta = (float)(5 * Math.PI / 180); // Five degrees.
for (float theta = 0; ; theta += dtheta)
{
// Calculate r.
float r = A * theta;
// Convert to Cartesian coordinates.
float x, y;
PolarToCartesian(r, theta + angle_offset, out x, out y);
// Center.
x += center.X;
y += center.Y;
// Create the point.
points.Add(new PointF((float)x, (float)y));
// If we have gone far enough, stop.
if (r > max_r) break;
}
return points;
}
This method uses a loop to generate the points on the spiral. It calculates r, uses the PolarToCartesian method to convert the polar coordinates (r, θ) into Cartesian coordinates (x, y), adds the spiral's center to the point to translate it into the correct position, and then adds the new point to the list.
Notice that the code adds the angle offset to theta when it calls PolarToCartesian. That makes the spiral start in a particular direction so the program can draw multiple interlaced spirals.
Next, if r is greater than the distance to the farthest corner of the drawing rectangle max_r, the code breaks out of its loop and returns the spiral's points.
The following code shows the PolarToCartesian method.
// Convert polar coordinates into Cartesian coordinates.
private void PolarToCartesian(float r, float theta,
out float x, out float y)
{
x = (float)(r * Math.Cos(theta));
y = (float)(r * Math.Sin(theta));
}
This method simply converts polar coordinates into Cartesian coordinates.
The only remaining code of any real interest is the following
// Pens to use for different spirals.
private Pen[] SpiralPens =
{
Pens.Red, Pens.Green, Pens.Purple,
Pens.Blue, Pens.Magenta,
};
// 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_r = Distance(center, rect);
// Draw the spirals.
for (int i = 0; i < num_spirals; i++)
{
List<PointF> points =
GetSpiralPoints(center, A, start_angle, max_r);
e.Graphics.DrawLines(SpiralPens[i % SpiralPens.Length],
points.ToArray());
start_angle += d_start;
}
// Draw the target rectangle.
e.Graphics.DrawRectangle(Pens.Black, rect);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This code defines an array of colors to use when drawing spirals.
The picSpiral control's Paint event draws the spirals. It gets the user-entered parameters. It then divides 2π radians by the number of spirals to get the angular spacing between the interlocked spirals.
The code finds the center of the PictureBox and uses it as the center of its spirals. It then draws axes running through that center and makes a test rectangle where it will fit the spirals.
Next, the code calls the Distance method to find the distance to the drawing area's farthest corner. It then enters a loop to draw each of the spirals, increasing each one's angle offset by d_start so they start out pointing in different directions. The code calls the GetSpiralPoints method to get the spiral's points and then uses the Graphics object's DrawLines method to draw the spiral.
The code finishes by drawing the target rectangle so you can see that the spirals are big enough to cover the rectangle.
The program includes a few other details such as the Distance method that are relatively straightforward. Download the example program to see additional details.
Download the example to experiment with it and to see additional details.
|