using UnityEngine; namespace ProceduralToolkit { /// /// Collection of intersection algorithms /// public static partial class Intersect { #region Point-Line /// /// Tests if the point lies on the line /// public static bool PointLine(Vector2 point, Line2 line) { return PointLine(point, line.origin, line.direction); } /// /// Tests if the point lies on the line /// /// /// -1 if the point is to the left of the line, /// 0 if it is on the line, /// 1 if it is to the right of the line /// public static bool PointLine(Vector2 point, Line2 line, out int side) { return PointLine(point, line.origin, line.direction, out side); } /// /// Tests if the point lies on the line /// public static bool PointLine(Vector2 point, Vector2 lineOrigin, Vector2 lineDirection) { float perpDot = VectorE.PerpDot(point - lineOrigin, lineDirection); return -Geometry.Epsilon < perpDot && perpDot < Geometry.Epsilon; } /// /// Tests if the point lies on the line /// /// /// -1 if the point is to the left of the line, /// 0 if it is on the line, /// 1 if it is to the right of the line /// public static bool PointLine(Vector2 point, Vector2 lineOrigin, Vector2 lineDirection, out int side) { float perpDot = VectorE.PerpDot(point - lineOrigin, lineDirection); if (perpDot < -Geometry.Epsilon) { side = -1; return false; } if (perpDot > Geometry.Epsilon) { side = 1; return false; } side = 0; return true; } #endregion Point-Line #region Point-Ray /// /// Tests if the point lies on the ray /// public static bool PointRay(Vector2 point, Ray2D ray) { return PointRay(point, ray.origin, ray.direction); } /// /// Tests if the point lies on the ray /// /// /// -1 if the point is to the left of the ray, /// 0 if it is on the line, /// 1 if it is to the right of the ray /// public static bool PointRay(Vector2 point, Ray2D ray, out int side) { return PointRay(point, ray.origin, ray.direction, out side); } /// /// Tests if the point lies on the ray /// public static bool PointRay(Vector2 point, Vector2 rayOrigin, Vector2 rayDirection) { Vector2 toPoint = point - rayOrigin; float perpDot = VectorE.PerpDot(toPoint, rayDirection); return -Geometry.Epsilon < perpDot && perpDot < Geometry.Epsilon && Vector2.Dot(rayDirection, toPoint) > -Geometry.Epsilon; } /// /// Tests if the point lies on the ray /// /// /// -1 if the point is to the left of the ray, /// 0 if it is on the line, /// 1 if it is to the right of the ray /// public static bool PointRay(Vector2 point, Vector2 rayOrigin, Vector2 rayDirection, out int side) { Vector2 toPoint = point - rayOrigin; float perpDot = VectorE.PerpDot(toPoint, rayDirection); if (perpDot < -Geometry.Epsilon) { side = -1; return false; } if (perpDot > Geometry.Epsilon) { side = 1; return false; } side = 0; return Vector2.Dot(rayDirection, toPoint) > -Geometry.Epsilon; } #endregion Point-Ray #region Point-Segment /// /// Tests if the point lies on the segment /// public static bool PointSegment(Vector2 point, Segment2 segment) { return PointSegment(point, segment.a, segment.b); } /// /// Tests if the point lies on the segment /// /// /// -1 if the point is to the left of the segment, /// 0 if it is on the line, /// 1 if it is to the right of the segment /// public static bool PointSegment(Vector2 point, Segment2 segment, out int side) { return PointSegment(point, segment.a, segment.b, out side); } /// /// Tests if the point lies on the segment /// public static bool PointSegment(Vector2 point, Vector2 segmentA, Vector2 segmentB) { Vector2 fromAToB = segmentB - segmentA; float sqrSegmentLength = fromAToB.sqrMagnitude; if (sqrSegmentLength < Geometry.Epsilon) { // The segment is a point return point == segmentA; } // Normalized direction gives more stable results Vector2 segmentDirection = fromAToB.normalized; Vector2 toPoint = point - segmentA; float perpDot = VectorE.PerpDot(toPoint, segmentDirection); if (-Geometry.Epsilon < perpDot && perpDot < Geometry.Epsilon) { float pointProjection = Vector2.Dot(segmentDirection, toPoint); return pointProjection > -Geometry.Epsilon && pointProjection < Mathf.Sqrt(sqrSegmentLength) + Geometry.Epsilon; } return false; } /// /// Tests if the point lies on the segment /// /// /// -1 if the point is to the left of the segment, /// 0 if it is on the line, /// 1 if it is to the right of the segment /// public static bool PointSegment(Vector2 point, Vector2 segmentA, Vector2 segmentB, out int side) { Vector2 fromAToB = segmentB - segmentA; float sqrSegmentLength = fromAToB.sqrMagnitude; if (sqrSegmentLength < Geometry.Epsilon) { // The segment is a point side = 0; return point == segmentA; } // Normalized direction gives more stable results Vector2 segmentDirection = fromAToB.normalized; Vector2 toPoint = point - segmentA; float perpDot = VectorE.PerpDot(toPoint, segmentDirection); if (perpDot < -Geometry.Epsilon) { side = -1; return false; } if (perpDot > Geometry.Epsilon) { side = 1; return false; } side = 0; float pointProjection = Vector2.Dot(segmentDirection, toPoint); return pointProjection > -Geometry.Epsilon && pointProjection < Mathf.Sqrt(sqrSegmentLength) + Geometry.Epsilon; } private static bool PointSegment(Vector2 point, Vector2 segmentA, Vector2 segmentDirection, float sqrSegmentLength) { float segmentLength = Mathf.Sqrt(sqrSegmentLength); segmentDirection /= segmentLength; Vector2 toPoint = point - segmentA; float perpDot = VectorE.PerpDot(toPoint, segmentDirection); if (-Geometry.Epsilon < perpDot && perpDot < Geometry.Epsilon) { float pointProjection = Vector2.Dot(segmentDirection, toPoint); return pointProjection > -Geometry.Epsilon && pointProjection < segmentLength + Geometry.Epsilon; } return false; } public static bool PointSegmentCollinear(Vector2 segmentA, Vector2 segmentB, Vector2 point) { if (Mathf.Abs(segmentA.x - segmentB.x) < Geometry.Epsilon) { // Vertical if (segmentA.y <= point.y && point.y <= segmentB.y) { return true; } if (segmentA.y >= point.y && point.y >= segmentB.y) { return true; } } else { // Not vertical if (segmentA.x <= point.x && point.x <= segmentB.x) { return true; } if (segmentA.x >= point.x && point.x >= segmentB.x) { return true; } } return false; } #endregion Point-Segment #region Point-Circle /// /// Tests if the point is inside the circle /// public static bool PointCircle(Vector2 point, Circle2 circle) { return PointCircle(point, circle.center, circle.radius); } /// /// Tests if the point is inside the circle /// public static bool PointCircle(Vector2 point, Vector2 circleCenter, float circleRadius) { // For points on the circle's edge magnitude is more stable than sqrMagnitude return (point - circleCenter).magnitude < circleRadius + Geometry.Epsilon; } #endregion Point-Circle #region Line-Line /// /// Computes an intersection of the lines /// public static bool LineLine(Line2 lineA, Line2 lineB) { return LineLine(lineA.origin, lineA.direction, lineB.origin, lineB.direction, out IntersectionLineLine2 intersection); } /// /// Computes an intersection of the lines /// public static bool LineLine(Line2 lineA, Line2 lineB, out IntersectionLineLine2 intersection) { return LineLine(lineA.origin, lineA.direction, lineB.origin, lineB.direction, out intersection); } /// /// Computes an intersection of the lines /// public static bool LineLine(Vector2 originA, Vector2 directionA, Vector2 originB, Vector2 directionB) { return LineLine(originA, directionA, originB, directionB, out IntersectionLineLine2 intersection); } /// /// Computes an intersection of the lines /// public static bool LineLine(Vector2 originA, Vector2 directionA, Vector2 originB, Vector2 directionB, out IntersectionLineLine2 intersection) { Vector2 originBToA = originA - originB; float denominator = VectorE.PerpDot(directionA, directionB); float perpDotB = VectorE.PerpDot(directionB, originBToA); if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel float perpDotA = VectorE.PerpDot(directionA, originBToA); if (Mathf.Abs(perpDotA) > Geometry.Epsilon || Mathf.Abs(perpDotB) > Geometry.Epsilon) { // Not collinear intersection = IntersectionLineLine2.None(); return false; } // Collinear intersection = IntersectionLineLine2.Line(originA); return true; } // Not parallel intersection = IntersectionLineLine2.Point(originA + directionA*(perpDotB/denominator)); return true; } #endregion Line-Line #region Line-Ray /// /// Computes an intersection of the line and the ray /// public static bool LineRay(Line2 line, Ray2D ray) { return LineRay(line.origin, line.direction, ray.origin, ray.direction, out IntersectionLineRay2 intersection); } /// /// Computes an intersection of the line and the ray /// public static bool LineRay(Line2 line, Ray2D ray, out IntersectionLineRay2 intersection) { return LineRay(line.origin, line.direction, ray.origin, ray.direction, out intersection); } /// /// Computes an intersection of the line and the ray /// public static bool LineRay(Vector2 lineOrigin, Vector2 lineDirection, Vector2 rayOrigin, Vector2 rayDirection) { return LineRay(lineOrigin, lineDirection, rayOrigin, rayDirection, out IntersectionLineRay2 intersection); } /// /// Computes an intersection of the line and the ray /// public static bool LineRay(Vector2 lineOrigin, Vector2 lineDirection, Vector2 rayOrigin, Vector2 rayDirection, out IntersectionLineRay2 intersection) { Vector2 rayOriginToLineOrigin = lineOrigin - rayOrigin; float denominator = VectorE.PerpDot(lineDirection, rayDirection); float perpDotA = VectorE.PerpDot(lineDirection, rayOriginToLineOrigin); if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel float perpDotB = VectorE.PerpDot(rayDirection, rayOriginToLineOrigin); if (Mathf.Abs(perpDotA) > Geometry.Epsilon || Mathf.Abs(perpDotB) > Geometry.Epsilon) { // Not collinear intersection = IntersectionLineRay2.None(); return false; } // Collinear intersection = IntersectionLineRay2.Ray(rayOrigin); return true; } // Not parallel float rayDistance = perpDotA/denominator; if (rayDistance > -Geometry.Epsilon) { intersection = IntersectionLineRay2.Point(rayOrigin + rayDirection*rayDistance); return true; } intersection = IntersectionLineRay2.None(); return false; } #endregion Line-Ray #region Line-Segment /// /// Computes an intersection of the line and the segment /// public static bool LineSegment(Line2 line, Segment2 segment) { return LineSegment(line.origin, line.direction, segment.a, segment.b, out IntersectionLineSegment2 intersection); } /// /// Computes an intersection of the line and the segment /// public static bool LineSegment(Line2 line, Segment2 segment, out IntersectionLineSegment2 intersection) { return LineSegment(line.origin, line.direction, segment.a, segment.b, out intersection); } /// /// Computes an intersection of the line and the segment /// public static bool LineSegment(Vector2 lineOrigin, Vector2 lineDirection, Vector2 segmentA, Vector2 segmentB) { return LineSegment(lineOrigin, lineDirection, segmentA, segmentB, out IntersectionLineSegment2 intersection); } /// /// Computes an intersection of the line and the segment /// public static bool LineSegment(Vector2 lineOrigin, Vector2 lineDirection, Vector2 segmentA, Vector2 segmentB, out IntersectionLineSegment2 intersection) { Vector2 segmentAToOrigin = lineOrigin - segmentA; Vector2 segmentDirection = segmentB - segmentA; float denominator = VectorE.PerpDot(lineDirection, segmentDirection); float perpDotA = VectorE.PerpDot(lineDirection, segmentAToOrigin); if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel // Normalized direction gives more stable results float perpDotB = VectorE.PerpDot(segmentDirection.normalized, segmentAToOrigin); if (Mathf.Abs(perpDotA) > Geometry.Epsilon || Mathf.Abs(perpDotB) > Geometry.Epsilon) { // Not collinear intersection = IntersectionLineSegment2.None(); return false; } // Collinear bool segmentIsAPoint = segmentDirection.sqrMagnitude < Geometry.Epsilon; if (segmentIsAPoint) { intersection = IntersectionLineSegment2.Point(segmentA); return true; } bool codirected = Vector2.Dot(lineDirection, segmentDirection) > 0; if (codirected) { intersection = IntersectionLineSegment2.Segment(segmentA, segmentB); } else { intersection = IntersectionLineSegment2.Segment(segmentB, segmentA); } return true; } // Not parallel float segmentDistance = perpDotA/denominator; if (segmentDistance > -Geometry.Epsilon && segmentDistance < 1 + Geometry.Epsilon) { intersection = IntersectionLineSegment2.Point(segmentA + segmentDirection*segmentDistance); return true; } intersection = IntersectionLineSegment2.None(); return false; } #endregion Line-Segment #region Line-Circle /// /// Computes an intersection of the line and the circle /// public static bool LineCircle(Line2 line, Circle2 circle) { return LineCircle(line.origin, line.direction, circle.center, circle.radius, out IntersectionLineCircle intersection); } /// /// Computes an intersection of the line and the circle /// public static bool LineCircle(Line2 line, Circle2 circle, out IntersectionLineCircle intersection) { return LineCircle(line.origin, line.direction, circle.center, circle.radius, out intersection); } /// /// Computes an intersection of the line and the circle /// public static bool LineCircle(Vector2 lineOrigin, Vector2 lineDirection, Vector2 circleCenter, float circleRadius) { return LineCircle(lineOrigin, lineDirection, circleCenter, circleRadius, out IntersectionLineCircle intersection); } /// /// Computes an intersection of the line and the circle /// public static bool LineCircle(Vector2 lineOrigin, Vector2 lineDirection, Vector2 circleCenter, float circleRadius, out IntersectionLineCircle intersection) { Vector2 originToCenter = circleCenter - lineOrigin; float centerProjection = Vector2.Dot(lineDirection, originToCenter); float sqrDistanceToLine = originToCenter.sqrMagnitude - centerProjection*centerProjection; float sqrDistanceToIntersection = circleRadius*circleRadius - sqrDistanceToLine; if (sqrDistanceToIntersection < -Geometry.Epsilon) { intersection = IntersectionLineCircle.None(); return false; } if (sqrDistanceToIntersection < Geometry.Epsilon) { intersection = IntersectionLineCircle.Point(lineOrigin + lineDirection*centerProjection); return true; } float distanceToIntersection = Mathf.Sqrt(sqrDistanceToIntersection); float distanceA = centerProjection - distanceToIntersection; float distanceB = centerProjection + distanceToIntersection; Vector2 pointA = lineOrigin + lineDirection*distanceA; Vector2 pointB = lineOrigin + lineDirection*distanceB; intersection = IntersectionLineCircle.TwoPoints(pointA, pointB); return true; } #endregion Line-Circle #region Ray-Ray /// /// Computes an intersection of the rays /// public static bool RayRay(Ray2D rayA, Ray2D rayB) { return RayRay(rayA.origin, rayA.direction, rayB.origin, rayB.direction, out IntersectionRayRay2 intersection); } /// /// Computes an intersection of the rays /// public static bool RayRay(Ray2D rayA, Ray2D rayB, out IntersectionRayRay2 intersection) { return RayRay(rayA.origin, rayA.direction, rayB.origin, rayB.direction, out intersection); } /// /// Computes an intersection of the rays /// public static bool RayRay(Vector2 originA, Vector2 directionA, Vector2 originB, Vector2 directionB) { return RayRay(originA, directionA, originB, directionB, out IntersectionRayRay2 intersection); } /// /// Computes an intersection of the rays /// public static bool RayRay(Vector2 originA, Vector2 directionA, Vector2 originB, Vector2 directionB, out IntersectionRayRay2 intersection) { Vector2 originBToA = originA - originB; float denominator = VectorE.PerpDot(directionA, directionB); float perpDotA = VectorE.PerpDot(directionA, originBToA); float perpDotB = VectorE.PerpDot(directionB, originBToA); if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel if (Mathf.Abs(perpDotA) > Geometry.Epsilon || Mathf.Abs(perpDotB) > Geometry.Epsilon) { // Not collinear intersection = IntersectionRayRay2.None(); return false; } // Collinear bool codirected = Vector2.Dot(directionA, directionB) > 0; float originBProjection = -Vector2.Dot(directionA, originBToA); if (codirected) { intersection = IntersectionRayRay2.Ray(originBProjection > 0 ? originB : originA, directionA); return true; } else { if (originBProjection < -Geometry.Epsilon) { intersection = IntersectionRayRay2.None(); return false; } if (originBProjection < Geometry.Epsilon) { intersection = IntersectionRayRay2.Point(originA); return true; } intersection = IntersectionRayRay2.Segment(originA, originB); return true; } } // Not parallel float distanceA = perpDotB/denominator; if (distanceA < -Geometry.Epsilon) { intersection = IntersectionRayRay2.None(); return false; } float distanceB = perpDotA/denominator; if (distanceB < -Geometry.Epsilon) { intersection = IntersectionRayRay2.None(); return false; } intersection = IntersectionRayRay2.Point(originA + directionA*distanceA); return true; } #endregion Ray-Ray #region Ray-Segment /// /// Computes an intersection of the ray and the segment /// public static bool RaySegment(Ray2D ray, Segment2 segment) { return RaySegment(ray.origin, ray.direction, segment.a, segment.b, out IntersectionRaySegment2 intersection); } /// /// Computes an intersection of the ray and the segment /// public static bool RaySegment(Ray2D ray, Segment2 segment, out IntersectionRaySegment2 intersection) { return RaySegment(ray.origin, ray.direction, segment.a, segment.b, out intersection); } /// /// Computes an intersection of the ray and the segment /// public static bool RaySegment(Vector2 rayOrigin, Vector2 rayDirection, Vector2 segmentA, Vector2 segmentB) { return RaySegment(rayOrigin, rayDirection, segmentA, segmentB, out IntersectionRaySegment2 intersection); } /// /// Computes an intersection of the ray and the segment /// public static bool RaySegment(Vector2 rayOrigin, Vector2 rayDirection, Vector2 segmentA, Vector2 segmentB, out IntersectionRaySegment2 intersection) { Vector2 segmentAToOrigin = rayOrigin - segmentA; Vector2 segmentDirection = segmentB - segmentA; float denominator = VectorE.PerpDot(rayDirection, segmentDirection); float perpDotA = VectorE.PerpDot(rayDirection, segmentAToOrigin); // Normalized direction gives more stable results float perpDotB = VectorE.PerpDot(segmentDirection.normalized, segmentAToOrigin); if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel if (Mathf.Abs(perpDotA) > Geometry.Epsilon || Mathf.Abs(perpDotB) > Geometry.Epsilon) { // Not collinear intersection = IntersectionRaySegment2.None(); return false; } // Collinear bool segmentIsAPoint = segmentDirection.sqrMagnitude < Geometry.Epsilon; float segmentAProjection = Vector2.Dot(rayDirection, segmentA - rayOrigin); if (segmentIsAPoint) { if (segmentAProjection > -Geometry.Epsilon) { intersection = IntersectionRaySegment2.Point(segmentA); return true; } intersection = IntersectionRaySegment2.None(); return false; } float segmentBProjection = Vector2.Dot(rayDirection, segmentB - rayOrigin); if (segmentAProjection > -Geometry.Epsilon) { if (segmentBProjection > -Geometry.Epsilon) { if (segmentBProjection > segmentAProjection) { intersection = IntersectionRaySegment2.Segment(segmentA, segmentB); } else { intersection = IntersectionRaySegment2.Segment(segmentB, segmentA); } } else { if (segmentAProjection > Geometry.Epsilon) { intersection = IntersectionRaySegment2.Segment(rayOrigin, segmentA); } else { intersection = IntersectionRaySegment2.Point(rayOrigin); } } return true; } if (segmentBProjection > -Geometry.Epsilon) { if (segmentBProjection > Geometry.Epsilon) { intersection = IntersectionRaySegment2.Segment(rayOrigin, segmentB); } else { intersection = IntersectionRaySegment2.Point(rayOrigin); } return true; } intersection = IntersectionRaySegment2.None(); return false; } // Not parallel float rayDistance = perpDotB/denominator; float segmentDistance = perpDotA/denominator; if (rayDistance > -Geometry.Epsilon && segmentDistance > -Geometry.Epsilon && segmentDistance < 1 + Geometry.Epsilon) { intersection = IntersectionRaySegment2.Point(segmentA + segmentDirection*segmentDistance); return true; } intersection = IntersectionRaySegment2.None(); return false; } #endregion Ray-Segment #region Ray-Circle /// /// Computes an intersection of the ray and the circle /// public static bool RayCircle(Ray2D ray, Circle2 circle) { return RayCircle(ray.origin, ray.direction, circle.center, circle.radius, out IntersectionRayCircle intersection); } /// /// Computes an intersection of the ray and the circle /// public static bool RayCircle(Ray2D ray, Circle2 circle, out IntersectionRayCircle intersection) { return RayCircle(ray.origin, ray.direction, circle.center, circle.radius, out intersection); } /// /// Computes an intersection of the ray and the circle /// public static bool RayCircle(Vector2 rayOrigin, Vector2 rayDirection, Vector2 circleCenter, float circleRadius) { return RayCircle(rayOrigin, rayDirection, circleCenter, circleRadius, out IntersectionRayCircle intersection); } /// /// Computes an intersection of the ray and the circle /// public static bool RayCircle(Vector2 rayOrigin, Vector2 rayDirection, Vector2 circleCenter, float circleRadius, out IntersectionRayCircle intersection) { Vector2 originToCenter = circleCenter - rayOrigin; float centerProjection = Vector2.Dot(rayDirection, originToCenter); if (centerProjection + circleRadius < -Geometry.Epsilon) { intersection = IntersectionRayCircle.None(); return false; } float sqrDistanceToLine = originToCenter.sqrMagnitude - centerProjection*centerProjection; float sqrDistanceToIntersection = circleRadius*circleRadius - sqrDistanceToLine; if (sqrDistanceToIntersection < -Geometry.Epsilon) { intersection = IntersectionRayCircle.None(); return false; } if (sqrDistanceToIntersection < Geometry.Epsilon) { if (centerProjection < -Geometry.Epsilon) { intersection = IntersectionRayCircle.None(); return false; } intersection = IntersectionRayCircle.Point(rayOrigin + rayDirection*centerProjection); return true; } // Line intersection float distanceToIntersection = Mathf.Sqrt(sqrDistanceToIntersection); float distanceA = centerProjection - distanceToIntersection; float distanceB = centerProjection + distanceToIntersection; if (distanceA < -Geometry.Epsilon) { if (distanceB < -Geometry.Epsilon) { intersection = IntersectionRayCircle.None(); return false; } intersection = IntersectionRayCircle.Point(rayOrigin + rayDirection*distanceB); return true; } Vector2 pointA = rayOrigin + rayDirection*distanceA; Vector2 pointB = rayOrigin + rayDirection*distanceB; intersection = IntersectionRayCircle.TwoPoints(pointA, pointB); return true; } #endregion Ray-Circle #region Segment-Segment /// /// Computes an intersection of the segments /// public static bool SegmentSegment(Segment2 segment1, Segment2 segment2) { return SegmentSegment(segment1.a, segment1.b, segment2.a, segment2.b, out IntersectionSegmentSegment2 intersection); } /// /// Computes an intersection of the segments /// public static bool SegmentSegment(Segment2 segment1, Segment2 segment2, out IntersectionSegmentSegment2 intersection) { return SegmentSegment(segment1.a, segment1.b, segment2.a, segment2.b, out intersection); } /// /// Computes an intersection of the segments /// public static bool SegmentSegment(Vector2 segment1A, Vector2 segment1B, Vector2 segment2A, Vector2 segment2B) { return SegmentSegment(segment1A, segment1B, segment2A, segment2B, out IntersectionSegmentSegment2 intersection); } /// /// Computes an intersection of the segments /// public static bool SegmentSegment(Vector2 segment1A, Vector2 segment1B, Vector2 segment2A, Vector2 segment2B, out IntersectionSegmentSegment2 intersection) { Vector2 from2ATo1A = segment1A - segment2A; Vector2 direction1 = segment1B - segment1A; Vector2 direction2 = segment2B - segment2A; float sqrSegment1Length = direction1.sqrMagnitude; float sqrSegment2Length = direction2.sqrMagnitude; bool segment1IsAPoint = sqrSegment1Length < Geometry.Epsilon; bool segment2IsAPoint = sqrSegment2Length < Geometry.Epsilon; if (segment1IsAPoint && segment2IsAPoint) { if (segment1A == segment2A) { intersection = IntersectionSegmentSegment2.Point(segment1A); return true; } intersection = IntersectionSegmentSegment2.None(); return false; } if (segment1IsAPoint) { if (PointSegment(segment1A, segment2A, direction2, sqrSegment2Length)) { intersection = IntersectionSegmentSegment2.Point(segment1A); return true; } intersection = IntersectionSegmentSegment2.None(); return false; } if (segment2IsAPoint) { if (PointSegment(segment2A, segment1A, direction1, sqrSegment1Length)) { intersection = IntersectionSegmentSegment2.Point(segment2A); return true; } intersection = IntersectionSegmentSegment2.None(); return false; } float denominator = VectorE.PerpDot(direction1, direction2); float perpDot1 = VectorE.PerpDot(direction1, from2ATo1A); float perpDot2 = VectorE.PerpDot(direction2, from2ATo1A); if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel if (Mathf.Abs(perpDot1) > Geometry.Epsilon || Mathf.Abs(perpDot2) > Geometry.Epsilon) { // Not collinear intersection = IntersectionSegmentSegment2.None(); return false; } // Collinear bool codirected = Vector2.Dot(direction1, direction2) > 0; if (codirected) { // Codirected float segment2AProjection = -Vector2.Dot(direction1, from2ATo1A); if (segment2AProjection > -Geometry.Epsilon) { // 1A------1B // 2A------2B return SegmentSegmentCollinear(segment1A, segment1B, sqrSegment1Length, segment2A, segment2B, out intersection); } else { // 1A------1B // 2A------2B return SegmentSegmentCollinear(segment2A, segment2B, sqrSegment2Length, segment1A, segment1B, out intersection); } } else { // Contradirected float segment2BProjection = Vector2.Dot(direction1, segment2B - segment1A); if (segment2BProjection > -Geometry.Epsilon) { // 1A------1B // 2B------2A return SegmentSegmentCollinear(segment1A, segment1B, sqrSegment1Length, segment2B, segment2A, out intersection); } else { // 1A------1B // 2B------2A return SegmentSegmentCollinear(segment2B, segment2A, sqrSegment2Length, segment1A, segment1B, out intersection); } } } // Not parallel float distance1 = perpDot2/denominator; if (distance1 < -Geometry.Epsilon || distance1 > 1 + Geometry.Epsilon) { intersection = IntersectionSegmentSegment2.None(); return false; } float distance2 = perpDot1/denominator; if (distance2 < -Geometry.Epsilon || distance2 > 1 + Geometry.Epsilon) { intersection = IntersectionSegmentSegment2.None(); return false; } intersection = IntersectionSegmentSegment2.Point(segment1A + direction1*distance1); return true; } private static bool SegmentSegmentCollinear(Vector2 leftA, Vector2 leftB, float sqrLeftLength, Vector2 rightA, Vector2 rightB, out IntersectionSegmentSegment2 intersection) { Vector2 leftDirection = leftB - leftA; float rightAProjection = Vector2.Dot(leftDirection, rightA - leftB); if (Mathf.Abs(rightAProjection) < Geometry.Epsilon) { // LB == RA // LA------LB // RA------RB intersection = IntersectionSegmentSegment2.Point(leftB); return true; } if (rightAProjection < 0) { // LB > RA // LA------LB // RARB // RA--RB // RA------RB Vector2 pointB; float rightBProjection = Vector2.Dot(leftDirection, rightB - leftA); if (rightBProjection > sqrLeftLength) { pointB = leftB; } else { pointB = rightB; } intersection = IntersectionSegmentSegment2.Segment(rightA, pointB); return true; } // LB < RA // LA------LB // RA------RB intersection = IntersectionSegmentSegment2.None(); return false; } #endregion Segment-Segment #region Segment-Circle /// /// Computes an intersection of the segment and the circle /// public static bool SegmentCircle(Segment2 segment, Circle2 circle) { return SegmentCircle(segment.a, segment.b, circle.center, circle.radius, out IntersectionSegmentCircle intersection); } /// /// Computes an intersection of the segment and the circle /// public static bool SegmentCircle(Segment2 segment, Circle2 circle, out IntersectionSegmentCircle intersection) { return SegmentCircle(segment.a, segment.b, circle.center, circle.radius, out intersection); } /// /// Computes an intersection of the segment and the circle /// public static bool SegmentCircle(Vector2 segmentA, Vector2 segmentB, Vector2 circleCenter, float circleRadius) { return SegmentCircle(segmentA, segmentB, circleCenter, circleRadius, out IntersectionSegmentCircle intersection); } /// /// Computes an intersection of the segment and the circle /// public static bool SegmentCircle(Vector2 segmentA, Vector2 segmentB, Vector2 circleCenter, float circleRadius, out IntersectionSegmentCircle intersection) { Vector2 segmentAToCenter = circleCenter - segmentA; Vector2 fromAtoB = segmentB - segmentA; float segmentLength = fromAtoB.magnitude; if (segmentLength < Geometry.Epsilon) { float distanceToPoint = segmentAToCenter.magnitude; if (distanceToPoint < circleRadius + Geometry.Epsilon) { if (distanceToPoint > circleRadius - Geometry.Epsilon) { intersection = IntersectionSegmentCircle.Point(segmentA); return true; } intersection = IntersectionSegmentCircle.None(); return true; } intersection = IntersectionSegmentCircle.None(); return false; } Vector2 segmentDirection = fromAtoB.normalized; float centerProjection = Vector2.Dot(segmentDirection, segmentAToCenter); if (centerProjection + circleRadius < -Geometry.Epsilon || centerProjection - circleRadius > segmentLength + Geometry.Epsilon) { intersection = IntersectionSegmentCircle.None(); return false; } float sqrDistanceToLine = segmentAToCenter.sqrMagnitude - centerProjection*centerProjection; float sqrDistanceToIntersection = circleRadius*circleRadius - sqrDistanceToLine; if (sqrDistanceToIntersection < -Geometry.Epsilon) { intersection = IntersectionSegmentCircle.None(); return false; } if (sqrDistanceToIntersection < Geometry.Epsilon) { if (centerProjection < -Geometry.Epsilon || centerProjection > segmentLength + Geometry.Epsilon) { intersection = IntersectionSegmentCircle.None(); return false; } intersection = IntersectionSegmentCircle.Point(segmentA + segmentDirection*centerProjection); return true; } // Line intersection float distanceToIntersection = Mathf.Sqrt(sqrDistanceToIntersection); float distanceA = centerProjection - distanceToIntersection; float distanceB = centerProjection + distanceToIntersection; bool pointAIsAfterSegmentA = distanceA > -Geometry.Epsilon; bool pointBIsBeforeSegmentB = distanceB < segmentLength + Geometry.Epsilon; if (pointAIsAfterSegmentA && pointBIsBeforeSegmentB) { Vector2 pointA = segmentA + segmentDirection*distanceA; Vector2 pointB = segmentA + segmentDirection*distanceB; intersection = IntersectionSegmentCircle.TwoPoints(pointA, pointB); return true; } if (!pointAIsAfterSegmentA && !pointBIsBeforeSegmentB) { // The segment is inside, but no intersection intersection = IntersectionSegmentCircle.None(); return true; } bool pointAIsBeforeSegmentB = distanceA < segmentLength + Geometry.Epsilon; if (pointAIsAfterSegmentA && pointAIsBeforeSegmentB) { // Point A intersection intersection = IntersectionSegmentCircle.Point(segmentA + segmentDirection*distanceA); return true; } bool pointBIsAfterSegmentA = distanceB > -Geometry.Epsilon; if (pointBIsAfterSegmentA && pointBIsBeforeSegmentB) { // Point B intersection intersection = IntersectionSegmentCircle.Point(segmentA + segmentDirection*distanceB); return true; } intersection = IntersectionSegmentCircle.None(); return false; } #endregion Segment-Circle #region Circle-Circle /// /// Computes an intersection of the circles /// /// True if the circles intersect or one circle is contained within the other public static bool CircleCircle(Circle2 circleA, Circle2 circleB) { return CircleCircle(circleA.center, circleA.radius, circleB.center, circleB.radius, out IntersectionCircleCircle intersection); } /// /// Computes an intersection of the circles /// /// True if the circles intersect or one circle is contained within the other public static bool CircleCircle(Circle2 circleA, Circle2 circleB, out IntersectionCircleCircle intersection) { return CircleCircle(circleA.center, circleA.radius, circleB.center, circleB.radius, out intersection); } /// /// Computes an intersection of the circles /// /// True if the circles intersect or one circle is contained within the other public static bool CircleCircle(Vector2 centerA, float radiusA, Vector2 centerB, float radiusB) { return CircleCircle(centerA, radiusA, centerB, radiusB, out IntersectionCircleCircle intersection); } /// /// Computes an intersection of the circles /// /// True if the circles intersect or one circle is contained within the other public static bool CircleCircle(Vector2 centerA, float radiusA, Vector2 centerB, float radiusB, out IntersectionCircleCircle intersection) { Vector2 fromBtoA = centerA - centerB; float distanceFromBtoASqr = fromBtoA.sqrMagnitude; if (distanceFromBtoASqr < Geometry.Epsilon) { if (Mathf.Abs(radiusA - radiusB) < Geometry.Epsilon) { // Circles are coincident intersection = IntersectionCircleCircle.Circle(); return true; } // One circle is inside the other intersection = IntersectionCircleCircle.None(); return true; } // For intersections on the circle's edge magnitude is more stable than sqrMagnitude float distanceFromBtoA = Mathf.Sqrt(distanceFromBtoASqr); float sumOfRadii = radiusA + radiusB; if (Mathf.Abs(distanceFromBtoA - sumOfRadii) < Geometry.Epsilon) { // One intersection outside intersection = IntersectionCircleCircle.Point(centerB + fromBtoA*(radiusB/sumOfRadii)); return true; } if (distanceFromBtoA > sumOfRadii) { // No intersections, circles are separate intersection = IntersectionCircleCircle.None(); return false; } float differenceOfRadii = radiusA - radiusB; float differenceOfRadiiAbs = Mathf.Abs(differenceOfRadii); if (Mathf.Abs(distanceFromBtoA - differenceOfRadiiAbs) < Geometry.Epsilon) { // One intersection inside intersection = IntersectionCircleCircle.Point(centerB - fromBtoA*(radiusB/differenceOfRadii)); return true; } if (distanceFromBtoA < differenceOfRadiiAbs) { // One circle is contained within the other intersection = IntersectionCircleCircle.None(); return true; } // Two intersections float radiusASqr = radiusA*radiusA; float distanceToMiddle = 0.5f*(radiusASqr - radiusB*radiusB)/distanceFromBtoASqr + 0.5f; Vector2 middle = centerA - fromBtoA*distanceToMiddle; float discriminant = radiusASqr/distanceFromBtoASqr - distanceToMiddle*distanceToMiddle; Vector2 offset = fromBtoA.RotateCCW90()*Mathf.Sqrt(discriminant); intersection = IntersectionCircleCircle.TwoPoints(middle + offset, middle - offset); return true; } #endregion Circle-Circle } }