Spline
A 3D spline / curve primitive (engine PLM-243 / ADR 0080) — the keystone of Block
SPL (splines / geometry / scatter). Add control points, pick an interpolation type, then
evaluate the curve either by parameter t ∈ [0, 1] or, for even spacing, by
arc-length distance s ∈ [0, length]. frameAtDistance / sampleFrames return
rotation-minimizing orientation frames — a moving {position, tangent, normal, binormal} basis that never twist-flips along the curve — which is the input a mesh
extruder needs to sweep a profile into a ribbon, tube, road, or rail.
import "engine" for Spline
var path = Spline.new()path.addPoint(-4, 0, -2)path.addPoint(-2, 2.5, 2)path.addPoint(0.5, -1.5, 3)path.addPoint(2.5, 2, -1)path.addPoint(4, -0.5, -3)
System.print("length = %(path.length), points = %(path.pointCount)")
// Sample 16 evenly-spaced orientation frames along the curve (mesh-extrusion input).var frames = path.sampleFrames(16)Data formats
Section titled “Data formats”Two shapes flow across this API — read them once and the rest of the page is straightforward:
-
A vector is an
[x, y, z]numberList— the same convention asCamera.getViewMatrix.position,tangent, andpositionAtDistanceeach return one. -
A frame is a flat 13-number
List— position, tangent, normal, binormal, and width, concatenated:Index 0 1 23 4 56 7 89 10 1112Field position px py pztangent tx ty tznormal nx ny nzbinormal bx by bzwidthframeAtDistancereturns one such 13-number list;sampleFramesreturns aListof them. The tangent/normal/binormal are unit vectors forming a right-handed basis;widthis the interpolated per-point width (seeaddPointFull, default1.0), for tapering an extruded ribbon.
Constructor
Section titled “Constructor”Spline.new()
Section titled “Spline.new()”Returns: Spline — A new, empty spline (no control points). Add points, then evaluate.
Building the curve
Section titled “Building the curve”| Method | Parameters | Description |
|---|---|---|
addPoint(x, y, z) | x, y, z (Num) | Append a control point. Width defaults to 1.0. |
addPointFull(x, y, z, width) | x, y, z, width (Num) | Append a control point with a per-point width (scale), interpolated along the curve and delivered in each frame’s slot 12 — for tapering ribbons/tubes. |
setType(type) | type (String) | Interpolation: "catmullrom" (default, an interpolating spline that passes through every control point) or "bezier" (control points read as anchor, control, control, anchor, …). |
setClosed(closed) | closed (Bool) | Open curve (default false) or a closed loop (the last point joins back to the first). |
setRoll(radians) | radians (Num) | A constant roll about the tangent, applied to every orientation frame (rotate the extruded profile around the path). |
var loop = Spline.new()loop.setType("bezier")loop.setClosed(true)loop.setRoll(0.25) // radiansloop.addPointFull(0, 0, 0, 1.0) // wide at the start …loop.addPointFull(3, 1, 0, 0.4) // … tapering to 0.4Reading the curve
Section titled “Reading the curve”| Property | Returns | Description |
|---|---|---|
pointCount | Num | The number of control points added so far. |
length | Num | Total arc length of the curve (world units), the upper bound for the s distance parameters below. |
Evaluating by parameter t
Section titled “Evaluating by parameter t”t runs 0 → 1 across the whole curve. Both return an [x, y, z] List.
position(t)
Section titled “position(t)”Returns: List — [x, y, z], the point on the curve at parameter t ∈ [0, 1].
tangent(t)
Section titled “tangent(t)”Returns: List — [x, y, z], the unit tangent (direction of travel) at t.
tis NOT constant-speed. Equal steps intare not equal steps in distance —tmoves faster where control points are far apart and slower where they bunch up. For even spacing along the curve (sampling a line strip, placing props, extruding a mesh), use the arc-length methods below instead.
Evaluating by arc-length distance s
Section titled “Evaluating by arc-length distance s”s runs 0 → length and is constant-speed: equal steps in s are equal steps along
the curve.
positionAtDistance(s)
Section titled “positionAtDistance(s)”Returns: List — [x, y, z], the point at arc-length distance s ∈ [0, length].
tForDistance(s)
Section titled “tForDistance(s)”Returns: Num — the parameter t corresponding to arc-length distance s (feed it
back into position / tangent if you need the raw-t form).
// A line strip sampled at CONSTANT arc-length spacing (never bunches on tight turns).var L = path.lengthvar n = 80var verts = []for (i in 0...n) { var p = path.positionAtDistance(L * i / (n - 1)) verts.add(p) // p = [x, y, z]}Orientation frames (mesh extrusion)
Section titled “Orientation frames (mesh extrusion)”Frames are rotation-minimizing: the normal is transported along the curve without any extraneous twist, so a swept profile never suddenly flips over — the failure mode of a naive Frenet frame at an inflection point.
frameAtDistance(s)
Section titled “frameAtDistance(s)”Returns: List — a flat 13-number frame at arc-length distance s ∈ [0, length]
([px,py,pz, tx,ty,tz, nx,ny,nz, bx,by,bz, width] — see Data formats).
sampleFrames(count)
Section titled “sampleFrames(count)”Returns: List — a List of count frame lists (each 13 numbers), evenly spaced by
arc length from the start of the curve to the end. This is the direct input to a mesh
extruder: at each frame, place your cross-section profile at position, oriented by
normal/binormal, scaled by width.
var frames = path.sampleFrames(16)for (f in frames) { var pos = [f[0], f[1], f[2]] // position var tangent = [f[3], f[4], f[5]] // unit tangent var normal = [f[6], f[7], f[8]] // unit normal var binormal = [f[9], f[10], f[11]] // unit binormal var width = f[12] // per-point width // … emit ring vertices around `pos` in the normal/binormal plane, scaled by width …}See the Spline example (apps/spline_demo) — a 5-point Catmull-Rom
curve drawn as a line strip, with sampleFrames rendered as gizmos (tangent red, normal
green, binormal blue).