/// <summary> /// Returns the distance between the closest points on the line and the ray /// </summary> public static float LineRay(Vector2 lineOrigin, Vector2 lineDirection, Vector2 rayOrigin, Vector2 rayDirection) { 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 float rayOriginProjection = lineDirection.Dot(rayOriginToLineOrigin); float distanceSqr = rayOriginToLineOrigin.LengthSquared() - rayOriginProjection * rayOriginProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } // Collinear return(0); } // Not parallel float rayDistance = perpDotA / denominator; if (rayDistance < -Geometry.Epsilon) { // No intersection float rayOriginProjection = lineDirection.Dot(rayOriginToLineOrigin); Vector2 linePoint = lineOrigin - lineDirection * rayOriginProjection; return(linePoint.DistanceTo(rayOrigin)); } // Point intersection return(0); }
/// <summary> /// Returns the value of an angle. Assumes clockwise order of the polygon. /// </summary> /// <param name="previous">Previous vertex</param> /// <param name="current">Current vertex</param> /// <param name="next">Next vertex</param> public static float GetAngle(Vector2 previous, Vector2 current, Vector2 next) { Vector2 toPrevious = (previous - current).normalized; Vector2 toNext = (next - current).normalized; return(VectorE.Angle360(toNext, toPrevious)); }
/// <summary> /// Returns the bisector of an angle. Assumes clockwise order of the polygon. /// </summary> /// <param name="previous">Previous vertex</param> /// <param name="current">Current vertex</param> /// <param name="next">Next vertex</param> /// <param name="degrees">Value of the angle in degrees. Always positive.</param> public static Vector2 GetAngleBisector(Vector2 previous, Vector2 current, Vector2 next, out float degrees) { Vector2 toPrevious = (previous - current).normalized; Vector2 toNext = (next - current).normalized; degrees = VectorE.Angle360(toNext, toPrevious); Assert.IsFalse(float.IsNaN(degrees)); return(toNext.RotateCW(degrees / 2)); }
/// <summary> /// Returns the distance between the closest points on the line and the segment /// </summary> public static float LineSegment(Vector2 lineOrigin, Vector2 lineDirection, Vector2 segmentA, Vector2 segmentB) { 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 float segmentAProjection = lineDirection.Dot(segmentAToOrigin); float distanceSqr = segmentAToOrigin.LengthSquared() - segmentAProjection * segmentAProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } // Collinear return(0); } // Not parallel float segmentDistance = perpDotA / denominator; if (segmentDistance < -Geometry.Epsilon || segmentDistance > 1 + Geometry.Epsilon) { // No intersection Vector2 segmentPoint = segmentA + segmentDirection * Mathf.Clamp(segmentDistance, 0, 1); float segmentPointProjection = lineDirection.Dot(segmentPoint - lineOrigin); Vector2 linePoint = lineOrigin + lineDirection * segmentPointProjection; return(linePoint.DistanceTo(segmentPoint)); } // Point intersection return(0); }
/// <summary> /// Returns the distance between the closest points on the lines /// </summary> public static float LineLine(Vector2 originA, Vector2 directionA, Vector2 originB, Vector2 directionB) { if (Mathf.Abs(VectorE.PerpDot(directionA, directionB)) < Geometry.Epsilon) { // Parallel Vector2 originBToA = originA - originB; if (Mathf.Abs(VectorE.PerpDot(directionA, originBToA)) > Geometry.Epsilon || Mathf.Abs(VectorE.PerpDot(directionB, originBToA)) > Geometry.Epsilon) { // Not collinear float originBProjection = directionA.Dot(originBToA); float distanceSqr = originBToA.LengthSquared() - originBProjection * originBProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } // Collinear return(0); } // Not parallel return(0); }
/// <summary> /// Returns the distance between the closest points on the segments /// </summary> public static float SegmentSegment(Vector2 segment1A, Vector2 segment1B, Vector2 segment2A, Vector2 segment2B) { Vector2 from2ATo1A = segment1A - segment2A; Vector2 direction1 = segment1B - segment1A; Vector2 direction2 = segment2B - segment2A; float segment1Length = direction1.Length(); float segment2Length = direction2.Length(); bool segment1IsAPoint = segment1Length < Geometry.Epsilon; bool segment2IsAPoint = segment2Length < Geometry.Epsilon; if (segment1IsAPoint && segment2IsAPoint) { return(segment1A.DistanceTo(segment2A)); } if (segment1IsAPoint) { direction2.Normalized(); return(PointSegment(segment1A, segment2A, segment2B, direction2, segment2Length)); } if (segment2IsAPoint) { direction1.Normalized(); return(PointSegment(segment2A, segment1A, segment1B, direction1, segment1Length)); } direction1.Normalized(); direction2.Normalized(); 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 float segment2AProjection = -direction1.Dot(from2ATo1A); if (segment2AProjection > -Geometry.Epsilon && segment2AProjection < segment1Length + Geometry.Epsilon) { float distanceSqr = from2ATo1A.LengthSquared() - segment2AProjection * segment2AProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } Vector2 from1ATo2B = segment2B - segment1A; float segment2BProjection = direction1.Dot(from1ATo2B); if (segment2BProjection > -Geometry.Epsilon && segment2BProjection < segment1Length + Geometry.Epsilon) { float distanceSqr = from1ATo2B.LengthSquared() - segment2BProjection * segment2BProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } if (segment2AProjection < 0 && segment2BProjection < 0) { if (segment2AProjection > segment2BProjection) { return(segment1A.DistanceTo(segment2A)); } return(segment1A.DistanceTo(segment2B)); } if (segment2AProjection > 0 && segment2BProjection > 0) { if (segment2AProjection < segment2BProjection) { return(segment1B.DistanceTo(segment2A)); } return(segment1B.DistanceTo(segment2B)); } float segment1AProjection = direction2.Dot(from2ATo1A); Vector2 segment2Point = segment2A + direction2 * segment1AProjection; return(segment1A.DistanceTo(segment2Point)); } // Collinear bool codirected = direction1.Dot(direction2) > 0; if (codirected) { // Codirected float segment2AProjection = -direction1.Dot(from2ATo1A); if (segment2AProjection > -Geometry.Epsilon) { // 1A------1B // 2A------2B return(SegmentSegmentCollinear(segment1A, segment1B, segment2A)); } else { // 1A------1B // 2A------2B return(SegmentSegmentCollinear(segment2A, segment2B, segment1A)); } } else { // Contradirected float segment2BProjection = direction1.Dot(segment2B - segment1A); if (segment2BProjection > -Geometry.Epsilon) { // 1A------1B // 2B------2A return(SegmentSegmentCollinear(segment1A, segment1B, segment2B)); } else { // 1A------1B // 2B------2A return(SegmentSegmentCollinear(segment2B, segment2A, segment1A)); } } } // Not parallel float distance1 = perpDot2 / denominator; float distance2 = perpDot1 / denominator; if (distance1 < -Geometry.Epsilon || distance1 > segment1Length + Geometry.Epsilon || distance2 < -Geometry.Epsilon || distance2 > segment2Length + Geometry.Epsilon) { // No intersection bool codirected = direction1.Dot(direction2) > 0; Vector2 from1ATo2B; if (!codirected) { PTUtils.Swap(ref segment2A, ref segment2B); direction2 = -direction2; from1ATo2B = -from2ATo1A; from2ATo1A = segment1A - segment2A; distance2 = segment2Length - distance2; } else { from1ATo2B = segment2B - segment1A; } Vector2 segment1Point; Vector2 segment2Point; float segment2AProjection = -direction1.Dot(from2ATo1A); float segment2BProjection = direction1.Dot(from1ATo2B); bool segment2AIsAfter1A = segment2AProjection > -Geometry.Epsilon; bool segment2BIsBefore1B = segment2BProjection < segment1Length + Geometry.Epsilon; bool segment2AOnSegment1 = segment2AIsAfter1A && segment2AProjection < segment1Length + Geometry.Epsilon; bool segment2BOnSegment1 = segment2BProjection > -Geometry.Epsilon && segment2BIsBefore1B; if (segment2AOnSegment1 && segment2BOnSegment1) { if (distance2 < -Geometry.Epsilon) { segment1Point = segment1A + direction1 * segment2AProjection; segment2Point = segment2A; } else { segment1Point = segment1A + direction1 * segment2BProjection; segment2Point = segment2B; } } else if (!segment2AOnSegment1 && !segment2BOnSegment1) { if (!segment2AIsAfter1A && !segment2BIsBefore1B) { segment1Point = distance1 < -Geometry.Epsilon ? segment1A : segment1B; } else { // Not on segment segment1Point = segment2AIsAfter1A ? segment1B : segment1A; } float segment1PointProjection = direction2.Dot(segment1Point - segment2A); segment1PointProjection = Mathf.Clamp(segment1PointProjection, 0, segment2Length); segment2Point = segment2A + direction2 * segment1PointProjection; } else if (segment2AOnSegment1) { if (distance2 < -Geometry.Epsilon) { segment1Point = segment1A + direction1 * segment2AProjection; segment2Point = segment2A; } else { segment1Point = segment1B; float segment1PointProjection = direction2.Dot(segment1Point - segment2A); segment1PointProjection = Mathf.Clamp(segment1PointProjection, 0, segment2Length); segment2Point = segment2A + direction2 * segment1PointProjection; } } else { if (distance2 > segment2Length + Geometry.Epsilon) { segment1Point = segment1A + direction1 * segment2BProjection; segment2Point = segment2B; } else { segment1Point = segment1A; float segment1PointProjection = direction2.Dot(segment1Point - segment2A); segment1PointProjection = Mathf.Clamp(segment1PointProjection, 0, segment2Length); segment2Point = segment2A + direction2 * segment1PointProjection; } } return(segment1Point.DistanceTo(segment2Point)); } // Point intersection return(0); }
/// <summary> /// Returns the distance between the closest points on the ray and the segment /// </summary> public static float RaySegment(Vector2 rayOrigin, Vector2 rayDirection, Vector2 segmentA, Vector2 segmentB) { 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 float segmentAProjection = -rayDirection.Dot(segmentAToOrigin); Vector2 originToSegmentB = segmentB - rayOrigin; float segmentBProjection = rayDirection.Dot(originToSegmentB); if (Mathf.Abs(perpDotA) > Geometry.Epsilon || Mathf.Abs(perpDotB) > Geometry.Epsilon) { // Not collinear if (segmentAProjection > -Geometry.Epsilon) { float distanceSqr = segmentAToOrigin.LengthSquared() - segmentAProjection * segmentAProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } if (segmentBProjection > -Geometry.Epsilon) { float distanceSqr = originToSegmentB.LengthSquared() - segmentBProjection * segmentBProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } if (segmentAProjection > segmentBProjection) { return(rayOrigin.DistanceTo(segmentA)); } return(rayOrigin.DistanceTo(segmentB)); } // Collinear if (segmentAProjection > -Geometry.Epsilon || segmentBProjection > -Geometry.Epsilon) { // Point or segment intersection return(0); } // No intersection return(segmentAProjection > segmentBProjection ? -segmentAProjection : -segmentBProjection); } // Not parallel float rayDistance = perpDotB / denominator; float segmentDistance = perpDotA / denominator; if (rayDistance < -Geometry.Epsilon || segmentDistance < -Geometry.Epsilon || segmentDistance > 1 + Geometry.Epsilon) { // No intersection bool codirected = rayDirection.Dot(segmentDirection) > 0; Vector2 segmentBToOrigin; if (!codirected) { PTUtils.Swap(ref segmentA, ref segmentB); segmentDirection = -segmentDirection; segmentBToOrigin = segmentAToOrigin; segmentAToOrigin = rayOrigin - segmentA; segmentDistance = 1 - segmentDistance; } else { segmentBToOrigin = rayOrigin - segmentB; } float segmentAProjection = -rayDirection.Dot(segmentAToOrigin); float segmentBProjection = -rayDirection.Dot(segmentBToOrigin); bool segmentAOnRay = segmentAProjection > -Geometry.Epsilon; bool segmentBOnRay = segmentBProjection > -Geometry.Epsilon; if (segmentAOnRay && segmentBOnRay) { if (segmentDistance < 0) { Vector2 rayPoint = rayOrigin + rayDirection * segmentAProjection; Vector2 segmentPoint = segmentA; return(rayPoint.DistanceTo(segmentPoint)); } else { Vector2 rayPoint = rayOrigin + rayDirection * segmentBProjection; Vector2 segmentPoint = segmentB; return(rayPoint.DistanceTo(segmentPoint)); } } else if (!segmentAOnRay && segmentBOnRay) { if (segmentDistance < 0) { Vector2 rayPoint = rayOrigin; Vector2 segmentPoint = segmentA; return(rayPoint.DistanceTo(segmentPoint)); } else if (segmentDistance > 1 + Geometry.Epsilon) { Vector2 rayPoint = rayOrigin + rayDirection * segmentBProjection; Vector2 segmentPoint = segmentB; return(rayPoint.DistanceTo(segmentPoint)); } else { Vector2 rayPoint = rayOrigin; float originProjection = segmentDirection.Dot(segmentAToOrigin); Vector2 segmentPoint = segmentA + segmentDirection * originProjection / segmentDirection.LengthSquared(); return(rayPoint.DistanceTo(segmentPoint)); } } else { // Not on ray Vector2 rayPoint = rayOrigin; float originProjection = segmentDirection.Dot(segmentAToOrigin); float sqrSegmentLength = segmentDirection.LengthSquared(); if (originProjection < 0) { return(rayPoint.DistanceTo(segmentA)); } else if (originProjection > sqrSegmentLength) { return(rayPoint.DistanceTo(segmentB)); } else { Vector2 segmentPoint = segmentA + segmentDirection * originProjection / sqrSegmentLength; return(rayPoint.DistanceTo(segmentPoint)); } } } // Point intersection return(0); }
/// <summary> /// Returns the distance between the closest points on the rays /// </summary> public static float RayRay(Vector2 originA, Vector2 directionA, Vector2 originB, Vector2 directionB) { Vector2 originBToA = originA - originB; float denominator = VectorE.PerpDot(directionA, directionB); float perpDotA = VectorE.PerpDot(directionA, originBToA); float perpDotB = VectorE.PerpDot(directionB, originBToA); bool codirected = directionA.Dot(directionB) > 0; if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel float originBProjection = -directionA.Dot(originBToA); if (Mathf.Abs(perpDotA) > Geometry.Epsilon || Mathf.Abs(perpDotB) > Geometry.Epsilon) { // Not collinear if (!codirected && originBProjection < Geometry.Epsilon) { return(originA.DistanceTo(originB)); } float distanceSqr = originBToA.LengthSquared() - originBProjection * originBProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } // Collinear if (codirected) { // Ray intersection return(0); } else { if (originBProjection < Geometry.Epsilon) { // No intersection return(originA.DistanceTo(originB)); } else { // Segment intersection return(0); } } } // Not parallel float distanceA = perpDotB / denominator; float distanceB = perpDotA / denominator; if (distanceA < -Geometry.Epsilon || distanceB < -Geometry.Epsilon) { // No intersection if (codirected) { float originAProjection = directionB.Dot(originBToA); if (originAProjection > -Geometry.Epsilon) { Vector2 rayPointA = originA; Vector2 rayPointB = originB + directionB * originAProjection; return(rayPointA.DistanceTo(rayPointB)); } float originBProjection = -directionA.Dot(originBToA); if (originBProjection > -Geometry.Epsilon) { Vector2 rayPointA = originA + directionA * originBProjection; Vector2 rayPointB = originB; return(rayPointA.DistanceTo(rayPointB)); } return(originA.DistanceTo(originB)); } else { if (distanceA > -Geometry.Epsilon) { float originBProjection = -directionA.Dot(originBToA); if (originBProjection > -Geometry.Epsilon) { Vector2 rayPointA = originA + directionA * originBProjection; Vector2 rayPointB = originB; return(rayPointA.DistanceTo(rayPointB)); } } else if (distanceB > -Geometry.Epsilon) { float originAProjection = directionB.Dot(originBToA); if (originAProjection > -Geometry.Epsilon) { Vector2 rayPointA = originA; Vector2 rayPointB = originB + directionB * originAProjection; return(rayPointA.DistanceTo(rayPointB)); } } return(originA.DistanceTo(originB)); } } // Point intersection return(0); }