fix: sync ceiling handles during polygon edits
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import { type CeilingNode, resolveLevelId, useLiveNodeOverrides, useScene } from '@pascal-app/core'
|
||||
import { PolygonEditor, triggerSFX } from '@pascal-app/editor'
|
||||
import { useViewer } from '@pascal-app/viewer'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
|
||||
/**
|
||||
* Phase 5 Stage D — ceiling boundary editor (registry-driven).
|
||||
@@ -25,8 +25,17 @@ export const CeilingBoundaryEditor: React.FC<{ ceilingId: CeilingNode['id'] }> =
|
||||
const setSelection = useViewer((s) => s.setSelection)
|
||||
const setHoveredId = useViewer((s) => s.setHoveredId)
|
||||
const ownsCeilingHoverRef = useRef(false)
|
||||
const ownsPolygonPreviewRef = useRef(false)
|
||||
const liveOverride = useLiveNodeOverrides((state) => {
|
||||
if (ownsPolygonPreviewRef.current) return null
|
||||
return state.overrides.get(ceilingId) as Partial<CeilingNode> | undefined
|
||||
})
|
||||
|
||||
const ceiling = ceilingNode?.type === 'ceiling' ? (ceilingNode as CeilingNode) : null
|
||||
const effectiveCeiling = useMemo(
|
||||
() => (ceiling && liveOverride ? ({ ...ceiling, ...liveOverride } as CeilingNode) : ceiling),
|
||||
[ceiling, liveOverride],
|
||||
)
|
||||
|
||||
const handlePolygonChange = useCallback(
|
||||
(newPolygon: Array<[number, number]>) => {
|
||||
@@ -39,11 +48,13 @@ export const CeilingBoundaryEditor: React.FC<{ ceilingId: CeilingNode['id'] }> =
|
||||
const handlePolygonPreview = useCallback(
|
||||
(preview: ReadonlyArray<readonly [number, number]> | null) => {
|
||||
if (preview) {
|
||||
ownsPolygonPreviewRef.current = true
|
||||
useLiveNodeOverrides.getState().set(ceilingId, {
|
||||
polygon: preview.map(([x, z]) => [x, z] as [number, number]),
|
||||
})
|
||||
} else {
|
||||
useLiveNodeOverrides.getState().clear(ceilingId)
|
||||
ownsPolygonPreviewRef.current = false
|
||||
}
|
||||
markDirty(ceilingId)
|
||||
},
|
||||
@@ -74,15 +85,28 @@ export const CeilingBoundaryEditor: React.FC<{ ceilingId: CeilingNode['id'] }> =
|
||||
|
||||
const handleDragStateChange = useCallback(
|
||||
(isDragging: boolean) => {
|
||||
if (!isDragging) {
|
||||
ownsPolygonPreviewRef.current = false
|
||||
}
|
||||
setCeilingHandleHover(isDragging)
|
||||
},
|
||||
[setCeilingHandleHover],
|
||||
)
|
||||
|
||||
const handlePolygonEditorDragStart = useCallback(() => {
|
||||
ownsPolygonPreviewRef.current = true
|
||||
triggerSFX('sfx:item-pick')
|
||||
}, [])
|
||||
|
||||
const handlePolygonEditorBeforeVertexDrag = useCallback(() => {
|
||||
ownsPolygonPreviewRef.current = true
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
useLiveNodeOverrides.getState().clear(ceilingId)
|
||||
useScene.getState().markDirty(ceilingId)
|
||||
ownsPolygonPreviewRef.current = false
|
||||
if (ownsCeilingHoverRef.current && useViewer.getState().hoveredId === ceilingId) {
|
||||
useViewer.getState().setHoveredId(null)
|
||||
}
|
||||
@@ -90,25 +114,26 @@ export const CeilingBoundaryEditor: React.FC<{ ceilingId: CeilingNode['id'] }> =
|
||||
}
|
||||
}, [ceilingId])
|
||||
|
||||
if (!ceiling?.polygon || ceiling.polygon.length < 3) return null
|
||||
if (!effectiveCeiling?.polygon || effectiveCeiling.polygon.length < 3) return null
|
||||
|
||||
return (
|
||||
<PolygonEditor
|
||||
allowEdgeMove
|
||||
color="#d4d4d4"
|
||||
highlightConnectedHandles
|
||||
levelId={resolveLevelId(ceiling, useScene.getState().nodes)}
|
||||
levelId={resolveLevelId(effectiveCeiling, useScene.getState().nodes)}
|
||||
minVertices={3}
|
||||
onBeforeVertexDrag={handlePolygonEditorBeforeVertexDrag}
|
||||
onDragStateChange={handleDragStateChange}
|
||||
onDragCommit={() => triggerSFX('sfx:item-place')}
|
||||
onDragStart={() => triggerSFX('sfx:item-pick')}
|
||||
onDragStart={handlePolygonEditorDragStart}
|
||||
onEdgeHoverChange={handleHandleHoverChange}
|
||||
onMidpointHoverChange={handleHandleHoverChange}
|
||||
onPolygonChange={handlePolygonChange}
|
||||
onPolygonPreview={handlePolygonPreview}
|
||||
onVertexHoverChange={handleHandleHoverChange}
|
||||
polygon={ceiling.polygon}
|
||||
surfaceHeight={ceiling.height ?? 2.5}
|
||||
polygon={effectiveCeiling.polygon}
|
||||
surfaceHeight={effectiveCeiling.height ?? 2.5}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user