# Screenshots

README + docs gallery, captured straight out of the playground via the
viewer's own `exportScreenshot` ref method. No external CAD render or
hand-edit involved — every PNG below was generated by automating the
playground over `window.__viewer`.

## How to refresh

The playground exposes two devtools handles for scripted capture:
`window.__viewer` (Proxy onto the live `XtsViewer3DRef`) and
`window.__setDemo` (switches the active sample by `DemoKey`). It also
ships a Vite dev middleware at `POST /__save-screenshot?path=...` that
writes the body bytes to a whitelisted file under `docs/screenshots/`
(see `vite.config.ts → capturedScreenshotSaver`).

Two ways to refresh:

**Manual** (one shot, hand-framed):

1. `npm run dev` — open the playground.
2. Pick the demo from the sidebar (names match the filenames below).
3. Frame the camera (ViewCube for clean isometric / top-down, or the
   sidebar's **Reset camera** button for `zoomToFit`).
4. Open devtools and:

   ```js
   const r = await window.__viewer.exportScreenshot({
     mode: 'current',        // or 'top-down'
     pixelRatio: 2,
     format: 'png',
     backgroundColor: null,  // transparent
   });
   await fetch('/__save-screenshot?path=docs/screenshots/02-multi-track.png', {
     method: 'POST',
     body: r.blob,
     headers: { 'content-type': 'image/png' },
   });
   ```

**Automated** (re-shoot the whole gallery): drive the same loop over the
demo keys below from any browser-automation tool (Claude Preview / in
Chrome MCP, Playwright, …). Switch with `window.__setDemo('<key>')`,
wait ~2 s for GLBs + initial frame, then call the eval above.

## File map

| File | Demo key | Camera | Notes |
|------|------|--------|-------|
| `01-oval-loop.png`            | `oval-loop`     | 3⁄4 isometric  | hero shot, env reflections on |
| `02-multi-track.png`          | `multi-track`   | 3⁄4 isometric  | shows `trackTransform` placement |
| `03-stations-areas.png`       | `areas`         | 3⁄4 isometric  | cleanroom / safety-loop tubes |
| `04-stator-heatmap.png`       | `heatmap`       | 3⁄4 isometric  | green → red gradient, `displacementMm: 35` lifts the tube above the rail |
| `05-collision.png`            | `collision`     | 3⁄4 isometric  | two movers touching mid-track |
| `06-drive-status.png`         | `drive-status`  | 3⁄4 isometric  | pulsing emissives + ▲ / ⊙ status icons |
| `07-perf-stress.png`          | `perf-stress`   | wide isometric | 3 ovals × 250 movers = 750 |
| `08-shadows.png`              | `standard` + shadows on | low-angle | PCF-soft shadows + IBL |
| `09-screenshot-export.png`    | `oval-loop`     | top-down ortho | demonstrates `exportScreenshot('top-down')` itself |

All PNGs are RGBA with a transparent alpha channel, captured at
`pixelRatio: 2` (~3000 × 2400 px) so they stay crisp at 1× and shrink
cleanly to the README's two-column gallery rendering.
