Replicates the slab Stage D recipe for ceiling: four affordances routed through the registry — def.tool for the placement flow, def.affordanceTools for boundary edit / hole edit / whole-ceiling move. Ceiling-specific bits preserved: - Placement tool keeps the dual-cursor + vertical TSL-gradient connector + ground-shadow lines (1:1 with legacy). - Move tool wrapper renders the translucent preview fill + outline overlay so the user sees the destination before clicking. ToolManager mount sites for CeilingBoundaryEditor / CeilingHoleEditor now route through `getRegistryAffordanceTool` with legacy fallback. Per-kind progress: ceiling A ✅ B (n/a, def.renderer escape hatch preserved) C ✅ D ✅; E + F pending. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
1.5 KiB
TypeScript
48 lines
1.5 KiB
TypeScript
'use client'
|
|
|
|
import { type CeilingNode, resolveLevelId, useScene } from '@pascal-app/core'
|
|
import { PolygonEditor } from '@pascal-app/editor'
|
|
import { useViewer } from '@pascal-app/viewer'
|
|
import { useCallback } from 'react'
|
|
|
|
/**
|
|
* Phase 5 Stage D — ceiling boundary editor (registry-driven).
|
|
*
|
|
* Thin wrapper around the shared `<PolygonEditor>` (same shape as
|
|
* slab's boundary-editor). Activates when a ceiling is selected in
|
|
* structure/select mode and no hole edit is in progress.
|
|
*/
|
|
export const CeilingBoundaryEditor: React.FC<{ ceilingId: CeilingNode['id'] }> = ({
|
|
ceilingId,
|
|
}) => {
|
|
const ceilingNode = useScene((s) => s.nodes[ceilingId])
|
|
const updateNode = useScene((s) => s.updateNode)
|
|
const setSelection = useViewer((s) => s.setSelection)
|
|
|
|
const ceiling = ceilingNode?.type === 'ceiling' ? (ceilingNode as CeilingNode) : null
|
|
|
|
const handlePolygonChange = useCallback(
|
|
(newPolygon: Array<[number, number]>) => {
|
|
updateNode(ceilingId, { polygon: newPolygon })
|
|
setSelection({ selectedIds: [ceilingId] })
|
|
},
|
|
[ceilingId, updateNode, setSelection],
|
|
)
|
|
|
|
if (!ceiling?.polygon || ceiling.polygon.length < 3) return null
|
|
|
|
return (
|
|
<PolygonEditor
|
|
allowEdgeMove
|
|
color="#d4d4d4"
|
|
levelId={resolveLevelId(ceiling, useScene.getState().nodes)}
|
|
minVertices={3}
|
|
onPolygonChange={handlePolygonChange}
|
|
polygon={ceiling.polygon}
|
|
surfaceHeight={ceiling.height ?? 2.5}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export default CeilingBoundaryEditor
|