Skip to content

spline

stable

Interpolation and curve functions for animations and paths, including Bezier, Catmull-Rom, Hermite splines, arc-length sampling, and 2D path utilities.

use plugin spline::{lerp, bezier_quadratic, bezier_cubic, …}
13 functions Graphics
/ filter jk navigate Esc clear
Functions (13)
  1. lerp Linear interpolation between two values
  2. bezier_quadratic Evaluate a quadratic Bezier curve
  3. bezier_cubic Evaluate a cubic Bezier curve
  4. bezier_tangent Compute the tangent of a cubic Bezier
  5. bezier_point_2d Evaluate a 2D cubic Bezier returning {x,y}
  6. catmull_rom Evaluate a 1D Catmull-Rom spline
  7. catmull_rom_2d Evaluate a 2D Catmull-Rom spline returning {x,y}
  8. hermite Evaluate a cubic Hermite spline
  9. evaluate_path Evaluate a polyline path at parameter t
  10. tangent Get the normalised tangent of a path at t
  11. closest_point Find the closest point on a path to a 2D point
  12. arc_length_estimate Estimate the arc length of a path
  13. sample_uniform Resample a path at evenly-spaced arc lengths

Overview

spline is a dependency-free math toolkit for interpolation and smooth curves, the kind of work that powers animation easing, camera paths, and motion along a track. It is stateless: there are no handles or objects to keep around — every function takes plain numbers (or a table of {x, y} points) and returns plain numbers or a fresh {x, y} table, so you can call it on the fly inside a render or update loop.

Two layers live here. The low-level scalar evaluators (lerp, bezier_*, catmull_rom, hermite) sample a single curve at a parameter t in [0, 1] and have explicit _2d variants that return a point. The path layer (evaluate_path, tangent, closest_point, arc_length_estimate, sample_uniform) operates over a whole polyline of points, treating t as a normalised position from start (0.0) to end (1.0). Reach for the scalar functions when you control the curve's control points directly, and the path functions when you already have a sequence of points to follow.

Common patterns

Drive a value along a cubic Bezier and orient it with the tangent at the same t:

use plugin spline::{bezier_cubic, bezier_tangent}

let t = 0.35
let value = bezier_cubic(0.0, 0.2, 0.9, 1.0, t)
let slope = bezier_tangent(0.0, 0.2, 0.9, 1.0, t)
print("value={value} slope={slope}")

Walk a 2D path frame by frame, sampling its position and facing direction:

use plugin spline::{evaluate_path, tangent}

let path = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 5.0, "y": 2.0},
  #{"x": 10.0, "y": 0.0}
]
let steps = 4
for i in 0..steps {
  let t = i / steps
  let pos = evaluate_path(path, t)
  let dir = tangent(path, t)
  print("pos=({pos["x"]},{pos["y"]}) facing=({dir["x"]},{dir["y"]})")
}

Measure a path and then drop evenly-spaced markers along it by arc length:

use plugin spline::{arc_length_estimate, sample_uniform}

let path = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 3.0, "y": 4.0},
  #{"x": 6.0, "y": 0.0}
]
print("length: {arc_length_estimate(path, 100)}")
for _, pt in sample_uniform(path, 6) {
  print("marker at x={pt["x"]} y={pt["y"]}")
}

Linear interpolation between two values

Returns the linear interpolation between a and b at parameter t (0.0 = a, 1.0 = b).

use plugin spline::{lerp}

let mid = lerp(0.0, 100.0, 0.5)
print(mid)

let eased = lerp(10.0, 20.0, 0.25)
print(eased)

Stepping t across a loop produces a simple linear animation:

use plugin spline::{lerp}

let steps = 5
for i in 0..steps {
  let t = i / steps
  print("frame {i}: {lerp(0.0, 100.0, t)}")
}

Evaluate a quadratic Bezier curve

Evaluates a quadratic Bezier curve with control points p0, p1, p2 at parameter t.

use plugin spline::{bezier_quadratic}

let y = bezier_quadratic(0.0, 50.0, 100.0, 0.5)
print(y)

Evaluate a cubic Bezier curve

Evaluates a cubic Bezier curve with four control points at parameter t.

use plugin spline::{bezier_cubic}

let v = bezier_cubic(0.0, 0.0, 1.0, 1.0, 0.5)
print(v)

Compute the tangent of a cubic Bezier

Returns the derivative (tangent) of a cubic Bezier at parameter t. Useful for orienting objects moving along a curve.

use plugin spline::{bezier_tangent}

let slope = bezier_tangent(0.0, 0.33, 0.66, 1.0, 0.5)
print("tangent: {slope}")

Evaluate a 2D cubic Bezier returning {x,y}

Evaluates a 2D cubic Bezier curve and returns {x, y} at parameter t.

use plugin spline::{bezier_point_2d}

let pt = bezier_point_2d(0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.5)
print("x={pt["x"]} y={pt["y"]}")

Trace the whole curve by sampling it at several values of t:

use plugin spline::{bezier_point_2d}

let steps = 4
for i in 0..steps {
  let t = i / steps
  let p = bezier_point_2d(0.0, 0.0, 0.0, 10.0, 10.0, 10.0, 10.0, 0.0, t)
  print("({p["x"]}, {p["y"]})")
}

Evaluate a 1D Catmull-Rom spline

Evaluates a Catmull-Rom spline through four control points at t. Passes exactly through p1 and p2, making it useful for smooth paths through known points.

use plugin spline::{catmull_rom}

let v = catmull_rom(0.0, 1.0, 3.0, 4.0, 0.5)
print(v)

Evaluate a 2D Catmull-Rom spline returning {x,y}

Evaluates a 2D Catmull-Rom spline and returns {x, y} at parameter t.

use plugin spline::{catmull_rom_2d}

let pt = catmull_rom_2d(0.0, 0.0, 1.0, 0.0, 2.0, 1.0, 3.0, 1.0, 0.5)
print("x={pt["x"]} y={pt["y"]}")

Evaluate a cubic Hermite spline

Evaluates a cubic Hermite spline between p0 and p1 with tangents m0 and m1 at parameter t. Gives direct control over entry and exit slopes.

use plugin spline::{hermite}

let v = hermite(0.0, 1.0, 1.0, 0.0, 0.5)
print(v)

Evaluate a polyline path at parameter t

Evaluates a polyline path (table of {x, y} points) at normalised parameter t (0.0 = start, 1.0 = end) and returns {x, y}.

use plugin spline::{evaluate_path}

let path = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 1.0, "y": 1.0},
  #{"x": 2.0, "y": 0.0}
]
let pt = evaluate_path(path, 0.5)
print("x={pt["x"]} y={pt["y"]}")

Get the normalised tangent of a path at t

Returns the normalised tangent direction {x, y} of the polyline path at parameter t. Useful for orienting sprites or objects along a path.

use plugin spline::{tangent}

let path = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 1.0, "y": 1.0}
]
let dir = tangent(path, 0.5)
print("dx={dir["x"]} dy={dir["y"]}")

Find the closest point on a path to a 2D point

Finds the closest point on the path to the 2D query point (px, py). Returns {t, distance} where t is the path parameter of the closest point.

use plugin spline::{closest_point}

let path = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 10.0, "y": 0.0}
]
let result = closest_point(path, 5.0, 3.0)
print("t={result["t"]} dist={result["distance"]}")

Feed the returned t back into evaluate_path to get the actual snapped point:

use plugin spline::{closest_point, evaluate_path}

let path = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 10.0, "y": 0.0},
  #{"x": 10.0, "y": 10.0}
]
let hit = closest_point(path, 8.0, 4.0)
let snapped = evaluate_path(path, hit["t"])
print("snapped to x={snapped["x"]} y={snapped["y"]}")

Estimate the arc length of a path

Estimates the arc length of the polyline path by sampling it at segments intervals. Higher segments gives a more accurate result.

use plugin spline::{arc_length_estimate}

let path = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 3.0, "y": 4.0}
]
let length = arc_length_estimate(path, 100)
print("length: {length}")

Resample a path at evenly-spaced arc lengths

Resamples the path into count evenly-spaced points by arc length. Returns a table of {x, y} tables, useful for placing objects uniformly along a curve.

use plugin spline::{sample_uniform}

let path = [
  #{"x": 0.0, "y": 0.0},
  #{"x": 5.0, "y": 0.0},
  #{"x": 10.0, "y": 5.0}
]
let samples = sample_uniform(path, 5)
for _, pt in samples {
  print("x={pt["x"]} y={pt["y"]}")
}
enespt-br