fix(editor): harden editor interactions and WebGPU rendering
Fix editor bug sweep regressions, WebGPU CSG/material crashes, Shift snap bypass behavior, arrow handle drag projection, and the wall preview null guard covered by the Sentry follow-up PRs.
This commit is contained in:
@@ -63,7 +63,7 @@ export const columnFloorplanMoveTarget: FloorplanMoveTarget<ColumnNode> = ({ nod
|
||||
return Math.round(value / step) * step
|
||||
}
|
||||
const gridSnapped = resolveCursor(planPoint, { snap }) as WallPlanPoint
|
||||
// Figma-style alignment layered on the grid snap (Alt bypasses).
|
||||
// Figma-style alignment layered on the grid snap (Alt bypasses alignment; Shift all snap).
|
||||
const { point: snapped } = applyFloorplanAlignment(
|
||||
gridSnapped,
|
||||
movingFootprintAnchors(
|
||||
@@ -73,13 +73,13 @@ export const columnFloorplanMoveTarget: FloorplanMoveTarget<ColumnNode> = ({ nod
|
||||
rotationY,
|
||||
),
|
||||
candidates,
|
||||
{ bypass: modifiers.altKey },
|
||||
{ bypass: modifiers.altKey || modifiers.shiftKey },
|
||||
)
|
||||
const next: [number, number, number] = [snapped[0], originalPosition[1], snapped[1]]
|
||||
lastPosition = next
|
||||
|
||||
const snapKey = `${snapped[0]},${snapped[1]}`
|
||||
if (snapKey !== lastSnapKey) {
|
||||
if (!modifiers.shiftKey && snapKey !== lastSnapKey) {
|
||||
triggerSFX('sfx:grid-snap')
|
||||
lastSnapKey = snapKey
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ const snapToGridStep = (value: number) => {
|
||||
return Math.round(value / step) * step
|
||||
}
|
||||
|
||||
/** 90° steps, matching the GLB item / shelf placement rotation. */
|
||||
const ROTATION_STEP = Math.PI / 2
|
||||
/** 45° steps, matching the generic move tool's R/T rotation. */
|
||||
const ROTATION_STEP = Math.PI / 4
|
||||
|
||||
/** Figma-style alignment-snap threshold (meters), matching the other tools. */
|
||||
const ALIGNMENT_THRESHOLD_M = 0.08
|
||||
@@ -124,15 +124,15 @@ function MoveColumnTool({ node }: { node: ColumnNode }) {
|
||||
original: [node.position[0], node.position[2]],
|
||||
anchor: dragAnchor,
|
||||
mode: useAbsoluteCursorPlacement ? 'absolute' : 'relative',
|
||||
snap: snapToGridStep,
|
||||
snap: event.nativeEvent?.shiftKey === true ? (value) => value : snapToGridStep,
|
||||
})
|
||||
dragAnchor = resolved.anchor
|
||||
let [x, z] = resolved.point
|
||||
|
||||
// Figma-style alignment snap on top of grid snap; Alt bypasses. The
|
||||
// Figma-style alignment snap on top of grid snap; Alt bypasses alignment; Shift all snap. The
|
||||
// guide connects to the candidate's nearest real anchor (resolver
|
||||
// tie-break), so the dot always sits on an actual point.
|
||||
const bypass = event.nativeEvent?.altKey === true
|
||||
const bypass = event.nativeEvent?.altKey === true || event.nativeEvent?.shiftKey === true
|
||||
if (!bypass && alignmentCandidates.length > 0) {
|
||||
const result = resolveAlignment({
|
||||
moving: movingFootprintAnchors(node, x, z, rotationY),
|
||||
@@ -151,7 +151,7 @@ function MoveColumnTool({ node }: { node: ColumnNode }) {
|
||||
applyPreview([x, 0, z])
|
||||
}
|
||||
|
||||
// R / T rotate the dragged column about Y in 90° steps (matches the move
|
||||
// R / T rotate the dragged column about Y in 45° steps (matches the move
|
||||
// HUD's "Rotate" hints), committed on drop.
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.metaKey || e.ctrlKey || e.altKey) return
|
||||
|
||||
@@ -87,7 +87,8 @@ const ColumnTool = () => {
|
||||
rawZ: event.localPosition[2],
|
||||
gridStep: useEditor.getState().gridSnapStep,
|
||||
candidates: alignmentCandidates,
|
||||
bypassAlignment: event.nativeEvent?.altKey === true,
|
||||
bypassAlignment: event.nativeEvent?.altKey === true || event.nativeEvent?.shiftKey === true,
|
||||
bypassGrid: event.nativeEvent?.shiftKey === true,
|
||||
})
|
||||
useAlignmentGuides.getState().set(guides)
|
||||
|
||||
@@ -107,7 +108,10 @@ const ColumnTool = () => {
|
||||
usePlacementPreview.getState().set({ ...previewNode, position })
|
||||
|
||||
const prev = previousSnapRef.current
|
||||
if (!prev || prev[0] !== position[0] || prev[1] !== position[2]) {
|
||||
if (
|
||||
event.nativeEvent?.shiftKey !== true &&
|
||||
(!prev || prev[0] !== position[0] || prev[1] !== position[2])
|
||||
) {
|
||||
triggerSFX('sfx:grid-snap')
|
||||
previousSnapRef.current = [position[0], position[2]]
|
||||
}
|
||||
@@ -116,7 +120,12 @@ const ColumnTool = () => {
|
||||
const commitAtCursor = (event: FloorPlacementClickTriggerEvent) => {
|
||||
const position =
|
||||
lastCursorRef.current ??
|
||||
getLevelLocalSnappedPosition(activeLevelId, event, useEditor.getState().gridSnapStep)
|
||||
getLevelLocalSnappedPosition(
|
||||
activeLevelId,
|
||||
event,
|
||||
useEditor.getState().gridSnapStep,
|
||||
event.nativeEvent?.shiftKey === true,
|
||||
)
|
||||
|
||||
const column = createColumnFromPreset(DEFAULT_COLUMN_PRESET_ID, position)
|
||||
useScene.getState().createNode(column, activeLevelId)
|
||||
|
||||
Reference in New Issue
Block a user