Title: Connect two points with arcs of midpoint circles in C#
Two points alone cannot uniquely define a circle because an infinite number of circles can pass through two points and therefore they can be connected by an infinite number of arcs. If you add extra information, however, two points can uniquely define a circle and hence arcs connecting the points. This example finds the circle that intersects two points and that has a center at the points' midpoint.
The example performs two main tasks: letting the user pick the two points and finding the arcs.
Picking Points
The following code shows how the program lets the user pick two points.
private List<PointF> Points = new List<PointF>();
private void picCanvas_MouseClick(object sender, MouseEventArgs e)
{
if (Points.Count == 2) Points = new List<PointF>();
Points.Add(e.Location);
picCanvas.Refresh();
}
The program stores the points in the Points list. When the user clicks on the program's PictureBox, the MouseClick event handler sees how many points are already in the list. If the list is full, the program creates a new list.
The event handler then saves the new point into the list and refreshes the PictureBox.
The program uses the following Paint event handler to draw the points and their connecting arcs.
private void picCanvas_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.Clear(picCanvas.BackColor);
// Find the arcs.
if (Points.Count == 2)
{
// Find the arcs' bounding rectangles.
RectangleF rect1, rect2;
float start_angle1, start_angle2, sweep_angle1, sweep_angle2;
FindMidpointArcs(Points[0], Points[1],
out rect1, out rect2,
out start_angle1, out start_angle2,
out sweep_angle1, out sweep_angle2);
// Draw the bounding rectangles.
using (Pen pen = new Pen(Color.Red, 0))
{
// Draw the bounding rectangles.
pen.DashPattern = new float[] { 1, 2 };
e.Graphics.DrawRectangle(pen, rect1);
pen.Color = Color.Green;
e.Graphics.DrawRectangle(pen, rect2);
// Draw the bounding ellipses.
pen.DashPattern = new float[] { 5, 5 };
e.Graphics.DrawEllipse(pen, rect1);
pen.Color = Color.Green;
e.Graphics.DrawEllipse(pen, rect2);
}
// Draw the arcs.
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawArc(pen, rect1, start_angle1, sweep_angle1);
pen.Color = Color.Green;
e.Graphics.DrawArc(pen, rect2, start_angle2, sweep_angle2);
}
}
// Draw the points.
foreach (PointF point in Points)
DrawPoint(e.Graphics, Brushes.Green, point);
}
The Paint event handler is relatively straightforward. If the Points list contains two points, the code calls the FindMidpointArcs method to get the arc data. It then draws the arcs' bounding rectangles with dotted lines, the ellipses that contain the arcs with dashed lines, and finally the arcs with solid lines.
After it has drawn the arcs, the code loops through any points in the Points list and draws them.
Download the example program for additional details.
Finding the Arcs
Finding the circle that intersects the points is easy. First find the midpoint between the two points. The distance from that midpoint to either of the original points gives the circle's radius. Alternatively you can calculate the distance between the two original points and divide by two.
You can then use the arctangent of the angle from the midpoint to the original points to find the angles.
The example program uses the following FindMidpointArcs method to find the arcs connecting the points.
// Find the arcs connecting two points
private void FindMidpointArcs(PointF point0, PointF point1,
out RectangleF rect1, out RectangleF rect2,
out float start_angle1, out float start_angle2,
out float sweep_angle1, out float sweep_angle2)
{
// Find the midpoint.
float cx = (point0.X + point1.X) / 2f;
float cy = (point0.Y + point1.Y) / 2f;
// Find the radius.
float radius = (float)(Distance(point0, point1) / 2.0);
// Create the rectangles.
rect1 = new RectangleF(
cx - radius, cy - radius,
2 * radius, 2 * radius);
rect2 = rect1;
// Find the start angles.
start_angle1 = (float)(180 / Math.PI * Math.Atan2(
point1.Y - point0.Y,
point1.X - point0.X));
start_angle2 = (start_angle1 + 180) % 360;
sweep_angle1 = 180;
sweep_angle2 = 180;
}
This method returns information needed to draw arcs connecting two points. To draw an arc, the Graphics class's DrawArc method uses a rectangle bounding the ellipse that contains the arc, a start angle, and a sweep angle. The method returns those values through output parameters.
This method first averages the points' X and Y coordinates to find the midpoint between the two points. To find the circle's radius, the code calculates the distance between the two original points and divides by two. (The Distance method simply calculates the distance between two points. It's straightforward so it isn't shown here.)
The code then uses the midpoint and radius to define the rectangle that bounds the midpoint circle. The two arcs that connect the points lie on the same circle, so the two bounding rectangles are the same.
The method then uses Math.Atan2 to find the angle from the circle's center to the first point. The Math.Atan2 method returns the angle in radians but the Graphics.DrawArc method uses degrees, so the code converts the result into degrees.
To calculate the second angle, the code adds 180 degrees and takes the result modulus 360 so the final angle lies between 0 and 360. (It actually lies between
The last piece of information that the Graphics.DrawArc method needs to draw an arc is the arc's sweep angle. For midpoint circles, that angle is always 180 degrees, so the method sets sweep_angle1 and sweep_angle2 equal to 180.
Conclusion
Although you cannot uniquely define arcs connecting two points, you can define arcs that lie on a circle with center at the points' midpoint. In my next post I'll show another way to define arcs that connect two points. Meanwhile download the example to see additional details and to experiment with it.
Download the example to experiment with it and to see additional details.
|