See whether a point is above an arc in C#

This example shows how you can determine which part of an arc is at a particular position. If you move the mouse over the arc’s start or end point, the program changes its cursor to the PanWest and PanEast cursors. If you move the mouse over the arc’s body, the program changes its cursor to the SizeAll cursor. When the mouse is not over any part of the arc, the program displays the default cursor.

The program identifies the arc’s parts in two main steps. When it starts, it defines the arc and finds its end points. Later, when you move the mouse, the program determines which part, if any, is under the mouse.

Initialization

When it starts, the program uses the following code to define its arc.

// The arc's geometry.
private RectangleF ArcBounds;
private float StartAngle, SweepAngle;
private PointF[] EndPoints;

// Define the arc.
private void Form1_Load(object sender, EventArgs e)
{
// Define the arc.
ArcBounds = new RectangleF(20, 20,
picCanvas.ClientSize.Width - 40,
picCanvas.ClientSize.Height - 40);
StartAngle = 45;
SweepAngle = 255;

// Get the arc's end points.
EndPoints = FindArcEndPoints(ArcBounds,
StartAngle, StartAngle + SweepAngle);
}

The form’s Load event handler sets the ArcBounds rectangle that defines the arc that bounds the ellipse containing the arc. It sets the StartAngle and SweepAngle to determine the arc’s part of the ellipse.

The program then calls the FindArcEndPoints method to find the arc’s start and end points. That method is the same as the FindEllipsePoints method described in the post Make an intuitive extension method to draw an elliptical arc in WPF and C#. See that post for a description.

Finding Arc Pieces

When you move the mouse over the program’s PictureBox, the following event handler executes.

// See what's under the mouse.
private void picCanvas_MouseMove(object sender, MouseEventArgs e)
{
switch (ArcPartAtPoint(ArcBounds,
EndPoints[0], EndPoints[1], e.Location, 3))
{
case Part.None:
picCanvas.Cursor = Cursors.Default;
break;
case Part.Body:
picCanvas.Cursor = Cursors.SizeAll;
break;
case Part.StartPoint:
picCanvas.Cursor = Cursors.PanWest;
break;
case Part.EndPoint:
picCanvas.Cursor = Cursors.PanEast;
break;
default:
throw new Exception("Unknown arc part");
}
}

This code calls the ArcPartAtPoint method described next. It sets the PictureBox control’s cursor appropriately for the part of the arc under the mouse.

The most interesting piece in the rest of the program is the ArcPartAtPoint method.

// Pieces of the arc that the mouse might be over.
public enum Part
{
None,
StartPoint,
EndPoint,
Body,
}

// Return the part of the arc that is at the given point.
public Part ArcPartAtPoint(RectangleF arc_bounds,
PointF start_point, PointF end_point,
{
// See if the mouse is at the start or end point.
return Part.StartPoint;
return Part.EndPoint;

// Find the angle from the ellipse's center to the point.
float cx = (arc_bounds.Left + arc_bounds.Right) / 2f;
float cy = (arc_bounds.Top + arc_bounds.Bottom) / 2f;
float dx = target.X - cx;
float dy = target.Y - cy;
float degrees = (float)(radians * 180 / Math.PI);
while (degrees < 0) degrees += 360;

// Get the arc's minimum and maximum angles.
float min_angle = Math.Min(StartAngle, StartAngle + SweepAngle);
float max_angle = Math.Max(StartAngle, StartAngle + SweepAngle);

// Make degrees be the smallest value
// greater than or equal to min_angle.
while (degrees > min_angle) degrees -= 360;
while (degrees < min_angle) degrees += 360;

// See if the angle is within the ellipse's sweep.
if ((degrees < min_angle) ||
(degrees > max_angle))
return Part.None;

// Find the point on the ellipse at this angle.
PointF center = new PointF(cx, cy);
PointF[] intersections =
FindEllipseSegmentIntersections(
arc_bounds, center, target, false);
for (int i = 0; i < intersections.Length; i++)
return Part.Body;

// It's not on the arc.
return Part.None;
}

This code first defines the Part enumeration to identify the parts of an arc.

The ArcPartAtPoint method first uses the Distance helper method to find the distance between the target point and the arc’s end points. If the distance is less than the desired radius, then the method returns a value indicating which end point is at the target point. (The Distance method is straightforward so it isn’t shown here.)

Next, the method finds the angle from the arc’s center to the target point. If that angle is not between the arc’s start angle and its end angle, the method returns Part.None to indicate that the target point does not lie on the ellipse. (Note that this assumes that the start angle lies between 0 and 360 degrees.)

The method the uses the FindEllipseSegmentIntersections method to find the points where the line between the arc’s center and the target point intersect the arc’s ellipse. If one of those points of intersection lies within distance radius of the target point, then the target point lies on (or at least very close to) the arc’s body, so the method returns Part.Body.

Note that the point of intersection may not be the point on the arc that is closest to the target point. Unless the arc is perpendicular to the line, the closest point will be some other nearby point. However, if the radius we are using to test for closeness is reasonably small, then the result should be reasonably accurate. Let me know if you find examples where that isn’t true.

Download the example to see additional details. In my next few posts, I’ll explain how you can let the user draw and modify arcs.

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in drawing, graphics, mathematics and tagged , , , , , , , , , , . Bookmark the permalink.

3 Responses to See whether a point is above an arc in C#

1. ramazan says:

startangle =170
sweepangle = -330 or

startangle =330
sweepangle = 110

not working

• RodStephens says:

Thanks for pointing this out!

Yes, the code was adjusting the mouse’s angle so it lay between 0 and 360 degrees. If the arc spans 0 or 360, then parts of it lie outside of those bounds.

I’ve changed the code that determines whether the mouse’s angle lies within the arc’s sweep.

// Get the arc's minimum and maximum angles.
float min_angle = Math.Min(StartAngle, StartAngle + SweepAngle);
float max_angle = Math.Max(StartAngle, StartAngle + SweepAngle);

// Make degrees be the smallest value
// greater than or equal to min_angle.
while (degrees > min_angle) degrees -= 360;
while (degrees < min_angle) degrees += 360;

// See if the angle is within the ellipse's sweep.
if ((degrees < min_angle) ||
(degrees > max_angle))
return Part.None;

I think it works now.

This site uses Akismet to reduce spam. Learn how your comment data is processed.