Skip to content

Instantly share code, notes, and snippets.

@binouze
Created September 6, 2024 15:10
Show Gist options
  • Save binouze/20af758d16f4e78d2c10ebe6fe48f4d9 to your computer and use it in GitHub Desktop.
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.
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