/// <summary> /// Creates an arc with the given center, start, and end points. Performs a check /// at the given precision to make sure that both the start and end points are /// the same distance from the center, throwing an exception if they're not. Note /// that because radius is generated from start point, the resultant arc's start /// point will be more accurate than the end point. /// </summary> /// <param name="center">Center of the arc.</param> /// <param name="startPoint">Start point of the arc.</param> /// <param name="endPoint">Point that end angle is projected through.</param> /// <param name="decimals">Precision at which to check the distance of the endpoints to the center. /// A negative value will perform an exact check.</param> public Arc2D(Point2D center, Point2D startPoint, Point2D endPoint, int decimals = 15) : this(center, center.DistanceTo(startPoint), 0, 0) { if ((decimals < 0 && center.DistanceTo(endPoint) != Radius) || (decimals >= 0 && center.DistanceTo(endPoint).RoundFromZero(decimals) != Radius.RoundFromZero(decimals))) { throw new Exception("Can't create arc from center and endpoints because endpoints have different distances from the center!"); } _startAngle = Circle.AngleThroughPoint(startPoint); _endAngle = Circle.AngleThroughPoint(endPoint); }
/// <summary> /// Gets the two line segments that start at the given point and terminate /// on the circle and are tangent to the circle. /// </summary> /// <param name="point">The point for the tangent segments to go through.</param> /// <remarks> /// Got strategy from Wikipedia's description involving Thale's theorem. /// http://en.wikipedia.org/wiki/Tangent_lines_to_circles /// </remarks> public LineSeg2D[] TangentsThroughPoint(Point2D point) { Circle2D intersectingCircle = default(Circle2D); Point2D[] intersects = null; if (point.DistanceTo(this.Center) <= this.Radius) { return(null); } intersectingCircle = new Circle2D(this.Center, point); intersects = this.GetIntersect(intersectingCircle); if (intersects.Length != 2) { throw new Exception("Unexpected number of intersects when getting tangents through a point."); } return(new LineSeg2D[] { new LineSeg2D(point, intersects[0]), new LineSeg2D(point, intersects[1]) }); }
/// <summary> /// Returns the point on the line segment closest to the given point. Can also /// return the closest point on the line through the line segment. /// </summary> /// <param name="pt">A 2D point.</param> /// <param name="treatLineSegmentAsLine">If True, will return closest point on /// the line through the line segment even if it doesn't lie on the segment /// itself. If False, will find the closest point on the segment itself.</param> public Point2D ClosestPointTo(Point2D pt, bool treatLineSegmentAsLine = false) { _CheckIsValid(); LineSeg2D l = default(LineSeg2D); Point2D ret = default(Point2D); if (this.IsHorizontal) { ret = new Point2D(pt.X, this.Pt1.Y); } else { l = FromPointSlope(pt, this.PerpendicularSlope); ret = this.GetIntersect(l, true).Value; } if (!treatLineSegmentAsLine && !this.IsInBounds(ret)) { if (pt.DistanceTo(Pt1) < pt.DistanceTo(Pt2)) { ret = Pt1; } else { ret = Pt2; } } return(ret); //' Alt method using vector project //' http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html //Dim v As Vector2D //Dim r As Vector2D //Dim proj As Vector2D //Dim ret As XYPoint //v = New Vector2D(Pt2.Y - Pt1.Y, -(Pt2.X - Pt1.X)) //r = New Vector2D(Pt1.X - pt.X, Pt1.Y - pt.Y) //proj = r.ProjectOnto(v) //ret = pt + proj //' End alt method //' Alt method using vectors //' http://www.gamedev.net/community/forums/topic.asp?topic_id=198199&whichpage=1� //' Not used because of round off problems that affect precision. //Dim d As Decimal //Dim t As Decimal //Dim aToB As Vector2D //Dim aToPt As Vector2D //aToB = (New Vector2D(Pt1, Pt2)).Normalize //aToPt = New Vector2D(Pt1, pt) //d = Me.Length //t = aToB.Dot(aToPt) //If Not treatLineSegmentAsLine Then // If t < 0 Then // ret = Pt1 // ElseIf t > d Then // ret = Pt2 // Else // ret = Pt1 + aToB * t // End If //Else // If Not (Pt1 + aToB * t).Equals(ret, 23) Then Stop // ret = Pt1 + aToB * t //End If //' End alt method }
/// <summary> /// Finds the closest distance between a point and this line segment or /// the line through this line segment. /// </summary> /// <param name="pt">A 2D point.</param> /// <param name="treatLineSegmentAsLine">If True, will return distance to closest /// point on the line through the line segment. If False, will return distance to the /// closest point on the segment itself.</param> public decimal DistanceTo(Point2D pt, bool treatLineSegmentAsLine = false) { return(pt.DistanceTo(this.ClosestPointTo(pt, treatLineSegmentAsLine))); }