Title: Use sines and cosines to draw circles and ellipses in C#
This example shows how you can use trigonometric functions to draw circles. It's such a basic technique that I sometimes forget that it's not completely obvious to everyone.
Given a radius R and an angle θ, the equations x = R * cos(θ) and y = R * sin(θ) give the coordinates of a point on a circle of radius R centered at the origin. The picture on the right shows how the equations define the point's location.
To trace out a circle, you can connect the points that you get when you let θ vary from 0 to 2 π. If you scale the X and Y coordinates by different amounts, you can make an ellipse. Finally, if you add offsets to the X and Y coordinates, you can move the result so it is centered somewhere other than at the origin.
This example draws two ellipses with tick marks. The key is the following method, which draws an ellipses with tick marks.
// Draw an ellipse with tick marks.
private void DrawTickedCircle(
Graphics gr, Pen circle_pen, Pen tick_pen,
float cx, float cy, float rx, float ry,
float num_theta,
float num_ticks, float tick_fraction)
{
// Draw the circle.
List<PointF> points = new List<PointF>();
float dtheta = (float)(2 * Math.PI / num_theta);
float theta = 0;
for (int i = 0; i < num_theta; i++)
{
float x = (float)(cx + rx * Math.Cos(theta));
float y = (float)(cy + ry * Math.Sin(theta));
points.Add(new PointF(x, y));
theta += dtheta;
}
gr.DrawPolygon(circle_pen, points.ToArray());
// Draw the tick marks.
dtheta = (float)(2 * Math.PI / num_ticks);
theta = 0;
float rx1 = rx * (1 - tick_fraction);
float ry1 = ry * (1 - tick_fraction);
for (int i = 0; i < num_ticks; i++)
{
float x1 = (float)(cx + rx * Math.Cos(theta));
float y1 = (float)(cy + ry * Math.Sin(theta));
float x2 = (float)(cx + rx1 * Math.Cos(theta));
float y2 = (float)(cy + ry1 * Math.Sin(theta));
gr.DrawLine(tick_pen, x1, y1, x2, y2);
theta += dtheta;
}
}
The method first draws the circle. To do that, it creates a List<PointF> to hold the points that it will connect. It then calculates dtheta, the amount by which it must increment angle theta to make it generate the desired number of points around the circle. The code initializes variable theta to 0 and then loops over the desired number of points.
For each value of i, the code calculates the point's coordinates and adds them to the points list. It then adds dtheta to theta to move to the next angle.
After it has generated all of the points, the method uses the DrawPolygon method to connect them.
Next, the method draws the tick marks. It calculates a new value for dtheta. It also calculates X and Y radii that are a fraction of the full radii rx and ry. For example, to make a tick mark that covers 10 percent of the distance from the edge of the circle to its center, you would set the parameter tick_fraction to 0.1. The code subtracts that value from 1 to get a new radius that is inside the circle's full radius.
The code then loops through the desired points and theta values as before. This time it uses the two sets of radii values {rx, ry} and {rx1, ry2} to find two points at the same angle but with different distances from the circle's center. After it calculates each pair of points, the code connects them to make a tick mark.
The following code shows the form's Paint event handler, which calls the DrawTickedCircle method.
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
// Draw a circle.
float cx = ClientRectangle.Width / 2f;
float cy = ClientRectangle.Height / 2f;
float rx = Math.Min(cx, cy) - 10;
float ry = rx;
DrawTickedCircle(e.Graphics, Pens.Blue, Pens.Orange,
cx, cy, rx, ry, 100, 12, 0.1f);
// Draw an ellipse.
rx *= 0.8f;
ry *= 0.6f;
DrawTickedCircle(e.Graphics, Pens.Red, Pens.Black,
cx, cy, rx, ry, 16, 16, 0.1f);
}
This code finds the center of the form (cx, cy). It sets rx and ry so the first circle is drawn 10 pixels from the edge of the form's sides or top and bottom, whichever is smaller. It then calls DrawTickedCircle to draw a circle that uses 100 points to make the circle and 12 tick marks.
The code then scales the rx and ry values and calls DrawTickedCircle again. This time rx and ry are not the same, so the result is an ellipse.
The ellipse also shows an interesting optical illusion that makes the polygon's edges appear to bend inward. If you hold a straight edge up against the screen, you can see that the ellipse's sides are straight.
There are many ways that you can use this basic technique to build shapes other than circles and ellipses. For example, you can use only a few points to draw a regular polygon, or you can connect points that don't lie next to each other to draw stars. (See Draw a star in C# and Draw stars inside polygons in C#.) If you change the radius R for each point, you can draw spirals. (See Draw an Archimedes spiral in C#, Draw a logarithmic spiral in C#, and Draw a hyperbolic spiral in C#.)
Download the example to experiment with it and to see additional details.
|