/// <summary> /// Finds the points of intersection between a line and a circle. The results are two lambdas along the line, one /// for each point, or NaN if there is no intersection.</summary> public static void LineWithCircle(ref EdgeD line, ref CircleD circle, out double lambda1, out double lambda2) { // The following expressions come up a lot in the solution, so simplify using them. double dx = line.End.X - line.Start.X; double dy = line.End.Y - line.Start.Y; double ax = -line.Start.X + circle.Center.X; double ay = -line.Start.Y + circle.Center.Y; // Solve simultaneously for l: // Eq of a line: x = sx + l * dx // y = sy + l * dy // Eq of a circle: (x - cx)^2 + (y - cy)^2 = r^2 // // Eventually we get a standard quadratic equation in l with the // following coefficients: double a = dx * dx + dy * dy; double b = -2 * (ax * dx + ay * dy); double c = ax * ax + ay * ay - circle.Radius * circle.Radius; // Now just solve the quadratic eqn... double D = b * b - 4 * a * c; if (D < 0) { lambda1 = lambda2 = double.NaN; } else { double sqrtD = Math.Sqrt(D); lambda1 = (-b + sqrtD) / (2 * a); lambda2 = (-b - sqrtD) / (2 * a); } }
/// <summary> /// Finds the points of intersection between a ray and an arc. The resulting lambdas along the ray are sorted in /// ascending order, so the "first" intersection is always in lambda1 (if any). Lambda may be NaN if there is no /// intersection (or no "second" intersection).</summary> public static void RayWithArc(ref EdgeD ray, ref ArcD arc, out double lambda1, out double lambda2) { RayWithCircle(ref ray, ref arc.Circle, out lambda1, out lambda2); var sweepdir = Math.Sign(arc.AngleSweep); if (!double.IsNaN(lambda1)) { var dir = ((ray.Start + lambda1 * (ray.End - ray.Start)) - arc.Circle.Center).Theta(); if (!(GeomUt.AngleDifference(arc.AngleStart, dir) * sweepdir > 0 && GeomUt.AngleDifference(arc.AngleStart + arc.AngleSweep, dir) * sweepdir < 0)) { lambda1 = double.NaN; } } if (!double.IsNaN(lambda2)) { var dir = ((ray.Start + lambda2 * (ray.End - ray.Start)) - arc.Circle.Center).Theta(); if (!(GeomUt.AngleDifference(arc.AngleStart, dir) * sweepdir > 0 && GeomUt.AngleDifference(arc.AngleStart + arc.AngleSweep, dir) * sweepdir < 0)) { lambda2 = double.NaN; } } if (double.IsNaN(lambda1) && !double.IsNaN(lambda2)) { lambda1 = lambda2; lambda2 = double.NaN; } }
/// <summary> /// Checks for intersections between a ray and a bounding box. Returns true if there is at least one intersection.</summary> public static bool RayWithBoundingBox(ref EdgeD ray, ref BoundingBoxD box) { double dx = ray.End.X - ray.Start.X; double dy = ray.End.Y - ray.Start.Y; double k, c; // temporaries // Check intersection with horizontal bounds if (dy != 0) { // Upper line k = (box.Ymax - ray.Start.Y) / dy; if (k >= 0) { c = ray.Start.X + k * dx; if (c >= box.Xmin && c <= box.Xmax) { return(true); } } // Lower line k = (box.Ymin - ray.Start.Y) / dy; if (k >= 0) { c = ray.Start.X + k * dx; if (c >= box.Xmin && c <= box.Xmax) { return(true); } } } // Check intersection with vertical bounds if (dx != 0) { // Rightmost line k = (box.Xmax - ray.Start.X) / dx; if (k >= 0) { c = ray.Start.Y + k * dy; if (c >= box.Ymin && c <= box.Ymax) { return(true); } } // Leftmost line k = (box.Xmin - ray.Start.X) / dx; if (k >= 0) { c = ray.Start.Y + k * dy; if (c >= box.Ymin && c <= box.Ymax) { return(true); } } } return(false); }
/// <summary> /// Calculates the intersection of a ray with a segment. Returns the result as the lambdas of the intersection /// point along the ray and the segment. If there is no intersection returns double.NaN in both lambdas.</summary> public static void RayWithSegment(ref EdgeD ray, ref EdgeD segment, out double rayL, out double segmentL) { Intersect.LineWithLine(ref ray, ref segment, out rayL, out segmentL); if (!double.IsNaN(rayL) && ((rayL < 0) || (segmentL < 0) || (segmentL > 1))) { rayL = segmentL = double.NaN; } }
private void AssertRayWithCircle(double frX, double frY, double toX, double toY, double cX, double cY, double cR, double expL1, double expL2) { EdgeD ray = new EdgeD(frX, frY, toX, toY); CircleD cir = new CircleD(cX, cY, cR); double l1, l2; Intersect.RayWithCircle(ref ray, ref cir, out l1, out l2); Assert.AreEqual(expL1, l1, 0.001); Assert.AreEqual(expL2, l2, 0.001); }
private void AssertLineWithCircle(double frX, double frY, double toX, double toY, double cX, double cY, double cR, double expL1, double expL2) { EdgeD lin = new EdgeD(frX, frY, toX, toY); CircleD cir = new CircleD(cX, cY, cR); double l1, l2; Intersect.LineWithCircle(ref lin, ref cir, out l1, out l2); Assert.AreEqual(MinTO(expL1, expL2), MinTO(l1, l2), 0.001); Assert.AreEqual(MaxTO(expL1, expL2), MaxTO(l1, l2), 0.001); }
/// <summary> /// Finds intersections between a ray and a rectangle. Returns the lambdas of intersections, if any, or NaN /// otherwise. Guarantees that lambda1 < lambda2, and if only one of them is NaN then it's lambda2. Lambda is /// such that ray.Start + lambda * (ray.End - ray.Start) gives the point of intersection.</summary> public static void RayWithRectangle(ref EdgeD ray, ref RectangleD rect, out double lambda1, out double lambda2) { double lambda, dummy; bool done1 = false; lambda1 = lambda2 = double.NaN; for (int i = 0; i < 4; i++) { EdgeD segment; switch (i) { case 0: segment = new EdgeD(rect.Left, rect.Top, rect.Right, rect.Top); break; case 1: segment = new EdgeD(rect.Right, rect.Top, rect.Right, rect.Bottom); break; case 2: segment = new EdgeD(rect.Right, rect.Bottom, rect.Left, rect.Bottom); break; case 3: segment = new EdgeD(rect.Left, rect.Bottom, rect.Left, rect.Top); break; default: throw new InternalErrorException("fsvxhfhj"); // unreachable } Intersect.RayWithSegment(ref ray, ref segment, out lambda, out dummy); if (!double.IsNaN(lambda)) { if (!done1) { lambda1 = lambda; done1 = true; } else if (lambda != lambda1) { if (lambda > lambda1) { lambda2 = lambda; } else { lambda2 = lambda1; lambda1 = lambda; } return; } } } }
/// <summary> /// Finds the point of intersection of two lines. The result is in terms of lambda along each of the lines. Point /// of intersection is defined as "line.Start + lambda * line", for each line. If the lines don't intersect, the /// lambdas are set to NaN.</summary> public static void LineWithLine(ref EdgeD line1, ref EdgeD line2, out double line1Lambda, out double line2Lambda) { // line1 direction vector double l1dx = line1.End.X - line1.Start.X; double l1dy = line1.End.Y - line1.Start.Y; // line2 direction vector double l2dx = line2.End.X - line2.Start.X; double l2dy = line2.End.Y - line2.Start.Y; double denom = l1dx * l2dy - l1dy * l2dx; if (denom == 0) { line1Lambda = double.NaN; line2Lambda = double.NaN; } else { line1Lambda = (l2dx * (line1.Start.Y - line2.Start.Y) - l2dy * (line1.Start.X - line2.Start.X)) / denom; line2Lambda = (l1dx * (line1.Start.Y - line2.Start.Y) - l1dy * (line1.Start.X - line2.Start.X)) / denom; } }
/// <summary> /// Finds the points of intersection between a ray and a circle. The resulting lambdas along the ray are sorted in /// ascending order, so the "first" intersection is always in lambda1 (if any). Lambda may be NaN if there is no /// intersection (or no "second" intersection).</summary> public static void RayWithCircle(ref EdgeD ray, ref CircleD circle, out double lambda1, out double lambda2) { LineWithCircle(ref ray, ref circle, out lambda1, out lambda2); // Sort the two values in ascending order, with NaN last, // while resetting negative values to NaNs if (lambda1 < 0) { lambda1 = double.NaN; } if (lambda2 < 0) { lambda2 = double.NaN; } if (lambda1 > lambda2 || double.IsNaN(lambda1)) { double temp = lambda1; lambda1 = lambda2; lambda2 = temp; } }
/// <summary> /// Finds the point of intersection of two lines. If the lines don't intersect, the resulting point coordinates /// are NaN.</summary> public static PointD LineWithLine(EdgeD line1, EdgeD line2) { LineWithLine(ref line1, ref line2, out var line1Lambda, out var line2Lambda); return(line1.Start + line1Lambda * (line1.End - line1.Start)); }
/// <summary> /// Finds the points of intersection between a line and a circle. The results are two lambdas along the line, one /// for each point, or NaN if there is no intersection.</summary> public static void LineWithCircle(EdgeD line, CircleD circle, out double lambda1, out double lambda2) { LineWithCircle(ref line, ref circle, out lambda1, out lambda2); }
/// <summary>Returns true if this bounding box intersects with the specified ray.</summary> public bool IntersectsWithRay(EdgeD ray) { return(Intersect.RayWithBoundingBox(ref ray, ref this)); }
/// <summary>Returns a new BoundingBox bounding the specified edge.</summary> public static BoundingBoxD FromEdge(EdgeD edge) { return(FromPoint(ref edge.Start, ref edge.End)); }
/// <summary> /// Returns a value indicating whether one of the triangle edges is equal to <paramref name="e"/>. Edge equality /// is direction-insensitive.</summary> public bool HasEdge(EdgeD e) => e == Edge12 || e == Edge23 || e == Edge31;