Math & geometry
Vectors, matrices, quaternions, Catmull-Rom splines, simplex and curl noise, and robust geometric predicates in the engine's core math library.
The engine’s core math library lives under @woosh/meep-engine/src/core/geom/ and @woosh/meep-engine/src/core/math/. Every function in it follows the same design contract: results are written into a caller-supplied output parameter, never returned as new allocations. This keeps hot paths — physics, animation, rendering — allocation-free at steady state.
Vectors
Vectors are plain JavaScript objects or arrays. The library does not impose a class hierarchy; most functions accept any object with the right shape.
Vec2
Utilities under core/geom/vec2/:
| Function | Description |
|---|---|
v2_dot(ax, ay, bx, by) | Dot product — returns a scalar |
v2_length(x, y) | Euclidean length |
v2_length_sqr(x, y) | Squared length (avoids a square root) |
v2_distance(ax, ay, bx, by) | Distance between two points |
v2_distance_sqr(ax, ay, bx, by) | Squared distance |
v2_cross_product(ax, ay, bx, by) | 2D cross product (scalar) |
v2_angle_between(ax, ay, bx, by) | Angle between two vectors in radians |
v2_matrix3_cm_multiply(out, x, y, m) | Transform by a column-major 3×3 matrix |
Vec3
Utilities under core/geom/vec3/:
| Function | Description |
|---|---|
v3_dot(ax, ay, az, bx, by, bz) | Dot product |
v3_length(x, y, z) | Euclidean length |
v3_length_sqr(x, y, z) | Squared length |
v3_distance(ax, ay, az, bx, by, bz) | Distance |
v3_distance_sqr(...) | Squared distance |
v3_lerp(result, ax, ay, az, bx, by, bz, t) | Linear interpolation; writes to result.set(x, y, z) |
v3_slerp(result, ...) | Spherical linear interpolation |
v3_angle_between(ax, ay, az, bx, by, bz) | Angle in radians |
v3_displace_in_direction(result, ox, oy, oz, dx, dy, dz, dist) | Move a point along a direction |
v3_quat3_apply(out, offset, vx, vy, vz, qx, qy, qz, qw) | Rotate a vector by a unit quaternion |
v3_quat3_apply_inverse(out, offset, ...) | Rotate by the conjugate (inverse rotation) |
v3_matrix4_multiply(out, offset, x, y, z, m) | Full 4×4 transform including translation |
v3_matrix4_rotate(out, offset, x, y, z, m) | Rotational part only (no translation) |
The out-parameter convention is uniform: the output buffer and an integer offset come first, followed by the inputs. For example:
import { v3_quat3_apply } from "@woosh/meep-engine/src/core/geom/vec3/v3_quat3_apply.js";
const out = new Float32Array(3);
// Rotate (0, 1, 0) by a unit quaternion stored as (qx, qy, qz, qw):
v3_quat3_apply(out, 0, 0, 1, 0, qx, qy, qz, qw);
No intermediate object is created. out may be a plain array, a Float32Array, or any array-like.
Vec4
Minimal utilities under core/geom/vec4/:
| Function | Description |
|---|---|
v4_dot(ax, ay, az, aw, bx, by, bz, bw) | Dot product |
v4_length(x, y, z, w) | Euclidean length |
v4_length_sqr(x, y, z, w) | Squared length |
v4_multiply_mat4(out, x, y, z, w, m) | Multiply by a 4×4 matrix |
Matrices
Mat3
Column-major 3×3 utilities under core/geom/mat3/:
| Function | Description |
|---|---|
m3_multiply(out, a, b) | Matrix product out = a × b |
m3_multiply_vec3(out, m, x, y, z) | Transform a vector |
m3_determinant(m) | Scalar determinant |
m3_cm_invert(out, m) | Invert a column-major matrix |
m3_cm_compose_transform(out, tx, ty, rotation, sx, sy) | Build a 2D TRS matrix |
m3_cm_extract_rotation(out, m) | Extract the rotation sub-matrix |
m3_make_rotation(out, angle) | Rotation about the Z axis |
Mat4
Column-major 4×4 utilities under core/geom/3d/mat4/:
| Function | Description |
|---|---|
m4_multiply(out, a, b) | Matrix product; returns out |
m4_invert(out, m) | General inverse |
m4_inverse_rotation_translation(out, m) | Fast inverse for rigid-body matrices |
compose_matrix4_array(out, position, rotation, scale) | Build a TRS matrix from position, unit quaternion, and scale objects |
decompose_matrix_4_array(m, position, rotation, scale) | Decompose a TRS matrix |
m4_make_translation(out, x, y, z) | Translation matrix |
m4_make_scale(out, x, y, z) | Scale matrix |
m4_extract_scale(out, m) | Extract scale factors |
m4_orthographic_off_center_z0(out, l, r, b, t, near, far) | Orthographic projection, depth mapped to [0, 1] |
m4_rotation_translation(out, quat, pos) | Rotation + translation (no scale) |
apply_mat4_transform_to_v3_array(positions, count, m) | Batch-transform a flat Float32Array of positions in place |
apply_mat4_transform_to_direction_v3_array(dirs, count, m) | Batch-transform directions (ignores translation) |
Quaternions
Quaternions use (x, y, z, w) component order — w last — throughout. Utilities live under core/geom/3d/quaternion/.
| Function | Description |
|---|---|
quat3_createFromAxisAngle(axis, angle, result) | Build a quaternion from an axis vector and an angle in radians |
quat3_multiply(out, offset, ax, ay, az, aw, bx, by, bz, bw) | Hamilton product out = a ⊗ b; applies b first then a |
quat3_to_matrix3(out, qx, qy, qz, qw) | Build a 3×3 rotation matrix |
The quat3_multiply convention: to rotate a vector by q1 and then q2, compose q2 ⊗ q1.
import { quat3_multiply } from "@woosh/meep-engine/src/core/geom/3d/quaternion/quat3_multiply.js";
const out = [0, 0, 0, 0];
// Compose: apply q1 first, then q2.
quat3_multiply(out, 0, q2x, q2y, q2z, q2w, q1x, q1y, q1z, q1w);
Morton-encoded quaternions for compact storage: quat_encode_to_uint32 / quat_decode_from_uint32 pack a unit quaternion into a single uint32.
Catmull-Rom splines
computeCatmullRomSpline samples a centripetal (or uniform) Catmull-Rom spline through an arbitrary set of N-dimensional control points.
import { computeCatmullRomSpline } from
"@woosh/meep-engine/src/core/math/spline/computeCatmullRomSpline.js";
const controlPoints = [0,0, 1,2, 3,1, 4,3]; // 4 points in 2D
const output = new Array(20 * 2); // 20 samples × 2 dimensions
computeCatmullRomSpline(
output,
controlPoints, 4, // input, point count
2, // dimensions per point
20, // number of output samples
0.5 // alpha (0 = uniform, 0.5 = centripetal, 1 = chordal)
);
The alpha parameter controls parameterisation: 0.5 (default) is centripetal and avoids cusps or self-intersections. A non-uniform variant computeNonuniformCatmullRomSplineSample lets you sample a single segment with explicit control points.
Related spline types with bounds and intersection tests are under the same directory: spline3_hermite, spline3_bezier, and their _bounds / _derivative / _integral companions.
Simplex and curl noise
Simplex noise
create_simplex_noise_2d returns a seeded 2D noise function with output in [-1, 1]:
import { create_simplex_noise_2d } from
"@woosh/meep-engine/src/core/math/noise/create_simplex_noise_2d.js";
const noise = create_simplex_noise_2d(Math.random);
const v = noise(x, y); // -1..1
The underlying 3D gradient-noise primitive sdnoise3 (from sdnoise.js) returns both the noise value and its analytical spatial derivative — useful for domain-warping and normal-map generation without finite differences.
noise_octaves layers multiple octaves of any noise function:
import { noise_octaves } from
"@woosh/meep-engine/src/core/math/noise/noise_octaves.js";
const value = noise_octaves(
noise, null, // noise function + context
x, y, z,
6, // octave count
0.5, // persistence (amplitude falloff)
2.0 // lacunarity (frequency multiplier)
);
Curl noise
curl_noise_3d computes a divergence-free 3D vector field from three offset simplex noise samples. The result is written into a caller-supplied array:
import { curl_noise_3d } from
"@woosh/meep-engine/src/core/math/noise/curl_noise_3d.js";
const result = [0, 0, 0];
curl_noise_3d(result, x, y, z);
// result[0], result[1], result[2] — divergence-free velocity
curl_noise_3dt adds a w (time) parameter for animated curl fields, displacing each noise sample along the time axis independently.
Robust geometric predicates
Two predicates delegate to the robust-predicates package for exact arithmetic, avoiding floating-point sign errors at degenerate configurations.
orient3d_robust(points, a, b, c, d) — tests whether point d is above, on, or below the plane defined by the counterclockwise triangle (a, b, c). points is a flat array of coordinates in stride-3 format ([x0, y0, z0, x1, y1, z1, ...]). Returns positive if d is above, negative if below, zero if coplanar.
in_sphere3d_robust(points, a, b, c, d, e) — tests whether point e is inside, on, or outside the circumsphere of the positively oriented tetrahedron (a, b, c, d). Returns positive if inside.
A fast (non-robust) variant orient3d_fast is available in the same directory for contexts where degenerate cases cannot occur.
import { orient3d_robust } from
"@woosh/meep-engine/src/core/geom/3d/plane/orient3d_robust.js";
import { in_sphere3d_robust } from
"@woosh/meep-engine/src/core/geom/3d/sphere/in_sphere3d_robust.js";
const pts = [/* flat x,y,z per point */];
const side = orient3d_robust(pts, 0, 1, 2, 3); // sign tells which side
const inside = in_sphere3d_robust(pts, 0, 1, 2, 3, 4); // sign tells in/out
These are used internally by the Delaunay/tetrahedral mesh code and the physics narrowphase.
Where to go next
- Spatial acceleration — the BVH and ray/frustum queries that consume these primitives.
- Color management — the color-space conversions built alongside this library.
- Spatial queries — physics raycasts and shape casts built on top of the BVH.