in MREUnityRuntimeLib/ProceduralToolkit/Geometry/Closest2D.cs [585:746]
public static void RaySegment(Vector2 rayOrigin, Vector2 rayDirection, Vector2 segmentA, Vector2 segmentB,
out Vector2 rayPoint, out Vector2 segmentPoint)
{
Vector2 segmentDirection = segmentB - segmentA;
Vector2 segmentAToOrigin = rayOrigin - 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 = -Vector2.Dot(rayDirection, segmentAToOrigin);
Vector2 rayOriginToSegmentB = segmentB - rayOrigin;
float segmentBProjection = Vector2.Dot(rayDirection, rayOriginToSegmentB);
if (Mathf.Abs(perpDotA) > Geometry.Epsilon || Mathf.Abs(perpDotB) > Geometry.Epsilon)
{
// Not collinear
if (segmentAProjection > -Geometry.Epsilon && segmentBProjection > -Geometry.Epsilon)
{
if (segmentAProjection < segmentBProjection)
{
rayPoint = rayOrigin + rayDirection*segmentAProjection;
segmentPoint = segmentA;
return;
}
else
{
rayPoint = rayOrigin + rayDirection*segmentBProjection;
segmentPoint = segmentB;
return;
}
}
if (segmentAProjection > -Geometry.Epsilon || segmentBProjection > -Geometry.Epsilon)
{
rayPoint = rayOrigin;
float sqrSegmentLength = segmentDirection.sqrMagnitude;
if (sqrSegmentLength > Geometry.Epsilon)
{
float rayOriginProjection = Vector2.Dot(segmentDirection, segmentAToOrigin)/sqrSegmentLength;
segmentPoint = segmentA + segmentDirection*rayOriginProjection;
}
else
{
segmentPoint = segmentA;
}
return;
}
rayPoint = rayOrigin;
segmentPoint = segmentAProjection > segmentBProjection ? segmentA : segmentB;
return;
}
// Collinear
if (segmentAProjection > -Geometry.Epsilon && segmentBProjection > -Geometry.Epsilon)
{
// Segment intersection
rayPoint = segmentPoint = segmentAProjection < segmentBProjection ? segmentA : segmentB;
return;
}
if (segmentAProjection > -Geometry.Epsilon || segmentBProjection > -Geometry.Epsilon)
{
// Point or segment intersection
rayPoint = segmentPoint = rayOrigin;
return;
}
// No intersection
rayPoint = rayOrigin;
segmentPoint = segmentAProjection > segmentBProjection ? segmentA : segmentB;
return;
}
// 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 = Vector2.Dot(rayDirection, 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 = -Vector2.Dot(rayDirection, segmentAToOrigin);
float segmentBProjection = -Vector2.Dot(rayDirection, segmentBToOrigin);
bool segmentAOnRay = segmentAProjection > -Geometry.Epsilon;
bool segmentBOnRay = segmentBProjection > -Geometry.Epsilon;
if (segmentAOnRay && segmentBOnRay)
{
if (segmentDistance < 0)
{
rayPoint = rayOrigin + rayDirection*segmentAProjection;
segmentPoint = segmentA;
return;
}
else
{
rayPoint = rayOrigin + rayDirection*segmentBProjection;
segmentPoint = segmentB;
return;
}
}
else if (!segmentAOnRay && segmentBOnRay)
{
if (segmentDistance < 0)
{
rayPoint = rayOrigin;
segmentPoint = segmentA;
return;
}
else if (segmentDistance > 1 + Geometry.Epsilon)
{
rayPoint = rayOrigin + rayDirection*segmentBProjection;
segmentPoint = segmentB;
return;
}
else
{
rayPoint = rayOrigin;
float originProjection = Vector2.Dot(segmentDirection, segmentAToOrigin);
segmentPoint = segmentA + segmentDirection*originProjection/segmentDirection.sqrMagnitude;
return;
}
}
else
{
// Not on ray
rayPoint = rayOrigin;
float originProjection = Vector2.Dot(segmentDirection, segmentAToOrigin);
float sqrSegmentLength = segmentDirection.sqrMagnitude;
if (originProjection < 0)
{
segmentPoint = segmentA;
return;
}
else if (originProjection > sqrSegmentLength)
{
segmentPoint = segmentB;
return;
}
else
{
segmentPoint = segmentA + segmentDirection*originProjection/sqrSegmentLength;
return;
}
}
}
// Point intersection
rayPoint = segmentPoint = segmentA + segmentDirection*segmentDistance;
}