feat(editor): unify placement/move facing triangle into one editor-side renderer
Every placement and move path now publishes its ghost pose to a single
`useFacingPose` store, drawn by one editor-side `<FacingPoseIndicator>` overlay,
instead of each path drawing its own triangle (which left the nodes-package and
PlacementBox paths invisible):
- column/shelf presets + all moves (PlacementBox via move-registry, and
DragBoundingBox) now publish the pose, so the triangle finally shows
- stair create + move use a declarative `facingIndicator: { reversed: true }`
(new registry resolver) so the triangle sits before the entry pointing out —
resolved in one place, so create and move match automatically
- stair placement defaults to single and respects the shared `point`
continuation (C) toggle, like the other placement tools
Checkpoint on the placement-interaction epic: also carries the in-flight
continuation-profile extraction (lib/continuation), grid surface (item #8), and
HUD work. Door/window still render their own legacy inline triangle and are
migrated to the overlay next.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
76089d85ea
commit
68e5c6ca67
@@ -315,6 +315,7 @@ function columnHandles(node: ColumnNodeType): HandleDescriptor<ColumnNodeType>[]
|
||||
export const columnDefinition: NodeDefinition<typeof ColumnNode> = {
|
||||
kind: 'column',
|
||||
snapProfile: 'item',
|
||||
facingIndicator: true,
|
||||
schemaVersion: 1,
|
||||
schema: ColumnNode,
|
||||
category: 'structure',
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
triggerSFX,
|
||||
useAlignmentGuides,
|
||||
useEditor,
|
||||
useFacingPose,
|
||||
usePlacementPreview,
|
||||
} from '@pascal-app/editor'
|
||||
import { useViewer } from '@pascal-app/viewer'
|
||||
@@ -101,6 +102,13 @@ const ColumnTool = () => {
|
||||
levelId: activeLevelId,
|
||||
})
|
||||
cursorRef.current?.position.set(...visualPosition)
|
||||
// Forward-facing floor triangle, drawn by the editor-side overlay. Columns
|
||||
// never rotate (`rotation: 0`), so the triangle just sits in front.
|
||||
useFacingPose.getState().set({
|
||||
position: visualPosition,
|
||||
rotationY: previewNode.rotation,
|
||||
depth: previewNode.depth,
|
||||
})
|
||||
lastCursorRef.current = position
|
||||
|
||||
// Publish a transient, positioned preview node for the 2D floor-plan
|
||||
@@ -130,12 +138,17 @@ const ColumnTool = () => {
|
||||
useScene.getState().createNode(column, activeLevelId)
|
||||
useViewer.getState().setSelection({ selectedIds: [column.id] })
|
||||
triggerSFX('sfx:structure-build')
|
||||
// The placed column is now a valid alignment target for the next one;
|
||||
// refresh the candidate pool and drop the guide from this drop. The
|
||||
// 2D ghost re-publishes on the next move.
|
||||
alignmentCandidates = collectAlignmentAnchors(useScene.getState().nodes, previewNode.id)
|
||||
useAlignmentGuides.getState().clear()
|
||||
usePlacementPreview.getState().clear()
|
||||
if (useEditor.getState().getContinuation('point') === 'repeat') {
|
||||
// The placed column is now a valid alignment target for the next one.
|
||||
alignmentCandidates = collectAlignmentAnchors(useScene.getState().nodes, previewNode.id)
|
||||
} else {
|
||||
cursorVisibleRef.current = false
|
||||
setCursorVisible(false)
|
||||
useFacingPose.getState().clear()
|
||||
useEditor.getState().setTool(null)
|
||||
}
|
||||
stopPlacementCommitPropagation(event)
|
||||
}
|
||||
|
||||
@@ -147,6 +160,7 @@ const ColumnTool = () => {
|
||||
unsubscribePlacementClicks()
|
||||
useAlignmentGuides.getState().clear()
|
||||
usePlacementPreview.getState().clear()
|
||||
useFacingPose.getState().clear()
|
||||
}
|
||||
}, [activeLevelId, previewNode])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user