/// <summary> /// Gets hypotenuse from a known side and the angle adjacent to that side. /// </summary> /// <param name="side">Length of the known side.</param> /// <param name="angleAdjacentToSide">The angle adjacent to the known side in degrees.</param> public static decimal GetHypFromSideAdjAngle(decimal side, decimal angleAdjacentToSide) { // cos(a) = s / h // h * cos(a) = s // h = s / cos(a) return(side / DecimalEx.Cos(DecimalEx.ToRad(angleAdjacentToSide))); }
/// <summary> /// Gets the Y values for which the equation of the circle yields the given X. /// </summary> /// <param name="x">The X value.</param> public decimal[] SolveForY(ref decimal x) { // Alternative calculations. May be faster/more accurate, but need to be tested. //Dim absDiff = Math.Abs(x - _center.X) //Select Case absDiff // Case Is > _radius // Return New Decimal() {} // Case _radius // Return New Decimal() {_center.Y} // Case 0 // Return New Decimal() {_center.Y + _radius, _center.Y - _radius} // Case Else // Dim height = RightTriangle.GetSideFromSideHyp(absDiff, _radius) // Return New Decimal() {_center.Y + height, _center.Y - height} //End Select decimal aCoeff = 0m; decimal bCoeff = 0m; decimal cCoeff = 0m; // Solve for y // (x - h)^2 + (y - k)^2 = r^2 // simplifies to // y^2 - 2 * k * y + k^2 + (x - h)^2 - r^2 = 0 aCoeff = 1; bCoeff = -2 * _center.Y; cCoeff = _center.Y * _center.Y + (x - _center.X) * (x - _center.X) - _radius * _radius; return(DecimalEx.SolveQuadratic(aCoeff, bCoeff, cCoeff)); }
public decimal DistanceTo(decimal x, decimal y) { var xDist = this.X - x; var yDist = this.Y - y; return(DecimalEx.Sqrt(xDist * xDist + yDist * yDist)); }
/// <summary> /// Gets the angle in degrees for a ray that extends from the center /// of the circle through the given point. An exception will occur, /// however, if the point specified is the same point as the center /// of the circle. /// </summary> /// <param name="pt">The point.</param> public decimal AngleThroughPoint(Point2D pt) { decimal a = 0m; // Shift point so it's relative to the origin. pt += new Vector2D(-Center.X, -Center.Y); // Do check here after we've translated the point // to account for small precision rounding. if (pt == Point2D.Origin) { throw new Exception("No angle through given point since point is at center of circle!"); } // Special case for 0 and 180 where we would get // a divide by zero below. if (pt.Y == 0) { if (pt.X > 0) { return(0); } else { return(180); } } // Special case for 90 and 270 where we can't // determine quadrant below. if (pt.X == 0) { if (pt.Y > 0) { return(90); } else { return(270); } } a = DecimalEx.ToDeg(DecimalEx.ATan(pt.X / pt.Y)); switch (pt.Quadrant) { case 1: case 2: a = 90 - a; break; case 3: case 4: a = 270 - a; break; } return(a); }
/// <summary> /// Gets the angle in degrees between this vector and another. /// </summary> /// <param name="other">The other vector to get the angle to.</param> /// <remarks>See http://en.wikipedia.org/wiki/Dot_product</remarks> public decimal AngleTo(Vector2D other) { if (this.IsNull || other.IsNull) { throw new Exception("Can't find angle when one or both vectors are null vectors!"); } // Cos(theta) = DotProduct(v1,v2) / (length(v1) * length(v2)) // aka theta = acos(v.normalize.dot(other.normalize)), however, the equation // used gives us better precision return(DecimalEx.ToDeg(DecimalEx.ACos(this.Dot(other) / (this.Magnitude * other.Magnitude)))); }
/// <summary> /// Gets a side from a known side and the hypotenuse. /// </summary> /// <param name="side">Length of the known side.</param> /// <param name="hypotenuse">Length of the hypotenuse.</param> public static decimal GetSideFromSideHyp(decimal side, decimal hypotenuse) { // a^2 + b^2 = c^2 // a^2 = c^2 - b^2 // a = sqrt(c^2 - b^2) decimal sideSquared = hypotenuse * hypotenuse - side * side; if (sideSquared < 0) { throw new Exception("Error finding side from other side and hypotenuse! Side and hypotenuse swapped or invalid right triangle."); } return(DecimalEx.Sqrt(sideSquared)); }
/// <summary> /// Determines whether or not the given angle lies on the arc. /// </summary> /// <param name="angle">The angle in question.</param> public bool IsAngleOnArc(decimal angle) { angle = DecimalEx.NormalizeAngleDeg(angle); if (_startAngle <= _endAngle) { return((_startAngle <= angle) && (angle <= _endAngle)); } else { return((angle > _startAngle) || (angle < _endAngle)); } }
/// <summary> /// Gets the X values for which the equation of the circle yields the given Y. /// </summary> /// <param name="y">The Y value.</param> public decimal[] SolveForX(decimal y) { decimal aCoeff = 0m; decimal bCoeff = 0m; decimal cCoeff = 0m; // Solve for x // (x - h)^2 + (y - k)^2 = r^2 // simplifies to // x^2 - 2 * h * x + h^2 + (y - k)^2 - r^2 = 0 aCoeff = 1; bCoeff = -2 * _center.X; cCoeff = _center.X * _center.X + (y - _center.Y) * (y - _center.Y) - _radius * _radius; return(DecimalEx.SolveQuadratic(aCoeff, bCoeff, cCoeff)); }
/// <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); }
/// <summary> /// Gets a side from the opposite angle and the length of the hypotenuse. /// </summary> /// <param name="oppositeAngle">Angle opposite to the side to calculate in degrees.</param> /// <param name="hypotenuse">Length of the hypotenuse.</param> public static decimal GetSideFromOppAngleHyp(decimal oppositeAngle, decimal hypotenuse) { // sin(oppositeAngle) = x / hypotenuse // x = hypotenuse * sin(oppositeAngle) return(hypotenuse * Convert.ToDecimal(DecimalEx.Sin(DecimalEx.ToRad(oppositeAngle)))); }
/// <summary> /// Gets angle in degrees from the opposite side and the hypotenuse. /// </summary> /// <param name="oppositeSide">Length of the known side opposite the angle.</param> /// <param name="hypotenuse">Length of the hypotenuse.</param> public static decimal GetAngleFromOppSideHyp(decimal oppositeSide, decimal hypotenuse) { // sin(a) = oppositeSide / hypotenuse // a = asin(oppositeSide / hypotenuse) return(DecimalEx.ToDeg(DecimalEx.ASin(oppositeSide / hypotenuse))); }
/// <summary> /// Gets the angle of the vector in degrees from the X+ axis. Will return result in the range /// -180 to +180. Will return 0 for null vector. /// </summary> public decimal Angle() { return(DecimalEx.ToDeg(DecimalEx.ATan2(Y, X))); }
/// <summary> /// Gets angle in degrees from the adjacent side and the hypotenuse. /// </summary> /// <param name="adjacentSide">Length of the known side adjacent to the angle.</param> /// <param name="hypotenuse">Length of the hypotenuse.</param> public static decimal GetAngleFromAdjSideHyp(decimal adjacentSide, decimal hypotenuse) { // cos(a) = adjacentSide / hypotenuse // a = acos(adjacentSide / hypotenuse) return(DecimalEx.ToDeg(DecimalEx.ACos(adjacentSide / hypotenuse))); }
/// <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> /// Gets the hypotenuse from the two other sides. /// </summary> /// <param name="sideA">Length of one side.</param> /// <param name="sideB">Length of other side.</param> public static decimal GetHypFromSides(decimal sideA, decimal sideB) { // a^2 + b^2 = c^2 // c = sqrt(a^2 + b^2) return(DecimalEx.Sqrt(sideA * sideA + sideB * sideB)); }
/// <summary> /// Computes the radius of a circle given a chord length and the chord's sagitta. /// </summary> /// <param name="chordLength">Length of the chord.</param> /// <param name="sagitta">Sagitta.</param> /// <remarks> http://www.physicsforums.com/showpost.php?p=2069646&postcount=2 </remarks> public static decimal GetRadiusFromChordLengthSagitta(decimal chordLength, decimal sagitta) { return((DecimalEx.Pow(sagitta, 2) + 0.25m * DecimalEx.Pow(chordLength, 2)) / (2 * sagitta)); }
/// <summary> /// Gets hypotenuse from a known side and the angle opposite to it. /// </summary> /// <param name="side">Length of the known side.</param> /// <param name="angleOppositeToSide">Angle opposite to the known side in degrees.</param> public static decimal GetHypFromSideOppAngle(decimal side, decimal angleOppositeToSide) { // sin(a) = s / h // h = s / sin(a) return(side / DecimalEx.Sin(DecimalEx.ToRad(angleOppositeToSide))); }
/// <summary> /// Gets the coordinates of the point on the circle at the given angle. /// </summary> /// <param name="degrees">The angle in degrees.</param> public Point2D PointAt(decimal degrees) { decimal rad = DecimalEx.ToRad(degrees); return(new Point2D(_center.X + _radius * DecimalEx.Cos(rad), _center.Y + _radius * DecimalEx.Sin(rad))); }
/// <summary> /// Gets angle in degrees from the two known sides. /// </summary> /// <param name="oppositeSide">Length of the known side opposite the angle.</param> /// <param name="adjacentSide">Length of the known side adjacent to the angle.</param> public static decimal GetAngleFromSides(decimal oppositeSide, decimal adjacentSide) { // tan(a) = opposideSide / adjacentSide // a = atan(opposideSide / adjacentSide) return(DecimalEx.ToDeg(DecimalEx.ATan(oppositeSide / adjacentSide))); }
/// <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 }); }
/// <summary> /// Gets a side from the adjacent angle and the length of the hypotenuse. /// </summary> /// <param name="adjacentAngle">Angle adjacent to the side to calculate in degrees.</param> /// <param name="hypotenuse">Length of the hypotenuse.</param> public static decimal GetSideFromAdjAngleHyp(decimal adjacentAngle, decimal hypotenuse) { // cos(adjacentAngle) = x / hypotenuse // x = hypotenuse * cos(adjacentAngle) return(hypotenuse * Convert.ToDecimal(DecimalEx.Cos(DecimalEx.ToRad(adjacentAngle)))); }
/// <summary> /// Gets a side from the opposite angle and the length of the opposite side. /// </summary> /// <param name="oppositeAngle">Angle opposite the side to calculate in degrees.</param> /// <param name="oppositeSide">Length of the opposite side.</param> public static decimal GetSideFromOppAngleOppSide(decimal oppositeAngle, decimal oppositeSide) { // tan(oppositeAngle) = x / oppositeSide // x = oppositeSide * tan(oppositeAngle) return(oppositeSide * Convert.ToDecimal(DecimalEx.Tan(DecimalEx.ToRad(oppositeAngle)))); }
/// <summary> /// Gets a side from the adjacent angle and the length of the opposite side. /// </summary> /// <param name="adjacentAngle">Angle adjacent to the side to calculate in degrees.</param> /// <param name="oppositeSide">Length of the opposite side.</param> public static decimal GetSideFromAdjAngleOppSide(decimal adjacentAngle, decimal oppositeSide) { // tan(adjacentAngle) = oppositeSide / x // x = oppositeSide / tan(adjacentAngle) return(oppositeSide / Convert.ToDecimal(DecimalEx.Tan(DecimalEx.ToRad(adjacentAngle)))); }