/// <summary> /// Returns circles that are tangent to another circle and line. /// </summary> /// <param name="circle">Circle that returned circles should be tangent to.</param> /// <param name="line">Line that returned circles should be tangent to.</param> /// <param name="radius">Radius of new circles.</param> /// <returns>External tangent circles are listed first.</returns> public static Circle2D[] FromTangentTangentRadius(Circle2D circle, LineSeg2D line, decimal radius) { List <Circle2D> orbits = new List <Circle2D>(); LineSeg2D[] parallels = new LineSeg2D[2]; List <Point2D> centerPoints = new List <Point2D>(); List <Circle2D> tangentCircles = new List <Circle2D>(); orbits.Add(new Circle2D(circle.X, circle.Y, circle.Radius + radius)); if (radius < circle.Radius) { orbits.Add(new Circle2D(circle.X, circle.Y, circle.Radius - radius)); } parallels = line.GetParallels(radius); foreach (Circle2D orbit in orbits) { for (int i = 0; i <= 1; i++) { centerPoints.AddRange(orbit.GetIntersect(parallels[i])); } } for (int i = 0; i <= centerPoints.Count - 1; i++) { tangentCircles.Add(new Circle2D(centerPoints[i].X, centerPoints[i].Y, radius)); } return(tangentCircles.ToArray()); }
/// <summary> /// Returns circles that are tangent to two lines. /// </summary> /// <param name="lineA">First line.</param> /// <param name="lineB">Second line.</param> /// <param name="radius">Radius of the tangent circles.</param> /// <returns>Will return no circles if the two lines are parallel or the same line.</returns> public static Circle2D[] FromTangentTangentRadius(LineSeg2D lineA, LineSeg2D lineB, decimal radius) { List <LineSeg2D> parallels = new List <LineSeg2D>(); Nullable <Point2D> centerPoint = default(Nullable <Point2D>); List <Point2D> centerPoints = new List <Point2D>(); List <Circle2D> tangentCircles = new List <Circle2D>(); parallels.AddRange(lineA.GetParallels(radius)); parallels.AddRange(lineB.GetParallels(radius)); for (int i = 0; i <= parallels.Count - 2; i++) { for (int j = i + 1; j <= parallels.Count - 1; j++) { centerPoint = parallels[i].GetIntersect(parallels[j], true); if (centerPoint.HasValue) { centerPoints.Add(centerPoint.Value); } } } for (int i = 0; i <= centerPoints.Count - 1; i++) { tangentCircles.Add(new Circle2D(centerPoints[i].X, centerPoints[i].Y, radius)); } return(tangentCircles.ToArray()); }
public static Arc2D FromLinesCircle(LineSeg2D line1, LineSeg2D line2, Circle2D c, int decimals = -1) { Point2D[] intersects1 = null; Point2D[] intersects2 = null; intersects1 = c.GetIntersect(line1, decimals); intersects2 = c.GetIntersect(line2, decimals); throw new NotImplementedException(); }
public static bool PointsAreColinear(decimal x1, decimal y1, decimal x2, decimal y2, decimal x3, decimal y3) { var l1 = new LineSeg2D(x1, y1, x2, y2); var l2 = new LineSeg2D(x2, y2, x3, y3); var intersect = l1.GetIntersect(l2, true); // Points won't have an intersection if they are parallel. If they // are parallel and share a point, then they are colinear. return(!intersect.HasValue); }
/// <summary> /// Returns a line segment with the coordinates of a chord centered on /// the given angle with the given length. /// </summary> /// <param name="angleThroughCenter">The angle that passes through the /// center of the chord, in degrees.</param> /// <param name="chordLength">The length of the chord.</param> public LineSeg2D GetChord(decimal angleThroughCenter, decimal chordLength) { LineSeg2D l = default(LineSeg2D); decimal a = 0m; // Create a right triangle where one side is half the chord length, // the other side is from the center of the circle to the midpoint // of the chord, and the hypotenuse is from the center of the circle // to the chord start point a = RightTriangle.GetAngleFromOppSideHyp(0.5m * chordLength, _radius); l.Pt1 = PointAt(angleThroughCenter - a); l.Pt2 = PointAt(angleThroughCenter + a); return(l); }
/// <summary> /// Create a circle from two points. /// </summary> /// <param name="pt1">A point.</param> /// <param name="pt2">A point.</param> public Circle2D(Point2D pt1, Point2D pt2) { _center = default(Point2D); _radius = 0m; LineSeg2D l = new LineSeg2D(pt1, pt2); if (l.IsZeroLength()) { throw new Exception("Can't create circle from two points that are the same point!"); } Center = l.MidPoint; Radius = l.Length / 2m; if (Radius == 0) { throw new Exception("Can't create circle from two points that are the same point!"); } }
/// <summary> /// Gets a line segment of length 1 starting at the point on the circle /// and extending either clockwise or counterclockwise. /// </summary> /// <param name="degrees">An angle.</param> public LineSeg2D TangentAt(decimal degrees, decimal length, bool clockwise) { var r = RightTriangleAbstract.FromTwoSides(_radius, length); decimal rad = DecimalEx.ToRad(degrees + r.AngleA); LineSeg2D ret = default(LineSeg2D); Debug.Assert(r.AngleA == RightTriangle.GetAngleFromSides(length, _radius)); Debug.Assert(r.Hypotenuse == RightTriangle.GetHypFromSides(length, _radius)); ret.Pt1 = PointAt(degrees); if (!clockwise) { ret.Pt2 = new Point2D(_center.X + r.Hypotenuse * DecimalEx.Cos(rad), _center.Y + r.Hypotenuse * DecimalEx.Sin(rad)); } else { ret.Pt2 = new Point2D(ret.Pt1.X - (_center.X + r.Hypotenuse * DecimalEx.Cos(rad) - ret.Pt1.X), ret.Pt1.Y - (_center.Y + r.Hypotenuse * DecimalEx.Sin(rad) - ret.Pt1.Y)); } return(ret); }
public static Circle2D[] FromTwoPointsAndRadius(decimal x1, decimal y1, decimal x2, decimal y2, decimal radius) { LineSeg2D pointToPoint = default(LineSeg2D); Point2D midPoint = default(Point2D); Vector2D vPerpBisector = default(Vector2D); if (x1 == x2 && y1 == y2) { return new Circle2D[] { } } ; pointToPoint = new LineSeg2D(x1, y1, x2, y2); midPoint = pointToPoint.MidPoint; vPerpBisector = pointToPoint.PerpendicularBisector().GetVectorP1toP2(); vPerpBisector.Magnitude = RightTriangle.GetSideFromSideHyp(pointToPoint.Length / 2, radius); return(new Circle2D[] { new Circle2D(midPoint + vPerpBisector, radius), new Circle2D(midPoint - vPerpBisector, radius) }); }
/// <summary> /// Creates a circle from three points. /// </summary> /// <param name="x1">A number.</param> /// <param name="y1">A number.</param> /// <param name="x2">A number.</param> /// <param name="y2">A number.</param> /// <param name="x3">A number.</param> /// <param name="y3">A number.</param> /// <remarks>See http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2#circle</remarks> public Circle2D(decimal x1, decimal y1, decimal x2, decimal y2, decimal x3, decimal y3) { _radius = 0m; // to satisfy compiler _center = default(Point2D); // to satisfy compiler if (Point2D.PointsAreColinear(x1, y1, x2, y2, x3, y3)) { throw new Exception("Can't create circle! Points are colinear."); } LineSeg2D l1 = new LineSeg2D(x1, y1, x2, y2).PerpendicularBisector(); LineSeg2D l2 = new LineSeg2D(x2, y2, x3, y3).PerpendicularBisector(); Point2D? intersect = l1.GetIntersect(l2, true); if (intersect == null) { // Should never get null here because lines won't be parallel so // long as they aren't colinear which we checked for above. throw new Exception("Something bad happened. Should have found an intersect so long as points are not colinear."); } Center = intersect.Value; Radius = intersect.Value.DistanceTo(x1, y1); }
public decimal DistanceTo(LineSeg2D l, bool treatLineSegmentAsLine = false) { return(l.DistanceTo(this, treatLineSegmentAsLine)); }
/// <summary> /// Gets the points of intersection between this circle and the line which /// contains the given line segment. /// </summary> /// <param name="l">Line segment used to determine line equation.</param> public Point2D[] GetIntersectFast(ref LineSeg2D l) { decimal[] x = new decimal[2]; decimal m = 0m; decimal b = 0m; decimal aCoeff = 0m; decimal bCoeff = 0m; decimal cCoeff = 0m; decimal lineX = 0m; decimal t = 0m; decimal p = 0m; decimal q = 0m; Point2D[] pts = null; if (!l.IsVertical) { // Circle (x - h) ^ 2 + (y - k) ^ 2 = r ^ 2 // Center: (h, k) // Radius: r // Line y = m * x + b // (x - h) ^ 2 + (m * x + b - k) ^ 2 = r ^ 2 // (x - h) * (x - h) + (m * x + b - k) * (m * x + b - k) = r^2 // (x^2 - 2 * h * x + h^2) + (m^2 * x^2 + 2 * (b - k) * m * x + (b - k)^2 = r^2 // (m^2 + 1) * x^2 + (2 * (b - k) * m - 2 * h) * x + (h^2 + (b - k)^2 - r^2) = 0 m = l.Slope; b = l.YIntersect; aCoeff = DecimalEx.Pow(m, 2) + 1; bCoeff = 2 * (b - _center.Y) * m - 2 * _center.X; cCoeff = DecimalEx.Pow(_center.X, 2) + DecimalEx.Pow(b - _center.Y, 2) - DecimalEx.Pow(_radius, 2); x = DecimalEx.SolveQuadratic(aCoeff, bCoeff, cCoeff); if (x.Length == 0) { return new Point2D[] { } } ; pts = new Point2D[x.Length]; for (var i = 0; i <= x.Length - 1; i++) { pts[i] = new Point2D(x[i], m * x[i] + b); } } else { // Circle (x - h) ^ 2 + (y - k) ^ 2 = r ^ 2 // Center: (h, k) // Radius: r // Line x = lineX // Got the following from // http://www.sonoma.edu/users/w/wilsonst/Papers/Geometry/circles/T1--2/T1-3-2.html // http://www.sonoma.edu/users/w/wilsonst/Papers/Geometry/circles/default.html lineX = l.Pt1.X; t = _radius * _radius - (lineX - _center.X) * (lineX - _center.X); if (t < 0) { return(new Point2D[] { }); } else { p = _center.Y + DecimalEx.Sqrt(t); q = _center.Y - DecimalEx.Sqrt(t); pts = new Point2D[1]; pts[0] = new Point2D(lineX, p); // NOTE that P=Q when t=0 if (p != q) { Array.Resize(ref pts, 2); pts[1] = new Point2D(lineX, q); } } } return(pts); }
/// <summary> /// Gets the points of intersection between this circle and the line which /// contains the given line segment. /// </summary> /// <param name="l">Line segment used to determine line equation.</param> /// <param name="decimals">Determines rounding that should be performed when determining /// if line intersection happens on zero, one, or two points. Default is -1 for /// no rounding.</param> public Point2D[] GetIntersect(LineSeg2D l, int decimals = -1) { LineSeg2D centToLine = default(LineSeg2D); decimal centToLineLength = 0m; decimal centToLineLengthForComp = 0m; decimal radiusForComp = 0m; if (l.IsHorizontal) { // The center to any horizontal line is a vertical line through // the center of the circle. centToLine = new LineSeg2D(this.Center, this.Center + new Vector2D(0, 1)); } else { centToLine = LineSeg2D.FromPointSlope(this.Center, l.PerpendicularSlope); } centToLine.Pt2 = l.GetIntersect(centToLine, true).Value; centToLineLength = centToLine.Length; // Get numbers for comparison, rounding if necessary centToLineLengthForComp = centToLineLength; radiusForComp = _radius; if (decimals >= 0) { centToLineLengthForComp = centToLineLengthForComp.RoundFromZero(decimals); radiusForComp = radiusForComp.RoundFromZero(decimals); } // See if line is outside of circle if (centToLineLengthForComp > radiusForComp) { return(new Point2D[] { }); } // See if line is tangent to circle if (centToLineLengthForComp == radiusForComp) { return(new Point2D[] { centToLine.Pt2 }); } // Line must intersect in two places Vector2D vCentToChord = default(Vector2D); decimal halfChord = 0m; // Get a vector from the center to the intersecting chord vCentToChord = centToLine.GetVectorP1toP2(); if (vCentToChord.Magnitude == 0) { Vector2D offsetVector = default(Vector2D); // Line goes through circle center if (l.IsVertical) { // Slope undefined so just go up the length of the radius offsetVector = new Vector2D(0, _radius); } else { offsetVector = new Vector2D(_radius * DecimalEx.Cos(DecimalEx.ATan(l.Slope)), _radius * DecimalEx.Sin(DecimalEx.ATan(l.Slope))); } return(new Point2D[] { this.Center + offsetVector, this.Center - offsetVector }); } else { Vector2D vChord = default(Vector2D); // Get a vector along the chord vChord = vCentToChord.GetPerpendicular(); // Determine the length of half the chord halfChord = RightTriangle.GetSideFromSideHyp(centToLineLength, _radius); // Set the magnitude of the vector along the chord // to be half the chord length vChord.Magnitude = halfChord; // The two intersecting points are points translated // from the center of the circle to the chord (+vCentToChord) // and then translated to the ends of the chord (+-vChord) return(new Point2D[] { this.Center + vCentToChord + vChord, this.Center + vCentToChord - vChord }); } }
/// <summary> /// Returns circles that are tangent to another circle and line. /// </summary> /// <param name="line">Line that returned circles should be tangent to.</param> /// <param name="circle">Circle that returned circles should be tangent to.</param> /// <param name="radius">Radius of new circles.</param> /// <returns>External tangent circles are listed first.</returns> public static Circle2D[] FromTangentTangentRadius(LineSeg2D line, Circle2D circle, decimal radius) { return(FromTangentTangentRadius(circle, line, radius)); }
/// <summary> /// Gets the points of intersection between this circle and another circle. /// </summary> /// <param name="other">The other circle.</param> /// <param name="decimals">Determines rounding that should be performed when determining /// if line intersection happens on zero, one, or two points. Default is -1 for /// no rounding.</param> /// <param name="impreciseResult">Which value to return for an imprecise result, /// i.e. tangency determined by rounding. If positive, returns the point on the larger circle. If /// negative, returns the point on the smaller circle. If 0, returns the midpoint between these /// two points.</param> public Point2D[] GetIntersect(Circle2D other, int decimals = -1, int impreciseResult = 0) { Vector2D meToOtherVector = default(Vector2D); meToOtherVector = this.Center.GetVectorTo(other.Center); if (decimals >= 0) { // The only intersection decision that can be made to a given precision is whether // the two circles are tangent to each other, either internally or externally. if (this.IsTangentTo(other, decimals)) { // If the smaller circle is inside the other, then the smaller is // considered internally tangent to the larger. if ((this.Radius < other.Radius && this.IsInside(other, decimals)) || (other.Radius < this.Radius && other.IsInside(this, decimals))) { // Internal tangent Point2D pointOnLargeCircle = default(Point2D); Point2D pointOnSmallCircle = default(Point2D); // Vectors to the two tangent points are both pointing in the same // direction--from the center of the larger circle towards the // center of the smaller circle. Their magnitude is the same as the // radius of the circle whose center they start from. if (this.Radius > other.Radius) { // Go from center of larger circle, Me, to smaller circle, other. pointOnLargeCircle = this.Center + new Vector2D(meToOtherVector, this.Radius); pointOnSmallCircle = other.Center + new Vector2D(meToOtherVector, other.Radius); } else { // Go from center of larger circle, other, to smaller circle, Me. pointOnLargeCircle = other.Center - new Vector2D(meToOtherVector, other.Radius); pointOnSmallCircle = this.Center - new Vector2D(meToOtherVector, this.Radius); } if (impreciseResult > 0) { // Point on larger return(new Point2D[] { pointOnLargeCircle }); } else if (impreciseResult < 0) { // Point on smaller return(new Point2D[] { pointOnSmallCircle }); } else { // Split difference LineSeg2D l = new LineSeg2D(pointOnLargeCircle, pointOnSmallCircle); return(new Point2D[] { l.MidPoint }); } } else { // External tangent Point2D pointOnMe = default(Point2D); Point2D pointOnOther = default(Point2D); // Vectors to the two tangent points are simply pointing at the other // circle's center with a magnitude of the radius of the circle whose // center it originates in. pointOnMe = this.Center + new Vector2D(meToOtherVector, this.Radius); pointOnOther = other.Center - new Vector2D(meToOtherVector, other.Radius); if (impreciseResult > 0) { // Point on larger return(new Point2D[] { (this.Radius > other.Radius ? pointOnMe : pointOnOther) }); } else if (impreciseResult < 0) { // Point on smaller return(new Point2D[] { (this.Radius < other.Radius ? pointOnMe : pointOnOther) }); } else { if (pointOnMe == pointOnOther) { return(new Point2D[] { pointOnMe }); } else { // Split difference return(new Point2D[] { new LineSeg2D(pointOnMe, pointOnOther).MidPoint }); } } } } } // Detect situations where two points touch decimal a = 0m; decimal h = 0m; decimal r2mina2 = 0m; Vector2D vToIntersectMidPt = default(Vector2D); Vector2D vToIntersect1 = default(Vector2D); // The following two equations are from: // http://paulbourke.net/geometry/2circle/ a = (DecimalEx.Pow(meToOtherVector.Magnitude, 2) - DecimalEx.Pow(other.Radius, 2) + DecimalEx.Pow(this.Radius, 2)) / (2 * meToOtherVector.Magnitude); r2mina2 = DecimalEx.Pow(this.Radius, 2) - DecimalEx.Pow(a, 2); // No intersection points -- one circle is inside or outside the other if (r2mina2 < 0) { return new Point2D[] { } } ; vToIntersectMidPt = new Vector2D(this.Center, other.Center); vToIntersectMidPt.Magnitude = a; if (r2mina2 == 0) { // Only one intersection point return(new Point2D[] { this.Center + vToIntersectMidPt }); } h = DecimalEx.Sqrt(r2mina2); vToIntersect1 = vToIntersectMidPt.GetPerpendicular(); vToIntersect1.Magnitude = h; return(new Point2D[] { this.Center + vToIntersectMidPt + vToIntersect1, this.Center + vToIntersectMidPt - vToIntersect1 }); }