using UnityEngine; namespace ProceduralToolkit { /// /// Collection of distance calculation algorithms /// public static partial class Distance { #region Point-Line /// /// Returns a distance to the closest point on the line /// public static float PointLine(Vector3 point, Line3 line) { return Vector3.Distance(point, Closest.PointLine(point, line)); } /// /// Returns a distance to the closest point on the line /// public static float PointLine(Vector3 point, Vector3 lineOrigin, Vector3 lineDirection) { return Vector3.Distance(point, Closest.PointLine(point, lineOrigin, lineDirection)); } #endregion Point-Line #region Point-Ray /// /// Returns a distance to the closest point on the ray /// public static float PointRay(Vector3 point, Ray ray) { return Vector3.Distance(point, Closest.PointRay(point, ray)); } /// /// Returns a distance to the closest point on the ray /// public static float PointRay(Vector3 point, Vector3 rayOrigin, Vector3 rayDirection) { return Vector3.Distance(point, Closest.PointRay(point, rayOrigin, rayDirection)); } #endregion Point-Ray #region Point-Segment /// /// Returns a distance to the closest point on the segment /// public static float PointSegment(Vector3 point, Segment3 segment) { return Vector3.Distance(point, Closest.PointSegment(point, segment)); } /// /// Returns a distance to the closest point on the segment /// public static float PointSegment(Vector3 point, Vector3 segmentA, Vector3 segmentB) { return Vector3.Distance(point, Closest.PointSegment(point, segmentA, segmentB)); } #endregion Point-Segment #region Point-Sphere /// /// Returns a distance to the closest point on the sphere /// /// Positive value if the point is outside, negative otherwise public static float PointSphere(Vector3 point, Sphere sphere) { return PointSphere(point, sphere.center, sphere.radius); } /// /// Returns a distance to the closest point on the sphere /// /// Positive value if the point is outside, negative otherwise public static float PointSphere(Vector3 point, Vector3 sphereCenter, float sphereRadius) { return (sphereCenter - point).magnitude - sphereRadius; } #endregion Point-Sphere #region Line-Sphere /// /// Returns the distance between the closest points on the line and the sphere /// public static float LineSphere(Line3 line, Sphere sphere) { return LineSphere(line.origin, line.direction, sphere.center, sphere.radius); } /// /// Returns the distance between the closest points on the line and the sphere /// public static float LineSphere(Vector3 lineOrigin, Vector3 lineDirection, Vector3 sphereCenter, float sphereRadius) { 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) { // No intersection return Mathf.Sqrt(sqrDistanceToLine) - sphereRadius; } return 0; } #endregion Line-Sphere #region Ray-Sphere /// /// Returns the distance between the closest points on the ray and the sphere /// public static float RaySphere(Ray ray, Sphere sphere) { return RaySphere(ray.origin, ray.direction, sphere.center, sphere.radius); } /// /// Returns the distance between the closest points on the ray and the sphere /// public static float RaySphere(Vector3 rayOrigin, Vector3 rayDirection, Vector3 sphereCenter, float sphereRadius) { Vector3 originToCenter = sphereCenter - rayOrigin; float centerProjection = Vector3.Dot(rayDirection, originToCenter); if (centerProjection + sphereRadius < -Geometry.Epsilon) { // No intersection return Mathf.Sqrt(originToCenter.sqrMagnitude) - sphereRadius; } float sqrDistanceToOrigin = originToCenter.sqrMagnitude; float sqrDistanceToLine = sqrDistanceToOrigin - centerProjection*centerProjection; float sqrDistanceToIntersection = sphereRadius*sphereRadius - sqrDistanceToLine; if (sqrDistanceToIntersection < -Geometry.Epsilon) { // No intersection if (centerProjection < -Geometry.Epsilon) { return Mathf.Sqrt(sqrDistanceToOrigin) - sphereRadius; } return Mathf.Sqrt(sqrDistanceToLine) - sphereRadius; } if (sqrDistanceToIntersection < Geometry.Epsilon) { if (centerProjection < -Geometry.Epsilon) { // No intersection return Mathf.Sqrt(sqrDistanceToOrigin) - sphereRadius; } // Point intersection return 0; } // Line intersection float distanceToIntersection = Mathf.Sqrt(sqrDistanceToIntersection); float distanceA = centerProjection - distanceToIntersection; float distanceB = centerProjection + distanceToIntersection; if (distanceA < -Geometry.Epsilon) { if (distanceB < -Geometry.Epsilon) { // No intersection return Mathf.Sqrt(sqrDistanceToOrigin) - sphereRadius; } // Point intersection; return 0; } // Two points intersection; return 0; } #endregion Ray-Sphere #region Segment-Sphere /// /// Returns the distance between the closest points on the segment and the sphere /// public static float SegmentSphere(Segment3 segment, Sphere sphere) { return SegmentSphere(segment.a, segment.b, sphere.center, sphere.radius); } /// /// Returns the distance between the closest points on the segment and the sphere /// public static float SegmentSphere(Vector3 segmentA, Vector3 segmentB, Vector3 sphereCenter, float sphereRadius) { Vector3 segmentAToCenter = sphereCenter - segmentA; Vector3 fromAtoB = segmentB - segmentA; float segmentLength = fromAtoB.magnitude; if (segmentLength < Geometry.Epsilon) { return segmentAToCenter.magnitude - sphereRadius; } Vector3 segmentDirection = fromAtoB.normalized; float centerProjection = Vector3.Dot(segmentDirection, segmentAToCenter); if (centerProjection + sphereRadius < -Geometry.Epsilon || centerProjection - sphereRadius > segmentLength + Geometry.Epsilon) { // No intersection if (centerProjection < 0) { return segmentAToCenter.magnitude - sphereRadius; } return (sphereCenter - segmentB).magnitude - sphereRadius; } float sqrDistanceToA = segmentAToCenter.sqrMagnitude; float sqrDistanceToLine = sqrDistanceToA - centerProjection*centerProjection; float sqrDistanceToIntersection = sphereRadius*sphereRadius - sqrDistanceToLine; if (sqrDistanceToIntersection < -Geometry.Epsilon) { // No intersection if (centerProjection < -Geometry.Epsilon) { return Mathf.Sqrt(sqrDistanceToA) - sphereRadius; } if (centerProjection > segmentLength + Geometry.Epsilon) { return (sphereCenter - segmentB).magnitude - sphereRadius; } return Mathf.Sqrt(sqrDistanceToLine) - sphereRadius; } if (sqrDistanceToIntersection < Geometry.Epsilon) { if (centerProjection < -Geometry.Epsilon) { // No intersection return Mathf.Sqrt(sqrDistanceToA) - sphereRadius; } if (centerProjection > segmentLength + Geometry.Epsilon) { // No intersection return (sphereCenter - segmentB).magnitude - sphereRadius; } // Point intersection return 0; } // 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) { // Two points intersection return 0; } if (!pointAIsAfterSegmentA && !pointBIsBeforeSegmentB) { // The segment is inside, but no intersection distanceB = -(distanceB - segmentLength); return distanceA > distanceB ? distanceA : distanceB; } bool pointAIsBeforeSegmentB = distanceA < segmentLength + Geometry.Epsilon; if (pointAIsAfterSegmentA && pointAIsBeforeSegmentB) { // Point A intersection return 0; } bool pointBIsAfterSegmentA = distanceB > -Geometry.Epsilon; if (pointBIsAfterSegmentA && pointBIsBeforeSegmentB) { // Point B intersection return 0; } // No intersection if (centerProjection < 0) { return Mathf.Sqrt(sqrDistanceToA) - sphereRadius; } return (sphereCenter - segmentB).magnitude - sphereRadius; } #endregion Segment-Sphere #region Sphere-Sphere /// /// Returns the distance between the closest points on the spheres /// /// /// Positive value if the spheres do not intersect, negative otherwise. /// Negative value can be interpreted as depth of penetration. /// public static float SphereSphere(Sphere sphereA, Sphere sphereB) { return SphereSphere(sphereA.center, sphereA.radius, sphereB.center, sphereB.radius); } /// /// Returns the distance between the closest points on the spheres /// /// /// Positive value if the spheres do not intersect, negative otherwise. /// Negative value can be interpreted as depth of penetration. /// public static float SphereSphere(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB) { return Vector3.Distance(centerA, centerB) - radiusA - radiusB; } #endregion Sphere-Sphere } }