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(Vector3 point, Line3 line) { return PointLine(point, line.origin, line.direction); } /// /// Tests if the point lies on the line /// public static bool PointLine(Vector3 point, Vector3 lineOrigin, Vector3 lineDirection) { return Distance.PointLine(point, lineOrigin, lineDirection) < Geometry.Epsilon; } #endregion Point-Line #region Point-Ray /// /// Tests if the point lies on the ray /// public static bool PointRay(Vector3 point, Ray ray) { return PointRay(point, ray.origin, ray.direction); } /// /// Tests if the point lies on the ray /// public static bool PointRay(Vector3 point, Vector3 rayOrigin, Vector3 rayDirection) { return Distance.PointRay(point, rayOrigin, rayDirection) < Geometry.Epsilon; } #endregion Point-Ray #region Point-Segment /// /// Tests if the point lies on the segment /// public static bool PointSegment(Vector3 point, Segment3 segment) { return PointSegment(point, segment.a, segment.b); } /// /// Tests if the point lies on the segment /// public static bool PointSegment(Vector3 point, Vector3 segmentA, Vector3 segmentB) { return Distance.PointSegment(point, segmentA, segmentB) < Geometry.Epsilon; } #endregion Point-Segment #region Point-Sphere /// /// Tests if the point is inside the sphere /// public static bool PointSphere(Vector3 point, Sphere sphere) { return PointSphere(point, sphere.center, sphere.radius); } /// /// Tests if the point is inside the sphere /// public static bool PointSphere(Vector3 point, Vector3 sphereCenter, float sphereRadius) { // For points on the sphere's surface magnitude is more stable than sqrMagnitude return (point - sphereCenter).magnitude < sphereRadius + Geometry.Epsilon; } #endregion Point-Sphere #region Line-Line /// /// Computes an intersection of the lines /// public static bool LineLine(Line3 lineA, Line3 lineB) { return LineLine(lineA.origin, lineA.direction, lineB.origin, lineB.direction, out Vector3 intersection); } /// /// Computes an intersection of the lines /// public static bool LineLine(Line3 lineA, Line3 lineB, out Vector3 intersection) { return LineLine(lineA.origin, lineA.direction, lineB.origin, lineB.direction, out intersection); } /// /// Computes an intersection of the lines /// public static bool LineLine(Vector3 originA, Vector3 directionA, Vector3 originB, Vector3 directionB) { return LineLine(originA, directionA, originB, directionB, out Vector3 intersection); } /// /// Computes an intersection of the lines /// public static bool LineLine(Vector3 originA, Vector3 directionA, Vector3 originB, Vector3 directionB, out Vector3 intersection) { float sqrMagnitudeA = directionA.sqrMagnitude; float sqrMagnitudeB = directionB.sqrMagnitude; float dotAB = Vector3.Dot(directionA, directionB); float denominator = sqrMagnitudeA*sqrMagnitudeB - dotAB*dotAB; Vector3 originBToA = originA - originB; float a = Vector3.Dot(directionA, originBToA); float b = Vector3.Dot(directionB, originBToA); Vector3 closestPointA; Vector3 closestPointB; if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel float distanceB = dotAB > sqrMagnitudeB ? a/dotAB : b/sqrMagnitudeB; closestPointA = originA; closestPointB = originB + directionB*distanceB; } else { // Not parallel float distanceA = (sqrMagnitudeA*b - dotAB*a)/denominator; float distanceB = (dotAB*b - sqrMagnitudeB*a)/denominator; closestPointA = originA + directionA*distanceA; closestPointB = originB + directionB*distanceB; } if ((closestPointB - closestPointA).sqrMagnitude < Geometry.Epsilon) { intersection = closestPointA; return true; } intersection = Vector3.zero; return false; } #endregion Line-Line #region Line-Sphere /// /// Computes an intersection of the line and the sphere /// public static bool LineSphere(Line3 line, Sphere sphere) { return LineSphere(line.origin, line.direction, sphere.center, sphere.radius, out IntersectionLineSphere intersection); } /// /// Computes an intersection of the line and the sphere /// public static bool LineSphere(Line3 line, Sphere sphere, out IntersectionLineSphere intersection) { return LineSphere(line.origin, line.direction, sphere.center, sphere.radius, out intersection); } /// /// Computes an intersection of the line and the sphere /// public static bool LineSphere(Vector3 lineOrigin, Vector3 lineDirection, Vector3 sphereCenter, float sphereRadius) { return LineSphere(lineOrigin, lineDirection, sphereCenter, sphereRadius, out IntersectionLineSphere intersection); } /// /// Computes an intersection of the line and the sphere /// public static bool LineSphere(Vector3 lineOrigin, Vector3 lineDirection, Vector3 sphereCenter, float sphereRadius, out IntersectionLineSphere intersection) { Vector3 originToCenter = sphereCenter - lineOrigin; float centerProjection = Vector3.Dot(lineDirection, originToCenter); float sqrDistanceToLine = originToCenter.sqrMagnitude - centerProjection*centerProjection; float sqrDistanceToIntersection = sphereRadius*sphereRadius - sqrDistanceToLine; if (sqrDistanceToIntersection < -Geometry.Epsilon) { intersection = IntersectionLineSphere.None(); return false; } if (sqrDistanceToIntersection < Geometry.Epsilon) { intersection = IntersectionLineSphere.Point(lineOrigin + lineDirection*centerProjection); return true; } float distanceToIntersection = Mathf.Sqrt(sqrDistanceToIntersection); float distanceA = centerProjection - distanceToIntersection; float distanceB = centerProjection + distanceToIntersection; Vector3 pointA = lineOrigin + lineDirection*distanceA; Vector3 pointB = lineOrigin + lineDirection*distanceB; intersection = IntersectionLineSphere.TwoPoints(pointA, pointB); return true; } #endregion Line-Sphere #region Ray-Sphere /// /// Computes an intersection of the ray and the sphere /// public static bool RaySphere(Ray ray, Sphere sphere) { return RaySphere(ray.origin, ray.direction, sphere.center, sphere.radius, out IntersectionRaySphere intersection); } /// /// Computes an intersection of the ray and the sphere /// public static bool RaySphere(Ray ray, Sphere sphere, out IntersectionRaySphere intersection) { return RaySphere(ray.origin, ray.direction, sphere.center, sphere.radius, out intersection); } /// /// Computes an intersection of the ray and the sphere /// public static bool RaySphere(Vector3 rayOrigin, Vector3 rayDirection, Vector3 sphereCenter, float sphereRadius) { return RaySphere(rayOrigin, rayDirection, sphereCenter, sphereRadius, out IntersectionRaySphere intersection); } /// /// Computes an intersection of the ray and the sphere /// public static bool RaySphere(Vector3 rayOrigin, Vector3 rayDirection, Vector3 sphereCenter, float sphereRadius, out IntersectionRaySphere intersection) { Vector3 originToCenter = sphereCenter - rayOrigin; float centerProjection = Vector3.Dot(rayDirection, originToCenter); if (centerProjection + sphereRadius < -Geometry.Epsilon) { intersection = IntersectionRaySphere.None(); return false; } float sqrDistanceToLine = originToCenter.sqrMagnitude - centerProjection*centerProjection; float sqrDistanceToIntersection = sphereRadius*sphereRadius - sqrDistanceToLine; if (sqrDistanceToIntersection < -Geometry.Epsilon) { intersection = IntersectionRaySphere.None(); return false; } if (sqrDistanceToIntersection < Geometry.Epsilon) { if (centerProjection < -Geometry.Epsilon) { intersection = IntersectionRaySphere.None(); return false; } intersection = IntersectionRaySphere.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 = IntersectionRaySphere.None(); return false; } intersection = IntersectionRaySphere.Point(rayOrigin + rayDirection*distanceB); return true; } Vector3 pointA = rayOrigin + rayDirection*distanceA; Vector3 pointB = rayOrigin + rayDirection*distanceB; intersection = IntersectionRaySphere.TwoPoints(pointA, pointB); return true; } #endregion Ray-Sphere #region Segment-Sphere /// /// Computes an intersection of the segment and the sphere /// public static bool SegmentSphere(Segment3 segment, Sphere sphere) { return SegmentSphere(segment.a, segment.b, sphere.center, sphere.radius, out IntersectionSegmentSphere intersection); } /// /// Computes an intersection of the segment and the sphere /// public static bool SegmentSphere(Segment3 segment, Sphere sphere, out IntersectionSegmentSphere intersection) { return SegmentSphere(segment.a, segment.b, sphere.center, sphere.radius, out intersection); } /// /// Computes an intersection of the segment and the sphere /// public static bool SegmentSphere(Vector3 segmentA, Vector3 segmentB, Vector3 sphereCenter, float sphereRadius) { return SegmentSphere(segmentA, segmentB, sphereCenter, sphereRadius, out IntersectionSegmentSphere intersection); } /// /// Computes an intersection of the segment and the sphere /// public static bool SegmentSphere(Vector3 segmentA, Vector3 segmentB, Vector3 sphereCenter, float sphereRadius, out IntersectionSegmentSphere intersection) { Vector3 segmentAToCenter = sphereCenter - segmentA; Vector3 fromAtoB = segmentB - segmentA; float segmentLength = fromAtoB.magnitude; if (segmentLength < Geometry.Epsilon) { float distanceToPoint = segmentAToCenter.magnitude; if (distanceToPoint < sphereRadius + Geometry.Epsilon) { if (distanceToPoint > sphereRadius - Geometry.Epsilon) { intersection = IntersectionSegmentSphere.Point(segmentA); return true; } intersection = IntersectionSegmentSphere.None(); return true; } intersection = IntersectionSegmentSphere.None(); return false; } Vector3 segmentDirection = fromAtoB.normalized; float centerProjection = Vector3.Dot(segmentDirection, segmentAToCenter); if (centerProjection + sphereRadius < -Geometry.Epsilon || centerProjection - sphereRadius > segmentLength + Geometry.Epsilon) { intersection = IntersectionSegmentSphere.None(); return false; } float sqrDistanceToLine = segmentAToCenter.sqrMagnitude - centerProjection*centerProjection; float sqrDistanceToIntersection = sphereRadius*sphereRadius - sqrDistanceToLine; if (sqrDistanceToIntersection < -Geometry.Epsilon) { intersection = IntersectionSegmentSphere.None(); return false; } if (sqrDistanceToIntersection < Geometry.Epsilon) { if (centerProjection < -Geometry.Epsilon || centerProjection > segmentLength + Geometry.Epsilon) { intersection = IntersectionSegmentSphere.None(); return false; } intersection = IntersectionSegmentSphere.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) { Vector3 pointA = segmentA + segmentDirection*distanceA; Vector3 pointB = segmentA + segmentDirection*distanceB; intersection = IntersectionSegmentSphere.TwoPoints(pointA, pointB); return true; } if (!pointAIsAfterSegmentA && !pointBIsBeforeSegmentB) { // The segment is inside, but no intersection intersection = IntersectionSegmentSphere.None(); return true; } bool pointAIsBeforeSegmentB = distanceA < segmentLength + Geometry.Epsilon; if (pointAIsAfterSegmentA && pointAIsBeforeSegmentB) { // Point A intersection intersection = IntersectionSegmentSphere.Point(segmentA + segmentDirection*distanceA); return true; } bool pointBIsAfterSegmentA = distanceB > -Geometry.Epsilon; if (pointBIsAfterSegmentA && pointBIsBeforeSegmentB) { // Point B intersection intersection = IntersectionSegmentSphere.Point(segmentA + segmentDirection*distanceB); return true; } intersection = IntersectionSegmentSphere.None(); return false; } #endregion Segment-Sphere #region Sphere-Sphere /// /// Computes an intersection of the spheres /// /// True if the spheres intersect or one sphere is contained within the other public static bool SphereSphere(Sphere sphereA, Sphere sphereB) { return SphereSphere(sphereA.center, sphereA.radius, sphereB.center, sphereB.radius, out IntersectionSphereSphere intersection); } /// /// Computes an intersection of the spheres /// /// True if the spheres intersect or one sphere is contained within the other public static bool SphereSphere(Sphere sphereA, Sphere sphereB, out IntersectionSphereSphere intersection) { return SphereSphere(sphereA.center, sphereA.radius, sphereB.center, sphereB.radius, out intersection); } /// /// Computes an intersection of the spheres /// /// True if the spheres intersect or one sphere is contained within the other public static bool SphereSphere(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB) { return SphereSphere(centerA, radiusA, centerB, radiusB, out IntersectionSphereSphere intersection); } /// /// Computes an intersection of the spheres /// /// True if the spheres intersect or one sphere is contained within the other public static bool SphereSphere(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB, out IntersectionSphereSphere intersection) { Vector3 fromBtoA = centerA - centerB; float distanceFromBtoASqr = fromBtoA.sqrMagnitude; if (distanceFromBtoASqr < Geometry.Epsilon) { if (Mathf.Abs(radiusA - radiusB) < Geometry.Epsilon) { // Spheres are coincident intersection = IntersectionSphereSphere.Sphere(centerA, radiusA); return true; } // One sphere is inside the other intersection = IntersectionSphereSphere.None(); return true; } // For intersections on the sphere'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 = IntersectionSphereSphere.Point(centerB + fromBtoA*(radiusB/sumOfRadii)); return true; } if (distanceFromBtoA > sumOfRadii) { // No intersections, spheres are separate intersection = IntersectionSphereSphere.None(); return false; } float differenceOfRadii = radiusA - radiusB; float differenceOfRadiiAbs = Mathf.Abs(differenceOfRadii); if (Mathf.Abs(distanceFromBtoA - differenceOfRadiiAbs) < Geometry.Epsilon) { // One intersection inside intersection = IntersectionSphereSphere.Point(centerB - fromBtoA*(radiusB/differenceOfRadii)); return true; } if (distanceFromBtoA < differenceOfRadiiAbs) { // One sphere is contained within the other intersection = IntersectionSphereSphere.None(); return true; } // Circle intersection float radiusASqr = radiusA*radiusA; float distanceToMiddle = 0.5f*(radiusASqr - radiusB*radiusB)/distanceFromBtoASqr + 0.5f; Vector3 middle = centerA - fromBtoA*distanceToMiddle; float discriminant = radiusASqr/distanceFromBtoASqr - distanceToMiddle*distanceToMiddle; float radius = distanceFromBtoA*Mathf.Sqrt(discriminant); intersection = IntersectionSphereSphere.Circle(middle, -fromBtoA.normalized, radius); return true; } #endregion Sphere-Sphere } }