GlobalBSpline is intentionally specialized around the open cubic interpolation
case because that case admits a compact, deterministic, linear-time solver.
For n + 1 fit points:
BSplineCurve stateNo iterative optimization is required.
The output has n + 3 control points and n + 7 knots:
fit points Q0 ... Qn
control points P0 ... P(n+2)
knots 0 0 0 0 u1 ... u(n-1) 1 1 1 1
The interior knots are simple, so a non-degenerate cubic curve is C2 across
those knots.
Clamping makes endpoint derivatives local:
C'(0) = 3 / U4 * (P1 - P0)
C'(1) = 3 / (1 - U(n+2)) * (P(n+2) - P(n+1))
Therefore P1 and P(n+1) do not belong to the global linear solve.
At an interior fit parameter u[k], which is also a simple cubic knot, the
fit-point equation has three non-zero basis coefficients:
Q[k] = a[k] P[k] + b[k] P[k+1] + c[k] P[k+2]
After subtracting the already-known endpoint-adjacent terms, the unknowns are
P2 ... Pn. Every row touches at most the previous, current, and next unknown,
so the matrix is tridiagonal.
The implementation performs forward elimination and backward substitution in
place on three scalar diagonals and a Vec2 right-hand side.
| Operation | Time | Additional memory |
|---|---|---|
| Distance/parameter generation | O(n) | O(n) |
| Knot construction | O(n) | O(n) |
| Tridiagonal assembly | O(n) | O(n) |
| Tridiagonal solve | O(n) | O(n) |
| One de Boor evaluation | O(log n + p^2) | O(p) |
| Cubic Bezier conversion | O(n) | O(n) output |
For the interpolator p = 3, so per-evaluation polynomial work is constant.
The global solve uses flat arrays only. Basis-function scratch storage is a
four-element stack span. No dense double[,] matrix is allocated.
BSplineCurve.Evaluate uses stack storage for normal spline degrees. Derivative
evaluation derives the control polygon into stack storage for small curves and
uses ArrayPool<Vec2> for large control nets to avoid persistent large temporary
allocations.
Geometry validation and tangent normalization use a scaled hypotenuse calculation
rather than directly evaluating sqrt(x*x + y*y). This avoids unnecessary
overflow or underflow when coordinate components have very different magnitudes.
Exact global interpolation is poorly conditioned when distinct fit constraints collapse to almost the same parameter. The solver rejects consecutive chords whose length is below:
RelativePointTolerance * totalChordLength
The default is 1e-12.
This is scale-relative: a model expressed in micrometres and the same model expressed in metres receive equivalent geometric treatment.
The cubic interpolation matrix is expected to be well behaved for valid strictly
increasing parameters, but production code should not silently divide by a tiny
or non-finite pivot. The solver checks each eliminated diagonal against
SolverPivotTolerance and reports an explicit failure if the system becomes
numerically unsafe.
Chord-length and centripetal parameterization usually produce better geometric behavior than uniform parameters for irregular point spacing. Centripetal parameterization is particularly useful when one or two chords are much longer than their neighbors.
Parameterization affects shape and matrix coefficients. It does not change the interpolation invariant:
C(u[k]) = Q[k]
When only tangent direction is known, Direction mode normalizes each input
vector and uses total chord length as the default derivative magnitude. This is a
shape heuristic, not an intrinsic property of B-splines.
For CAD data that already provides a derivative vector with meaningful magnitude,
use BSplineTangentMode.Derivative.
A cubic B-spline is piecewise cubic polynomial. ToBezierSegments() samples each
knot span at four exact parameter values and inverts the cubic Bernstein basis.
No flattening tolerance or iterative fitting is involved, and the conversion also
works when a directly constructed cubic curve contains repeated interior knots.
The test suite covers:
The large-input test validates the linear control-net and knot-size invariants without relying on fragile wall-clock timing assertions.