number of colliding pairs emitted this frame
this frame's findCollidingPairs output — the pooled narrowphase input, flat with stride 3: [bodyIndexA, bodyIndexB, pairRecordIndex]. collidingPairCount valid pairs. record indices are stable for the whole frame (freelist storage — records never move).
per-record chain head: the contact index at the head of each record's contact chain, or
INVALID_CONTACT_KEY when the record has no contacts. parallel-by-record like poseCache (NOT a
records-stride slot — this keeps contacts.ts free of any pair layout constant, so its only
dependency on pairs is the erased Pairs type). addPairRecord resets the head to empty.
free record indices available for reuse
the move set: indices of bodies whose fat leaf changed (escaped, added, or changed layer), awaiting pair discovery. consumed by findCollidingPairs. deduped via body.inMoveSet (invariant: flag set ⟺ index present here); insertion order is deterministic.
per-record last-narrowphase pose cache, flat with stride CACHE_STRIDE (8), indexed by record
(parallel to records): [valid, dpx, dpy, dpz, drx, dry, drz, drw]. deltaPosition is body B's
COM relative to body A expressed in body A's local frame; deltaRotation is inv(rA) * rB.
pose slots are meaningful only when the valid flag is 1. free record slots simply go stale —
addPairRecord resets valid=0 on reuse.
number of LIVE records (records.length / RECORD_STRIDE counts slots: live + free)
persistent overlapping-pair records, flat with stride RECORD_STRIDE (6): [bodyIndexA, bodyIndexB, prevEdgeA, nextEdgeA, prevEdgeB, nextEdgeB]. the prev/next slots hold packed pair edge keys (pairEdgeKey) linking the record into each body's intrusive pair list; INVALID_PAIR_KEY marks a list end. storage is a freelist with STABLE record indices (no swap-remove): a free slot is marked bodyIndexA === -1 and its index sits in freeRecords for reuse.
persistent overlapping-pair state for a physics world.
owns the persistent pair set (discovered by the broadphase move-set), its per-record body-pair relative-pose cache (folded in from the old contacts.cachedBodyPairs map), and the per-frame emitted narrowphase input.
records are linked into per-body intrusive pair lists (body.headPairKey, mirroring the contact edge lists in contacts.ts) — dedup and lookup walk a body's list instead of a key map, keeping the whole state plain flat arrays (JSON-serializable, no Map).