Skip to content

Math primitives

The math layer is plain readonly data and pure functions. Every other module builds on it.

The y axis points down, as on a canvas. A positive angle therefore turns +x towards +y, which appears clockwise on screen.

Vec is an unbranded { x, y } displacement. Use it for deltas, sizes and directions.

Function Result
vecAdd(a, b) a + b
vecSub(a, b) a - b
vecScale(v, s) v · s
vecRotate(v, θ) v rotated by θ radians
vecLength(v) The Euclidean length of v

Every function returns a plain Vec, even when its inputs are branded points.

Radians is a branded number. Create one with radians(value). A plain number does not type-check where Radians is expected, so a value in degrees cannot slip through by accident.

A point is a Vec branded with its coordinate space:

Type Space Constructor
PagePoint The infinite canvas pagePoint(x, y)
LocalPoint A frame’s unrotated space, origin top-left localPoint(x, y)
ScreenPoint The viewport, in CSS pixels screenPoint(x, y)

Passing a point from one space where another space is expected fails to compile. Any point is still a Vec, so the vector functions accept it. Their results are unbranded, and you must brand a result again on purpose.

Mat is a 2D affine transform with the fields a to f, in the order of the canvas setTransform call:

x' = a·x + c·y + e
y' = b·x + d·y + f
  • MAT_IDENTITY is the identity transform.
  • matTranslate(v), matRotate(θ) and matScale(sx, sy) build the basic transforms.
  • matMultiply(m, n) composes two transforms. The result applies n first, then m.
  • matApply(m, v) transforms a vector.

There is no inverse yet. Frame conversions undo their transform step by step instead.

Box is an axis-aligned box { minX, minY, maxX, maxY }.

  • boxFromPoints(points) returns the smallest box that holds every point. The list must contain at least one point, and the type enforces this.
  • boxUnion(a, b) returns the smallest box that holds both boxes.
  • boxContains(box, p) checks whether p is inside the box. Points on an edge count as inside.
  • boxIntersects(a, b) checks whether two boxes overlap. Boxes that only touch along an edge or at a corner count as overlapping, so a zero-width box still intersects what it lies on.