/// <summary> /// Find the distance from a point to a line segment. This is done by projecting the vector /// from point to segment.PointA onto the line segment, then calculating the distance between /// the projected point and original point. /// @param segment - the line segment /// @param point - the point /// @out projectedPoint - vector point -> segment.PointA projected onto segment /// @out projectedLineLength - the length of vector point -> segment.PointA /// @returns the distance between point and projectedPoint /// </summary> public static float FindDistanceToPoint( LineSegment2D segment, Vector2 point, out Vector2 projectedPoint, out float projectedLineLength) { Vector2 segmentVector = segment.ToVector2(); Vector2 projectingVector = point - segment.PointA; float dotProduct = Mathf.Infinity; Vector2 projectedVector = segmentVector.Project(projectingVector, out dotProduct); projectedPoint = segment.PointA + projectedVector; projectedLineLength = projectedVector.magnitude; return(Vector2.Distance(projectedPoint, point)); }
/// <summary> /// Determine if a point is inside a segment /// @param P - point /// @param S - collinear segment /// @returns - 0 = P is not inside S /// 1 = P is inside S /// </summary> public static int ContainsPoint(Vector2 P, LineSegment2D S) { // S is not vertical if (S.PointA.x != S.PointB.x) { if ((S.PointA.x <= P.x && P.x <= S.PointB.x) || (S.PointA.x >= P.x && P.x >= S.PointB.x)) { return(1); } } // S is vertical, so test y coordinate else { if ((S.PointA.y <= P.y && P.y <= S.PointB.y) || (S.PointA.y >= P.y && P.y >= S.PointB.y)) { return(1); } } return(0); }
/// <summary> /// Check whether two finite line segments intersect. /// NOTE: The out values may not always exist. /// @param segmentA - Finite line segment. /// @param segmentB - Finite line segment. /// @out i0 - Intersection point (if it exists) /// @out i1 - Intersection end point (if the segments overlap) /// @returns - 0: Disjointed, no intersection. /// 1: Intersection at unique point 'intersectPoint'. /// 2: Overlapping segments from 'intersectPoint' to 'intersectEndpoint'. /// </summary> public static int CheckIntersection( LineSegment2D segmentA, LineSegment2D segmentB, out Vector2 i0, out Vector2 i1) { Vector2 u = segmentA.PointB - segmentA.PointA; Vector2 v = segmentB.PointB - segmentB.PointA; Vector2 w = segmentA.PointA - segmentB.PointA; float D = ArePerpindicular(u, v); i0 = Vector2.zero; i1 = Vector2.zero; // test if they are parallel (includes either being a point) if (Mathf.Abs(D) < 0.01f) { // segmentA and segmentB are parallel // test if they are NOT collinear if (ArePerpindicular(u, w) != 0 || ArePerpindicular(v, w) != 0) { return(0); } // they are collinear or degenerate // check if they are degenerate points float du = Vector2.Dot(u, u); float dv = Vector2.Dot(v, v); // both segments are points if (du == 0 && dv == 0) { // they are distinct points if (segmentA.PointA != segmentB.PointA) { return(0); } // they are the same point i0 = segmentA.PointA; return(1); } // test if segmentA is a single point if (du == 0) { // but is not in segmentB if (ContainsPoint(segmentA.PointA, segmentB) == 0) { return(0); } i0 = segmentA.PointA; return(1); } // test if segmentB is a single point if (dv == 0) { // but is not in segmentA if (ContainsPoint(segmentB.PointA, segmentA) == 0) { return(0); } i0 = segmentB.PointA; return(1); } // they are collinear segments - test for overlap (or not) // endpoints of segmentA in eqn for segmentB float t0, t1; Vector2 w2 = segmentA.PointB - segmentB.PointA; if (v.x != 0) { t0 = w.x / v.x; t1 = w2.x / v.x; } else { t0 = w.y / v.y; t1 = w2.y / v.y; } // must have t0 smaller than t1 // swap if not if (t0 > t1) { float t = t0; t0 = t1; t1 = t; } if (t0 > 1 || t1 < 0) { // NO overlap return(0); } t0 = t0 < 0 ? 0 : t0; // clip to min 0 t1 = t1 > 1 ? 1 : t1; // clip to max 1 if (t0 == t1) { // intersect is a point i0 = segmentB.PointA + t0 * v; return(1); } // they overlap in a valid subsegment i0 = segmentB.PointA + t0 * v; i1 = segmentB.PointA + t1 * v; return(2); } // the segments are skew and may intersect in a point // get the intersect parameter for segmentA float sI = ArePerpindicular(v, w) / D; // no intersect with segmentA if (sI < 0 || sI > 1) { return(0); } // get the intersect parameter for segmentB float tI = ArePerpindicular(u, w) / D; // no intersect with segmentB if (tI < 0 || tI > 1) { return(0); } // compute segmentA intersect point i0 = segmentA.PointA + sI * u; return(1); }
/// <summary> /// Calculate the smallest difference in angles between two line segments, in degrees. /// @param a - line segment /// @param b - line segment /// @returns - the smallest angle between the line segments /// </summary> public static float MinimumAngleDifferenceInDegrees(LineSegment2D a, LineSegment2D b) { float diff = Mathf.Abs(a.DirectionInDegrees - b.DirectionInDegrees) % 180f; return(Mathf.Min(diff, Mathf.Abs(diff - 180f))); }