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:
@@ -79,16 +79,17 @@ export const windowFloorplanMoveTarget: FloorplanMoveTarget<WindowNode> = ({ nod
|
||||
|
||||
// Figma-style along-wall alignment first (edge-to-edge with other
|
||||
// openings / wall ends), winning over the 0.5m grid snap; falls back
|
||||
// to grid when nothing aligns. Alt bypasses; Shift drops the grid snap.
|
||||
const neighborX = modifiers.altKey
|
||||
? null
|
||||
: snapLocalXToNeighbors({
|
||||
wall: hit.wall,
|
||||
localX: hit.localX,
|
||||
width: node.width,
|
||||
selfId: node.id as AnyNodeId,
|
||||
nodes,
|
||||
})
|
||||
// to grid when nothing aligns. Alt bypasses alignment; Shift bypasses all snap.
|
||||
const neighborX =
|
||||
modifiers.altKey || modifiers.shiftKey
|
||||
? null
|
||||
: snapLocalXToNeighbors({
|
||||
wall: hit.wall,
|
||||
localX: hit.localX,
|
||||
width: node.width,
|
||||
selfId: node.id as AnyNodeId,
|
||||
nodes,
|
||||
})
|
||||
const snappedLocalX = neighborX ?? (modifiers.shiftKey ? hit.localX : snapToHalf(hit.localX))
|
||||
const { clampedX, clampedY } = clampToWall(
|
||||
hit.wall,
|
||||
|
||||
@@ -187,23 +187,31 @@ const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWindowNode
|
||||
const rawLocalX = event.localPosition[0]
|
||||
const rawLocalY = event.localPosition[1]
|
||||
if (!dragAnchor || dragAnchor.wallId !== event.node.id) {
|
||||
const bypassSnap = event.nativeEvent?.shiftKey === true
|
||||
dragAnchor = {
|
||||
wallId: event.node.id,
|
||||
rawX: rawLocalX,
|
||||
rawY: rawLocalY,
|
||||
startX: event.node.id === original.parentId ? original.position[0] : rawLocalX,
|
||||
startY:
|
||||
event.node.id === original.parentId ? original.position[1] : snapToHalf(rawLocalY),
|
||||
event.node.id === original.parentId
|
||||
? original.position[1]
|
||||
: bypassSnap
|
||||
? rawLocalY
|
||||
: snapToHalf(rawLocalY),
|
||||
}
|
||||
}
|
||||
const targetLocalX = dragAnchor.startX + (rawLocalX - dragAnchor.rawX)
|
||||
const targetLocalY = snapToHalf(dragAnchor.startY + (rawLocalY - dragAnchor.rawY))
|
||||
const targetRawLocalY = dragAnchor.startY + (rawLocalY - dragAnchor.rawY)
|
||||
const targetLocalY =
|
||||
event.nativeEvent?.shiftKey === true ? targetRawLocalY : snapToHalf(targetRawLocalY)
|
||||
const localX = resolveWallSlideAlignment({
|
||||
wallNode: event.node,
|
||||
rawLocalX: targetLocalX,
|
||||
width: movingWindowNode.width,
|
||||
candidates: alignmentCandidates,
|
||||
bypass: event.nativeEvent?.altKey === true,
|
||||
bypass: event.nativeEvent?.altKey === true || event.nativeEvent?.shiftKey === true,
|
||||
bypassSnap: event.nativeEvent?.shiftKey === true,
|
||||
})
|
||||
const { clampedX, clampedY } = clampToWall(
|
||||
event.node,
|
||||
@@ -409,7 +417,10 @@ const MoveWindowTool: React.FC<{ node: WindowNode }> = ({ node: movingWindowNode
|
||||
width: movingWindowNode.width,
|
||||
height: movingWindowNode.height,
|
||||
ignoreId: movingWindowNode.id,
|
||||
vertical: { kind: 'free', snap: snapToHalf },
|
||||
vertical: {
|
||||
kind: 'free',
|
||||
snap: event.nativeEvent?.shiftKey === true ? undefined : snapToHalf,
|
||||
},
|
||||
})
|
||||
|
||||
const updateRoofCursor = (target: RoofWallOpeningTarget, roof: RoofNode) => {
|
||||
|
||||
@@ -128,9 +128,13 @@ const WindowTool: React.FC = () => {
|
||||
rawLocalX: event.localPosition[0],
|
||||
width,
|
||||
candidates: alignmentCandidates,
|
||||
bypass: event.nativeEvent?.altKey === true,
|
||||
bypass: event.nativeEvent?.altKey === true || event.nativeEvent?.shiftKey === true,
|
||||
bypassSnap: event.nativeEvent?.shiftKey === true,
|
||||
})
|
||||
const localY = snapToHalf(event.localPosition[1])
|
||||
const localY =
|
||||
event.nativeEvent?.shiftKey === true
|
||||
? event.localPosition[1]
|
||||
: snapToHalf(event.localPosition[1])
|
||||
|
||||
const { clampedX, clampedY } = clampToWall(event.node, localX, localY, width, height)
|
||||
|
||||
@@ -183,9 +187,13 @@ const WindowTool: React.FC = () => {
|
||||
rawLocalX: event.localPosition[0],
|
||||
width,
|
||||
candidates: alignmentCandidates,
|
||||
bypass: event.nativeEvent?.altKey === true,
|
||||
bypass: event.nativeEvent?.altKey === true || event.nativeEvent?.shiftKey === true,
|
||||
bypassSnap: event.nativeEvent?.shiftKey === true,
|
||||
})
|
||||
const localY = snapToHalf(event.localPosition[1])
|
||||
const localY =
|
||||
event.nativeEvent?.shiftKey === true
|
||||
? event.localPosition[1]
|
||||
: snapToHalf(event.localPosition[1])
|
||||
|
||||
const { clampedX, clampedY } = clampToWall(event.node, localX, localY, width, height)
|
||||
|
||||
@@ -277,9 +285,13 @@ const WindowTool: React.FC = () => {
|
||||
rawLocalX: event.localPosition[0],
|
||||
width: draftRef.current.width,
|
||||
candidates: alignmentCandidates,
|
||||
bypass: event.nativeEvent?.altKey === true,
|
||||
bypass: event.nativeEvent?.altKey === true || event.nativeEvent?.shiftKey === true,
|
||||
bypassSnap: event.nativeEvent?.shiftKey === true,
|
||||
})
|
||||
const localY = snapToHalf(event.localPosition[1])
|
||||
const localY =
|
||||
event.nativeEvent?.shiftKey === true
|
||||
? event.localPosition[1]
|
||||
: snapToHalf(event.localPosition[1])
|
||||
const { clampedX, clampedY } = clampToWall(
|
||||
event.node,
|
||||
localX,
|
||||
@@ -367,7 +379,10 @@ const WindowTool: React.FC = () => {
|
||||
width: draftRef.current?.width ?? 1.5,
|
||||
height: draftRef.current?.height ?? 1.5,
|
||||
ignoreId: draftRef.current?.id,
|
||||
vertical: { kind: 'free', snap: snapToHalf },
|
||||
vertical: {
|
||||
kind: 'free',
|
||||
snap: event.nativeEvent?.shiftKey === true ? undefined : snapToHalf,
|
||||
},
|
||||
})
|
||||
|
||||
const updateRoofCursor = (target: RoofWallOpeningTarget, roof: RoofNode) => {
|
||||
|
||||
Reference in New Issue
Block a user