Hi!
I’m looking to do some pretty specific stuff with splines, and part of that is getting the closest point to a spline very rapidly. I have a few other options than this, but the main function that I’m looking at using is CurveUtility.GetNearestPoint().

Unfortunately, I can’t find any documentation on this function, and instead of using a float3 or Vector3 for the point, it uses a Ray. Maybe I’m feeble of imagination, but I can’t understand why a Ray is used, and which way - is the direction of the ray used, or just the location?

In any case, I would love to see any documentation of this function, or an explanation of its behavior, and that failing, I would appreciate any guesses as to why it uses a Ray and how that information is processed.

I’m pretty sure that’s the function used to decide which spline part is closest to your mouse, to implement clicking on the spline. It uses a ray instead of a point in order to (I assume) support clicking on a spline that’s not drawn orthogonally to the screen. So the direction is very much used.

As @Baste was saying the methods using a ray are mainly to facilitate ray tracing and spline selection using a mouse.
You were looking at CurveUtility, a curve is only one segment of the spline (between 2 consecutive knots) and not the entire spline itself. That’s why SplineUtility would be a better place to look for you

Moreover, you can have a look at the sample scene called “Extrude Spline and Nearest Point” in the package examples.
This sample is using that utility method and shows how to do it via the provided scripts.

Hi @Baste & @ThomasLopez , sorry that I missed your responses, which I appreciate.

I was specifically looking at the curve utility one because I knew which Bezier curves I needed to compare to and there were curves that I specifically did not want to compare to - close in space but far in spline traversal. This is in addition to the performance concerns - I was working with a procedurally-generated spline that could have arbitrarily high numbers of knots, and making MANY calls.

It makes sense that the method would be for clicking on it or raytracing, but again the lack of documentation meant that I wasn’t able to use it.

I’ve since abandoned splines entirely and designed a solution using circle arcs and straight lines - not the same thing, but better for my purposes anyway, and very fast to find the closest point on.

I have a similar problem. I’m trying to measure an object’s distance from a spline over time, and the SplineUtility.GetNearestPoint isn’t great for me because anywhere the spline loops near itself, the “nearest point” may jump somewhere different than I expect. Since I’m tracking the last position on the spline, let’s say it’s typically within +/- 0.01 of its last NormalizedPosition, I thought I should just take the current curve segment, and possibly the next/previous one, and check those instead. However, since the CurveUtility methods only accept a ray and not a float3, I’m stuck on how to use that.

Any suggestions? At first I was thinking I could create a copy of the spline and remove all knots except in the local region I’m checking, but now I think it makes more sense to create my own copy of the SplineUtility.GetNearestPoint function which allows a specific range (like how Unity’s implementation uses a Segment struct internally). If there’s a way to do this with the library as-is, please let me know, otherwise consider adding either a specified range on the SplineUtility.GetNearestPoint function (eg. parameters searchMin=0, searchMax=1, which get passed into the initial Segment), or a version of CurveUtility.GetNearestPoint accepting a float3 instead of Ray. I’m sure it’d help more than just me!

Yeah, that’s exactly the problem. I ended up giving up on splines entirely, in part because of this. I think your first idea is a good one, as distance to a spline is not easily calculated. I’d suggest instead of copying and removing, to construct a new spline explicitly, and clone the knots. You could have a query spline that stays instantiated and just updates the positions of the knots as needed. This might be what you meant already.

I fully agree with both of your suggestions for the SplineUtility and CurveUtility, I think both would be very beneficial features.

Well thankfully the source code is all there in the package, so I just made my own copy of the SplineUtility.GetNearestPoint which accepts a range. In case it helps anyone else, here’s what I’m using:

// My copy of SplineUtility.GetNearestPoint modified to allow searching only a small range of the spline (between searchMin and searchMax)
public static float GetNearestPoint<T>(T spline,
float3 point,
out float3 nearest,
out float t,
float searchMin = 0,
float searchMax = 1,
int resolution = SplineUtility.PickResolutionDefault,
int iterations = 2) where T : ISpline
{
float distance = float.PositiveInfinity;
nearest = float.PositiveInfinity;
searchMin = math.max(0, math.min(searchMin, 1));
searchMax = math.max(searchMin, math.min(searchMax, 1));
float2 segment = new float2(searchMin, math.max(0, searchMax - searchMin)); //x = segment start, y = segment length
t = 0f;
int res = math.min(math.max(SplineUtility.PickResolutionMin, resolution), SplineUtility.PickResolutionMax);
for (int i = 0, c = math.min(10, iterations); i < c; i++)
{
int segments = SplineUtility.GetSubdivisionCount(spline.GetLength() * segment.y, res);
segment = GetNearestPointInternal(spline, point, segment, out distance, out nearest, out t, segments);
}
return distance;
}
static float2 GetNearestPointInternal<T>(T spline,
float3 point,
float2 range,
out float distance, out float3 nearest, out float time,
int segments) where T : ISpline
{
distance = float.PositiveInfinity;
nearest = float.PositiveInfinity;
time = float.PositiveInfinity;
float2 segment = new float2(-1f, 0f);
float t0 = range.x;
float3 a = SplineUtility.EvaluatePosition(spline, t0);
for (int i = 1; i < segments; i++)
{
float t1 = range.x + (range.y * (i / (segments - 1f)));
float3 b = SplineUtility.EvaluatePosition(spline, t1);
var p = SplineMath.PointLineNearestPoint(point, a, b, out var lineParam);
float dsqr = math.distancesq(p, point);
if (dsqr < distance)
{
segment.x = t0;
segment.y = t1 - t0;
time = segment.x + segment.y * lineParam;
distance = dsqr;
nearest = p;
}
t0 = t1;
a = b;
}
distance = math.sqrt(distance);
return segment;
}

And pretty much the only change from what’s in SplineUtility is the added searchMin/searchMax parameters, and having to use a float2 instead of the internal struct Segment. It’s working well in my tests so far.