Phase 5 Stage C: item floor plan — all 9 kinds now at Stage C

Item is the last Stage A-only kind to gain def.floorplan. Closes Stage C
across every registered kind.

Files added:
 - nodes/src/item/floorplan.ts: buildItemFloorplan inlines a self-
   contained parent-chain transform walker using `ctx.resolve`. Mirrors
   the legacy `getItemFloorplanTransform` math from editor/lib/floorplan/
   items.ts:
     * Wall parent: rotate item.position by wall's angle, anchor at
       wall.start, handle wall-side attachTo via wall.thickness offset.
     * Item parent (nested): recurse for parent's transform.
     * Level / slab / ceiling parent: item.position is level-local.
   Returns a rotated width × depth rectangle. asset.floorPlanUrl image
   overlay deferred for Phase 5 follow-up.

Files changed:
 - nodes/src/item/definition.ts: wires `floorplan: buildItemFloorplan`.
 - floorplan-panel.tsx: floorplanItemEntries useMemo short-circuits to
   [] when nodeRegistry.has('item'). Phase 6 deletes the entire useMemo.

Stage C coverage (all 9 registered kinds):
 shelf  spawn  fence  slab  ceiling  wall  door  window  item 

Next sessions: Stage B for door / window / wall (each large geometry
extraction), Stage D per kind (DragAction affordance ports), Stage E
(drop legacy panels), Phase 6 Stage F cleanup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-05-15 16:50:30 -04:00
co-authored by Claude Opus 4.7
parent 9bcb25d0aa
commit 9aec943740
3 changed files with 145 additions and 17 deletions
+13 -17
View File
@@ -1,4 +1,5 @@
import type { ItemNode as ItemNodeType, NodeDefinition } from '@pascal-app/core'
import { buildItemFloorplan } from './floorplan'
import { itemParametrics } from './parametrics'
import { ItemNode } from './schema'
@@ -18,25 +19,17 @@ import { ItemNode } from './schema'
* placement. The smooth generic mover can't express that. Legacy
* mover keeps running via capability-driven dispatch.
* - `selectable`, `duplicable`, `deletable` standard.
* - Items have a catalog-defined `surface.height` (some items act as
* tables — they expose a surface other items stack on). For Stage A
* we don't surface this via `capabilities.surfaces.top` yet —
* legacy ItemSystem computes the stack y via spatial-grid lookups.
* Phase 5+ may surface it.
*
* Stages:
* - A: registered.
* - B: N/A — def.renderer escape hatch (GLB / useGLTF).
* - C: `def.floorplan` resolves parent chain via `ctx.resolve`,
* returns a rotated rectangle (width × depth). Mirrors the legacy
* `getItemFloorplanTransform` math. Legacy `floorplanItemEntries`
* short-circuits when item is registered.
*
* `toolHints`: matches the legacy ItemHelper UI (mouse / R / T / Shift /
* Esc) — same panel the user sees during placement. Once item registers,
* `HelperManager` consults `def.toolHints` and renders the
* `RegisteredToolHelper` for placement (the legacy ItemHelper still
* renders for movingNode state — that's a generic "you're moving
* something" panel, not item-specific; Phase 5+ may deprecate it).
*
* Renderer + system: wrap-export of legacy ItemRenderer + bundle of
* ItemSystem + ItemLightSystem.
*
* Tool field absent: catalog UI + item-tool placement flow stays on
* editor state. Phase 5+ may port to `DragAction` once the registry's
* catalog-aware affordances exist.
* Esc) — registry-driven placement panel.
*/
export const itemDefinition: NodeDefinition<typeof ItemNode> = {
kind: 'item',
@@ -86,6 +79,9 @@ export const itemDefinition: NodeDefinition<typeof ItemNode> = {
// Same priority as the legacy ItemSystem.
priority: 2,
},
// Stage C: floor-plan polygon. ctx.resolve walks the parent chain
// (wall / nested item / level) to compute the world-space transform.
floorplan: buildItemFloorplan,
toolHints: [
{ key: 'Left click', label: 'Place item' },