The pack
vectorHull
function vectorHull(): Pack<{ vecHull: VecHullApi }>
The vector-hull pack. Pure maths, so it needs no backend and runs headless.
Functions
convexHull
function convexHull(points: readonly VecPt[]): VecPt[]
The convex hull of a point set (monotone chain), counter-clockwise in standard axes — which is CLOCKWISE on screen, y being down.
Fewer than three distinct points comes back as-is; collinear points are dropped.
convexPieces
function convexPieces(points: readonly VecPt[], max = MAX_HULL_POINTS, tolerance = 0): VecPt[][]
Split an outline into CONVEX pieces (ear clipping, then merging ears back
together while the join stays convex and within max vertices).
The honest way to give a concave silhouette — a ship, a plus, a letter — real collision instead of the hull's approximation: each piece becomes its own Box2D polygon on ONE body, so the notch is a notch.
Built for outlines a hand drew, not only clean authored ones. Repeated points, runs of collinear points and zero-width spikes are cleaned out first; an outline that touches itself at a point still decomposes; one that CROSSES itself is cut at each crossing into loops, each decomposed on its own.
tolerance is the smallest feature that counts, in the outline's own units:
points nearer each other than this are one point, and a vertex nearer than
this to the line through its neighbours lies on that line. No piece comes
back with fewer than three points, two points that close, a vertex that
flat, or a thickness under it. A sliver is merged into a neighbour where the
join stays convex and is dropped where it will not, which loses at most a
strip tolerance thick. At 0 (the default) it is a billionth of the
outline's size, which removes rounding and nothing anyone could see; a
physics wrapper passes its solver's own weld distance, so every piece
survives the solver's hull step.
Pieces wind counter-clockwise in standard axes (clockwise on screen) and
have at most max vertices; a max below 3 is taken as 3, since a triangle
cannot be split. An outline that is already convex and within budget comes
back as one piece, so the cheap case stays cheap; one with fewer than three
usable points comes back as [].
hullFor
function hullFor(points: readonly VecPt[], max = MAX_HULL_POINTS): VecPt[]
Outline → a point list Box2D will actually accept: convex, at most
MAX_HULL_POINTS vertices, in the shape's own local frame.
Do not re-centre the result on its centroid. Box2D computes centroid, mass and inertia from the polygon in body-local space, so passing the raw local points keeps the drawn outline and the collision shape registered. Re-centring looks tidier and is the bug — a shape that spins about a point it isn't drawn around.
isConvex
function isConvex(p: readonly VecPt[]): boolean
True if every turn goes the same way (a convex simple polygon).
simplifyHull
function simplifyHull(points: readonly VecPt[], max = MAX_HULL_POINTS): VecPt[]
Reduce a CONVEX outline to at most max vertices, dropping the ones that
cost the least area — so the silhouette survives as well as it can.
This is the step that stops an 11-point asteroid becoming a null Box2D
shape. Run convexHull first; on a concave input the areas are meaningless.
triangulate
function triangulate(points: readonly VecPt[]): Array<[number, number, number]>
Ear-clipping triangulation of a simple polygon — index triples into
points. Handles concave outlines and either winding; no holes, no
self-intersection. Degenerate input fans the remainder rather than looping.
This is what turns a vector OUTLINE into a filled face. It lives here rather than being borrowed from the 3D geometry module because the vector layer must never pull in the 3D chunk.
Constants
MAX_HULL_POINTS
const MAX_HULL_POINTS: 8
Vertex limit for one hull or convex piece: 8, Box2D's polygon maximum; the default max of the hull and piece functions.
Types
Vec2
interface Vec2
A 2D point or vector. Plain data, so anything can produce one.
x: number— Horizontal coordinate in the caller's own units (pixels for screen paths); grows rightward.y: number— Vertical coordinate in the caller's own units (pixels for screen paths); grows downward on screen.
VecHullApi
type VecHullApi = typeof kit
game.vecHull — every function this pack carries, as one namespace.
VecPt
type VecPt = Vec2
A point on a path — the same shape, named for what it is used as.


