feat(editor): placement & interaction overhaul — FSM spine, bug tracks, perf
Implements plans/editor-placement-interaction-overhaul.md: an authoritative interaction-scope state machine plus the catalogued placement/interaction fixes, and split-view floor-plan performance. - Interaction-scope spine (lib/interaction/* + store/use-interaction-scope), driven from central useEditor setters; overlay scoping (zone labels, context badges, floating action menu) reads resolveOverlayPolicy. - Bug tracks A/B/D/E/F/G/H: handle/cutout raycast, footprint validity, auto-slab loop, ceiling hosting, B-key tool desync, 2D drop offset, per-frame jank. - Snapping modes (grid/lines/angles/off) + contextual HUD chips; modifier model (Shift=cycle, Alt=free place, Ctrl=grid step). - Item move now tracks the cursor 1:1 (was a laggy per-frame lerp); handle rig hides during a whole-node move; rotate gizmo advertises Shift=free rotation in the HUD and hides the move cross while rotating. - Floor-plan perf: pause live reactivity while in 3D-only view; per-node geometry cache so only changed nodes rebuild on a drag; hoist wall miters to a once-per-pass ctx.levelData (O(N^2) -> O(N) on wall/opening drags). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b2f1a8432e
commit
f773e6b8c5
@@ -315,7 +315,8 @@ export const itemDefinition: NodeDefinition<typeof ItemNode> = {
|
||||
{ key: 'Left click', label: 'Place item' },
|
||||
{ key: 'R', label: 'Rotate counterclockwise' },
|
||||
{ key: 'T', label: 'Rotate clockwise' },
|
||||
{ key: 'Shift', label: 'Free place' },
|
||||
{ key: 'Shift', label: 'Cycle snapping mode' },
|
||||
{ key: 'Alt', label: 'Free place (no snap)' },
|
||||
{ key: 'Esc', label: 'Cancel' },
|
||||
],
|
||||
|
||||
|
||||
@@ -182,6 +182,7 @@ export function buildItemFloorplan(node: ItemNode, ctx: GeometryContext): Floorp
|
||||
})
|
||||
|
||||
const isSelected = ctx.viewState?.selected ?? false
|
||||
const isMoving = ctx.viewState?.moving ?? false
|
||||
const floorPlanUrl = node.asset.floorPlanUrl
|
||||
const children: FloorplanGeometry[] = [
|
||||
{
|
||||
@@ -214,8 +215,10 @@ export function buildItemFloorplan(node: ItemNode, ctx: GeometryContext): Floorp
|
||||
rotation: transform.rotation,
|
||||
})
|
||||
}
|
||||
// Move handle — orange dot at the item center. Only when selected.
|
||||
if (isSelected) {
|
||||
// Move handle — orange dot at the item center. Only when selected and not
|
||||
// already moving: during a move the dot sits under the cursor, so a release
|
||||
// over it would re-arm the move (and re-enter edit) instead of committing.
|
||||
if (isSelected && !isMoving) {
|
||||
children.push({
|
||||
kind: 'move-handle',
|
||||
point: [cx, cy],
|
||||
|
||||
Reference in New Issue
Block a user