| 1 | /** |
| 2 | * The collider overlay: every boundary the physics actually uses, drawn in thin |
| 3 | * red over the table. |
| 4 | * |
| 5 | * Everything the tiles bounce off is *measured* off the DOM rather than written |
| 6 | * down anywhere — the felt is where the four strips leave off, the wall is |
| 7 | * seventy-odd real elements — so the only honest way to check a bounce is to |
| 8 | * draw the boxes in the same coordinate space the tiles are drawn in. A bounce |
| 9 | * that looks wrong is either a box in the wrong place or a box you had not |
| 10 | * thought about, and this tells you which. |
| 11 | * |
| 12 | * On with `?colliders` in the address bar, or `colliders()` from the console |
| 13 | * while a hand is running — which is the point of it, since the thing you want |
| 14 | * to look at is usually already skidding across the table. |
| 15 | */ |
| 16 | |
| 17 | const FLAG = 'colliders'; |
| 18 | |
| 19 | let on = typeof location !== 'undefined' && new URLSearchParams(location.search).has(FLAG); |
| 20 | |
| 21 | const listeners = new Set<() => void>(); |
| 22 | |
| 23 | export const showingColliders = () => on; |
| 24 | |
| 25 | /** Toggle, or set. Returns what it ended up as, so the console says it back. */ |
| 26 | export function toggleColliders(next = !on): boolean { |
| 27 | on = next; |
| 28 | for (const cb of listeners) cb(); |
| 29 | return on; |
| 30 | } |
| 31 | |
| 32 | /** Repaint when it changes — a settled table is not drawing frames otherwise. */ |
| 33 | export function onCollidersChange(cb: () => void): () => void { |
| 34 | listeners.add(cb); |
| 35 | return () => { |
| 36 | listeners.delete(cb); |
| 37 | }; |
| 38 | } |
| 39 | |
| 40 | declare global { |
| 41 | interface Window { |
| 42 | colliders?: (next?: boolean) => boolean; |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | if (typeof window !== 'undefined') window.colliders = toggleColliders; |