Builtin Library
README.md
spline.core
Pure spline math used by the parent spline module — point
normalisation, curve evaluation (linear / Catmull-Rom / Bezier),
tangent + length estimation, eased traversal, and closest-point /
sampling helpers. Side-effect free — no engine calls, no entity
spawns — so it stays easy to test and reuse.
Exports
Construction:
Core.new(points: {PointInput}, opts: SplineOpts?) -> SplineCore.linear(points, opts?) -> Spline/Core.catmullRom(points, opts?) -> Spline/Core.bezier(points, opts?) -> Spline
Sampling and queries:
Core.evaluate(path: Spline, t: number) -> Vec3/Core.evaluateEased(path, t, easing) -> Vec3Core.tangent(path: Spline, t: number) -> Vec3Core.length(path: Spline, steps?: number) -> numberCore.sample(path, count, opts?) -> {Sample}/Core.sampleBySpacing(path, spacing, opts?) -> {Sample}Core.closestProgress(path, point, steps?) -> (number, Vec3)Core.measure(pathOrPoints, opts?) -> Measured: the curve by arc length (a list of points is read as a polyline)Core.atDistance(measured, distance, wrap?) -> (Vec3, Vec3): position and direction a distance along it, the constant-speed read
Easing:
Core.resolveEasing(easing: EasingInput) -> EasingFnCore.easingNames() -> { string }
Vector helpers exposed for the placement / drawing siblings:
Core.copyPoint(p),Core.add(a, b),Core.sub(a, b),Core.scale(v, s),Core.normalize(v),Core.lengthVec(v).
Types:
Vec3 = { x: number, y: number, z: number }PointInput = Vec3 | { number }SplineMode = "linear" | "catmullRom" | "bezier"SplineOpts = { mode: SplineMode?, closed: boolean?, tension: number? }Spline = { points: {Vec3}, mode: SplineMode, closed: boolean, tension: number }Sample = { t: number, position: Vec3, tangent: Vec3 }SampleOpts = { easing: ..?, steps: number? }EasingFn = (number) -> numberEasingInput = string | EasingFn | nilMeasured = { positions: {Vec3}, distances: {number}, length: number, closed: boolean }
Usage
local Core = require("@builtin::modules.spline.core")
local path = Core.new({
{ x = 0, y = 0, z = 0 },
{ x = 2, y = 1, z = 0 },
{ x = 4, y = 0, z = 0 },
}, { mode = "catmullRom" })
local pos = Core.evaluate(path, 0.5)
local dir = Core.tangent(path, 0.5)
local samples = Core.sampleBySpacing(path, 0.25)
Notes
- Closed splines wrap the
tparameter viat - floor(t); open splines clamp to[0, 1]. evaluateEasedclampstbefore easing — values outside[0, 1]are squashed.tangentuses a small central-difference; on flat / degenerate sections it returns the{x=0, y=0, z=1}fallback.- The shared point helpers (
add,sub,scale, etc.) are used by the placement and drawing siblings to avoid re-deriving them.