Created
September 6, 2024 15:10
-
-
Save binouze/20af758d16f4e78d2c10ebe6fe48f4d9 to your computer and use it in GitHub Desktop.
Utility script for Unity to get the intersections between a line segment and a SplineContainer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using Unity.Mathematics; | |
using UnityEngine; | |
using UnityEngine.Splines; | |
public static class SplineUtils | |
{ | |
/// <summary> | |
/// Retrieve the list of intersections between a Spline and a ray (line segment with a direction and length). | |
/// </summary> | |
/// <param name="spline">The spline</param> | |
/// <param name="p0">The starting point of the ray</param> | |
/// <param name="direction">The direction of the ray</param> | |
/// <param name="length">The length of the ray</param> | |
/// <param name="step">The increment for traversing the spline positions, default is 0.02, which gives 50 positions on the spline</param> | |
/// <returns></returns> | |
public static List<Vector2> SplineIntersectsRay(this SplineContainer spline, Vector2 p0, Vector2 direction, float length = 20f, float step = 0.02f) | |
{ | |
// calculate point p1 at distance "length" from p0 in the given direction | |
var p1 = p0 + direction.normalized * length; | |
// use the other function that handles this similarly | |
return spline.SplineIntersects(p0, p1, step); | |
} | |
/// <summary> | |
/// Retrieve the list of intersections between a Spline and a segment p0-p1. | |
/// </summary> | |
/// <param name="spline">The spline</param> | |
/// <param name="p0">The starting point of the line</param> | |
/// <param name="p1">The end point of the line</param> | |
/// <param name="step">The increment for traversing the spline positions, default is 0.02, which gives 50 positions on the spline, MUST BE GREATER THAN 0 AND LOWER THAN 1</param> | |
/// <returns></returns> | |
public static List<Vector2> SplineIntersects(this SplineContainer spline, Vector2 p0, Vector2 p1, float step = 0.02f) | |
{ | |
// avoid infinite loops | |
if( step is < 0 or > 1 ) | |
throw new ArgumentException("step must be >= 0", nameof(step)); | |
// prepare the return list | |
var intersections = new List<Vector2>(); | |
// get the first point | |
var s0 = spline.EvaluatePosition(0); | |
// initialize the first position | |
var i = 0f; | |
// we'll check all positions on the spline to try and find intersections | |
while( i < 1 ) | |
{ | |
i += step; | |
// get the point at position i | |
var si = spline.EvaluatePosition(i); | |
// if we find an intersection between the requested segment and the gap between the previous position and this one, | |
// we add it to the list of found intersections | |
if( GetLinesIntersection(p0, p1, s0, si, out var intersection) ) | |
intersections.Add(intersection); | |
// store the current point as the new previous point for the next iteration | |
s0 = si; | |
} | |
// return the list of found intersections | |
return intersections; | |
} | |
/// <summary> | |
/// Try to find an intersection between two segments a0-a1 and b0-b1. | |
/// If an intersection is found, returns true and the intersection point is stored in the out variable "intersection". | |
/// </summary> | |
/// <param name="a0">The starting point of segment a</param> | |
/// <param name="a1">The end point of segment a</param> | |
/// <param name="b0">The starting point of segment b</param> | |
/// <param name="b1">The end point of segment b</param> | |
/// <param name="intersection">The intersection point if found</param> | |
/// <returns>true if an intersection is found</returns> | |
private static bool GetLinesIntersection(Vector2 a0, Vector2 a1, float3 b0, float3 b1, out Vector2 intersection) | |
{ | |
// initialize the out variable | |
intersection = Vector2.zero; | |
// segment directions | |
var dA = a1 - a0; | |
var dB = b1 - b0; | |
// calculate the determinant | |
var det = dA.x * dB.y - dA.y * dB.x; | |
// if the determinant is close to 0, the segments are parallel or collinear | |
if( Mathf.Abs(det) < Mathf.Epsilon ) | |
{ | |
return false; // no intersection | |
} | |
// t and u are parameters that determine where the intersection lies along the segments | |
var t = ((b0.x - a0.x) * dB.y - (b0.y - a0.y) * dB.x) / det; | |
var u = ((b0.x - a0.x) * dA.y - (b0.y - a0.y) * dA.x) / det; | |
// if t and u are between 0 and 1, it means the intersection is within the segment boundaries | |
if( t >= 0 && t <= 1 && u >= 0 && u <= 1 ) | |
{ | |
// calculate the intersection point | |
intersection = a0 + t * dA; | |
return true; | |
} | |
// no intersection within segment boundaries | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment