# bgm-tabletop a r3f based interactive component library to work with [bgm](./bgm-format.md) board games. will be used somewhere in the `web` app's bgm inspector routes. ## 1. stack `react` - react, react router, tailwindv4 `r3f` - r3f, drei, postprocessing `zustand` - for state management ## 2. states source-of-truth game state: ```ts { surfaces: Record, // enabled per surface id parts: Record, // part id -> placement state } interface PartState { path: string, // the path key this part is on index: number, // the part's position in its path's stack facing: 'face' | 'back' | 'standing', // how the part is oriented on the board } ``` **assumption:** each piece on the board has a unique id, even tokens of the same type. so a part id appears at most once, and a path's ordered children (for stacking) are derived from the map by sorting on `index`. this makes the render list keyed by piece id stable and unambiguous. derived surface render state: game state + surface routes => map of piece id to `{ surface, route, candidate, index, stackSize, facing }` for rendering on a surface. keys of this map makes a stable render list. - `route` - the matched route. - `candidate` - the matched candidate for a `:param` route, carrying its anchor `x`/`y`/`rotation`. absent for routes without candidates. - `index` - the piece's position in its path's stack. - `stackSize` - the number of pieces on the path. - `facing` - how the piece is oriented on the board (`face` / `back` / `standing`). the render map is per enabled surface: a piece may appear on more than one enabled surface (e.g. an expansion path and the main board), and each is rendered independently. ## 3. components - `SetupLoader` side effect only component that seeds the game state with setup (enabled surfaces + part placement). - `WorldSurfaceView` mounts a surface to world space. - `HudSurfaceView` mounts a surface to hud space. - `PartPlacement` a stable per-part component that positions a part on a surface location. uses the stacking hook (below) to apply the route's stacking strategy. - `PartView` used in `PartPlacement`, creates a mesh from part definition. a standalone component library, so it reuses geometry/shape code from `@tts/mesh` rather than the web app's viewers. ## 4. stacking the format's stacking strategy (`curve` / `limit` / `align` / `steps` / `tilt` / `zStart` / `zEnd`, see `bgm-format.md` §4) is implemented as a hook, e.g. `useStacking(route.stacking, index, stackSize)`, returning the offset/rotation to apply to a piece: `{ x, y, rotation, z, tilt }`. `x`/`y`/`rotation` come from the `curve`; `z` is the surface-normal height ramped from `zStart` to `zEnd`; `tilt` is the rotation about the card's local Y (long) axis, applied to every part. `PartPlacement` consumes it. ## 5. commands Scripted interaction — focus, tap-to-advance, move, caption — is built on an async command layer. See [`bgm-commands.md`](./bgm-commands.md) for command execution (lifecycle, run contexts, tap interaction). ## 6. usage - we will inspect individual parts with `PartView` in the web app's part inspection route. - as a library, the public surface is the components above: mount a surface with `WorldSurfaceView`/`HudSurfaceView`, seed state with `SetupLoader`, and let `PartPlacement`/`PartView` render the pieces. the web app is one consumer; the library should not assume the web app's routes or store. - a surface is mounted only when enabled; a disabled surface is not rendered.