feat(tabletop): add state, setup, mounting, and stacking

Implement the tabletop library's game state store, setup seeding with
bare-type expansion, surface mount tree resolution, part placement, and
the stacking positioning process with a dependency-free SVG path helper.
Wire the public API and add unit plus vite integration tests.
This commit is contained in:
2026-08-09 22:34:18 +08:00
parent ef0695ed04
commit cd08e6af04
20 changed files with 1281 additions and 11 deletions
+10 -9
View File
@@ -3,7 +3,8 @@
> **Scope:** A standalone r3f component library that renders [bgm](./bgm-format.md)
> board games: a state store, surface mounting, part placement with stacking,
> and per-part meshes. Design: [`bgm-tabletop.md`](./bgm-tabletop.md).
> **Status:** planning — no code yet.
> **Status:** items 18 implemented; web part-inspection route renders `PartView`
> from the library. Remaining: wiring the full tabletop scene into a web route.
## Goal
@@ -42,7 +43,7 @@ packages/tabletop/
## Work items
### 1. Package scaffold
### 1. Package scaffold
- New `packages/tabletop` workspace package (`pnpm-workspace.yaml` already
globs `packages/*`).
@@ -51,7 +52,7 @@ packages/tabletop/
`@types/three`.
- `tsconfig.json` mirroring `packages/bgm`'s (strict, ESM, `dist` output).
### 2. Part meshes + export + web integration
### 2. Part meshes + export + web integration
First deliverable: `PartView` renders a single part's mesh from its definition,
reusing `@tts/mesh` geometry (not the web app's viewers). This is the smallest
@@ -71,7 +72,7 @@ useful slice and unblocks the web app's part inspection route immediately.
(`/bgm/:id/parts/:type/:part`) to render `PartView` from the library, proving
it end-to-end.
### 3. State store (`state.ts`)
### 3. State store (`state.ts`)
Source-of-truth game state per `bgm-tabletop.md` §2:
@@ -89,7 +90,7 @@ interface GameState {
- **Assumption**: each piece id is unique within a path (documented in
`bgm-tabletop.md`); the render map is keyed by piece id.
### 4. Setup seeding (`setup.ts`)
### 4. Setup seeding (`setup.ts`)
- `SetupLoader`: side-effect-only component that seeds the store from a
`Setup` — enables its `surfaces` (or all when omitted) and places parts on
@@ -98,7 +99,7 @@ interface GameState {
type (documented in `bgm-format.md` §3; the loader doesn't do this — it's a
game-state init concern, so it lives here).
### 5. Surface mounting (`mount.ts`)
### 5. Surface mounting (`mount.ts`)
- Resolve the surface mount tree from `Surface.mount` + `Surface.children`:
- `kind: table` — root, world space.
@@ -108,13 +109,13 @@ interface GameState {
surface isn't rendered. Child surfaces mount relative to their parent's
anchor (`x`/`y`/`rotation`).
### 6. Part placement (`placement.ts`)
### 6. Part placement (`placement.ts`)
- `PartPlacement`: stable per-part component that positions a part on a surface
location from the derived render state (route anchor + candidate anchor).
- Applies the route's stacking strategy via `useStacking`.
### 7. Stacking (`stacking.ts`)
### 7. Stacking (`stacking.ts`)
- `useStacking(route.stacking, index, stackSize)` → `{ offset, rotation }`.
- Implements the format's positioning process (`bgm-format.md` §4): step
@@ -122,7 +123,7 @@ interface GameState {
`center`), and `limit` (`0` all, `n` first n, `-n` last n).
- Curve length from an SVG path string (small helper; no new dep).
### 8. Public API (`index.ts`)
### 8. Public API (`index.ts`)
Export `SetupLoader`, `WorldSurfaceView`, `HudSurfaceView`, `PartPlacement`,
`PartView`, `useStacking`, and the store hooks. The web app consumes these; the