Phase 5 Stage D slab: port placement + move + boundary/hole editors

Replicates the fence Stage D recipe for slab — three drag affordances
+ one placement tool, all routed through the registry:

  affordanceTools:
    'boundary-edit' → thin <PolygonEditor> wrapper (vertex/edge drag)
    'hole-edit'     → same for a single hole polygon
    move            → DragAction with single-undo dance
  tool: () => placement (multi-click polygon with axis/45° snap)

`PolygonEditor` + `PolygonEditorProps` exported from
`@pascal-app/editor` as Stage D transitional surface (Stage F cleanup
moves them into `@pascal-app/nodes`).

ToolManager mount sites for SlabBoundaryEditor / SlabHoleEditor now
route through `getRegistryAffordanceTool` with legacy fallback.

Slab move action does not use the live-drag exception (polygon CSG
rebuild every tick) — matches legacy behavior; optimization is a
separate task once we measure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-05-15 18:27:31 -04:00
co-authored by Claude Opus 4.7
parent 7b5a1c607a
commit b2e5d84986
8 changed files with 627 additions and 4 deletions
@@ -179,10 +179,31 @@ export const ToolManager: React.FC = () => {
rotation={buildingRotation as [number, number, number]}
>
{showZoneBoundaryEditor && selectedZoneId && <ZoneBoundaryEditor zoneId={selectedZoneId} />}
{showSlabBoundaryEditor && selectedSlabId && <SlabBoundaryEditor slabId={selectedSlabId} />}
{showSlabHoleEditor && selectedSlabId && editingHole && (
<SlabHoleEditor holeIndex={editingHole.holeIndex} slabId={selectedSlabId} />
)}
{showSlabBoundaryEditor &&
selectedSlabId &&
(() => {
const Registry = getRegistryAffordanceTool('slab', 'boundary-edit')
return Registry ? (
<Suspense fallback={null}>
<Registry slabId={selectedSlabId} />
</Suspense>
) : (
<SlabBoundaryEditor slabId={selectedSlabId} />
)
})()}
{showSlabHoleEditor &&
selectedSlabId &&
editingHole &&
(() => {
const Registry = getRegistryAffordanceTool('slab', 'hole-edit')
return Registry ? (
<Suspense fallback={null}>
<Registry holeIndex={editingHole.holeIndex} slabId={selectedSlabId} />
</Suspense>
) : (
<SlabHoleEditor holeIndex={editingHole.holeIndex} slabId={selectedSlabId} />
)
})()}
{showCeilingBoundaryEditor && selectedCeilingId && (
<CeilingBoundaryEditor ceilingId={selectedCeilingId} />
)}
+5
View File
@@ -14,6 +14,11 @@ export {
snapFenceDraftPoint,
} from './components/tools/fence/fence-drafting'
export { CursorSphere } from './components/tools/shared/cursor-sphere'
// Phase 5 Stage D — PolygonEditor for slab/ceiling boundary + hole editors.
export {
PolygonEditor,
type PolygonEditorProps,
} from './components/tools/shared/polygon-editor'
export {
formatAngleRadians,
getAngleToSegmentReference,