docs: document the A3 test rig; note server-authoritative motion and proxy cache override

This commit is contained in:
2026-08-24 10:40:23 +10:00
parent ef3c13d8c8
commit 518b29860a
+43 -6
View File
@@ -26,12 +26,39 @@ Open in `EXHIBIT_MODE=dev`; password-protected in `EXHIBIT_MODE=final`
| `/admin/characters.html` | Characters | Per-ghost visuals: model, part placement, gradient colours, opacity, height, scale, face/torso textures. | | `/admin/characters.html` | Characters | Per-ghost visuals: model, part placement, gradient colours, opacity, height, scale, face/torso textures. |
| `/admin/landing.html` | Landing Page | Visitor landing screen: title, logo, background, accent colour, about text, disclaimer. | | `/admin/landing.html` | Landing Page | Visitor landing screen: title, logo, background, accent colour, about text, disclaimer. |
| `/admin/preview.html` | Virtual Viewport | Live orbit view of whatever ghosts are currently active, straight off the same WebSocket feed as the exhibit. Ground truth when the AR view disagrees. | | `/admin/preview.html` | Virtual Viewport | Live orbit view of whatever ghosts are currently active, straight off the same WebSocket feed as the exhibit. Ground truth when the AR view disagrees. |
| `/admin/testsheet.html` | A3 Test Rig | Printable A3 calibration/evaluation sheet, and a one-click load of the matching scene + playlist. See below. |
| `/admin/plan.html` | Placement Plan | Measured plan for physically positioning crests and models on the real table. | | `/admin/plan.html` | Placement Plan | Measured plan for physically positioning crests and models on the real table. |
| `/admin/print.html` | Print Crests | Generates printable ArUco markers ("Newbury Crests") at the configured `sizeMM`. | | `/admin/print.html` | Print Crests | Generates printable ArUco markers ("Newbury Crests") at the configured `sizeMM`. |
| `/admin/calibrate.html` | Calibrate | Camera/marker calibration helper. Detects crests **without** filtering against the scene, so it's the quickest way to prove a printed crest is readable at all. | | `/admin/calibrate.html` | Calibrate | Camera/marker calibration helper. Detects crests **without** filtering against the scene, so it's the quickest way to prove a printed crest is readable at all. |
| `/admin/errors.html` | Client Errors | Errors reported by visitor devices (JS errors, unhandled rejections, ghost build and scene-fetch failures). First stop when something works on one phone but not another. | | `/admin/errors.html` | Client Errors | Errors reported by visitor devices (JS errors, unhandled rejections, ghost build and scene-fetch failures). First stop when something works on one phone but not another. |
| `/admin/login.html` | Login | Only relevant in final mode. | | `/admin/login.html` | Login | Only relevant in final mode. |
## The A3 test rig
`/admin/testsheet.html` is a self-contained test environment for evaluating
tracking, occlusion and lighting without the LEGO town in the way.
- **Print A3 landscape at 100% ("Actual size"), never "Fit to page."** Check the
100 mm scale bar first: if it doesn't measure 100 mm, every crest is the wrong
size and every pose is scaled with it.
- The sheet *is* the table — A3 landscape is exactly 42.0 × 29.7 cm. Origin is
the front-left corner, matching the exhibit's world frame.
- **6 crests** at 60 mm, spread 38.7 cm corner to corner (the spread is what
makes the joint solve well-conditioned).
- **Cylinder occluder**: stand an 85 mm ⌀ × 160 mm tall object on the dashed
circle at centre.
- **Static ghost** at (21, 22) cm, deliberately *behind* the cylinder from the
front — walk around it and the ghost should disappear behind the post.
- **Walking ghost** on an 18 × 12 cm rectangular loop around the cylinder;
60 cm perimeter, ~40 s per lap at the default 1.5 cm/s.
- "Load this layout onto the server" writes both the scene and the playlist,
built from the same constants that drew the sheet, so print and config can
never drift apart. It **replaces** the live scene and playlist — save your
current layout first.
Known gap: the layout editor doesn't understand cylinder occluders, so opening
and saving this layout there will drop the cylinder.
## Useful API endpoints ## Useful API endpoints
Read-only ones are handy to open directly in a phone browser while debugging; Read-only ones are handy to open directly in a phone browser while debugging;
@@ -50,15 +77,25 @@ the rest need auth in final mode.
| `/api/client-errors` | Reported client errors (auth in final mode) | | `/api/client-errors` | Reported client errors (auth in final mode) |
| `/ws` | WebSocket: `scene`, `active`, `spawn`, `despawn`, `characters`, `pong` | | `/ws` | WebSocket: `scene`, `active`, `spawn`, `despawn`, `characters`, `pong` |
## Gotcha worth remembering ## Gotchas worth remembering
Detection filters camera detections against the marker IDs in the **active **Detection filters against the active scene.** A crest that isn't an anchor in
scene**. A crest that isn't an anchor in the current layout is detected and then the current layout is detected and then discarded, which used to look exactly
discarded, which used to look exactly like "the camera can't see anything". like "the camera can't see anything".
- The scene is now fetched over HTTP at start, so a down or flapping WebSocket - The scene is fetched over HTTP at start, so a down or flapping WebSocket no
no longer breaks tracking. longer breaks tracking.
- The HUD reports `scene N anchors` (or `NOT LOADED`) and `markers 1/2 seen - The HUD reports `scene N anchors` (or `NOT LOADED`) and `markers 1/2 seen
[0,7]` — usable over raw — so a filtered-out crest is visible at a glance. [0,7]` — usable over raw — so a filtered-out crest is visible at a glance.
- The tracking chip says "Crest N not in this layout" rather than "Point at a - The tracking chip says "Crest N not in this layout" rather than "Point at a
Newbury Crest" when this happens. Newbury Crest" when this happens.
**Motion amounts live on the server.** `playlist.js` sends explicit `bobAmp`,
`radius`, `speed` values with every spawn, so the client's defaults never apply
to a real ghost. Tune motion in the playlist config (`behaviors.bobAmp`,
`wanderRadius`, `pathSpeed`), not in `behavior.js`.
**An upstream proxy can override cache headers.** The app sends `no-cache` for
JS and HTML, but openresty in front has been seen replacing it with a daily
`expires`. If a deploy doesn't reach a device, check the response headers and
test in a private tab.