API Reference · Math utilities

Spline

Namespace: Ava3D

public static class Spline

A smooth curve through a row of points, and the direction it is going when it gets there.

The problem is always the same shape: there are waypoints, and something has to move between them without stopping at each one. A camera move, a flight path, a spine to extrude a tube along, a track of scalars driving an angle. Linear interpolation gives a corner at every waypoint; a Bézier wants control handles nobody has; a B-spline does not pass through its own points. Catmull–Rom passes through every point it is given, needs nothing but the points, and is C¹ — which is the combination that made it the default for exactly this job in every engine that has one.

Uniform in the parameter, not in arc length. Each segment gets an equal share of t however long it is, so waypoint i is always reached at i / (n − 1) and placing something somewhere at a particular moment is a matter of counting waypoints. The cost is that a long segment is travelled faster than a short one. Arc-length parameterisation is the other trade — even speed, timing you have to solve for — and it needs a sampled length table, an allocation and a lifetime to hang it on. Nothing here has one, so it is not offered: spacing the waypoints evenly is the answer that costs nothing, and it is the answer nearly every caller wants anyway.

Everything is a static function of an array. No object to build, no state to invalidate when the array changes, and the same array can be sampled by anything that can see it.

Methods

MemberDescription
static Vector3 Direction(Vector3[] points, float t, bool closed = false)

Which way the curve is heading at t, as a unit vector.

This is what orients something that follows a path: Rotations.LookAlong(Spline.Direction(p, t)) points a nose down the curve, and there is no second quantity kept beside the position that could drift out of step with it.

Where the curve is momentarily stationary there is no direction to give, and this returns Vector3.Zero rather than a NaN or a silent fallback to some fixed axis. Zero is visible in a debugger and survives being fed to Rotations.LookAlong, which answers Quaternion.Identity for it; a NaN spreads to every transform downstream and a pretend direction is a model that snaps round to face north for one frame.

static Vector3 Sample(Vector3[] points, float t, bool closed = false)

The point at t along points, where t runs 0..1 end to end.

points

The waypoints. The curve passes through every one of them.

t

How far along, 0..1. Values outside that extrapolate rather than clamp.

closed

Whether the path is a loop, so the last point leads back to the first. Open — the default — is what a path usually is: it starts somewhere and ends somewhere else.

static float Sample(float[] values, float t, bool closed = false)

The same curve through a row of plain numbers: an animation track, a roll angle, an opacity.

static Vector3 Tangent(Vector3[] points, float t, bool closed = false)

How fast the curve is moving at t, in units per whole t — so a path travelled over ten seconds gives a velocity in units per second when divided by ten.

This is the analytic derivative, not a secant between two nearby samples. The difference is not accuracy so much as honesty: a numerical derivative needs a step size, and the step size is a number that has to be small enough to be a derivative and large enough not to be rounding noise, which is a tuning parameter nobody should have to own.

The length is zero where the curve is momentarily stationary, so normalise with care — Spline.Direction is that, done safely.

static float Tangent(float[] values, float t, bool closed = false)

The rate of change of a scalar track at t, per whole t.

There is no Direction for a track, because a number has a sign rather than a direction and MathF.Sign already ships it.

See also