fix(editor): 2D slab/zone/ceiling drafting honors the active snapping mode
The polygon-draft snap path used the legacy model: bypassSnap = shiftPressed and angleSnap = pointCount > 0 && !bypassSnap, so the 15deg angle lock engaged after the first vertex regardless of mode — hijacking grid/lines/off into angle-snap even though the HUD chip showed the right mode. Migrate all three placement paths (move preview, single-click vertex, double- click close) to the unified model: angleSnap = isAngleSnapActive(); grid flows through snapToHalf (step 0 in non-grid modes); wall-snap/alignment already gates on isMagneticSnapActive(). Behavior now matches the chip — grid quantizes, angles locks 15deg rays, lines snaps to walls/alignment, off is free. Drop the now-dead bypassSnap param from snapPolygonDraftPoint. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a769b45756
commit
bc9e075b2d
@@ -2167,15 +2167,14 @@ function snapPolygonDraftPoint({
|
|||||||
point,
|
point,
|
||||||
start,
|
start,
|
||||||
angleSnap,
|
angleSnap,
|
||||||
bypassSnap,
|
|
||||||
}: {
|
}: {
|
||||||
point: WallPlanPoint
|
point: WallPlanPoint
|
||||||
start?: WallPlanPoint
|
start?: WallPlanPoint
|
||||||
angleSnap: boolean
|
angleSnap: boolean
|
||||||
bypassSnap?: boolean
|
|
||||||
}): WallPlanPoint {
|
}): WallPlanPoint {
|
||||||
if (bypassSnap) return point
|
// `snapToHalf`'s default step is 0 in any non-`grid` mode, so the grid branch
|
||||||
|
// passes the raw point through for `lines` / `off` (where wall-snap /
|
||||||
|
// alignment, run by the caller, takes over) — no explicit bypass needed.
|
||||||
if (!(start && angleSnap)) {
|
if (!(start && angleSnap)) {
|
||||||
return [snapToHalf(point[0]), snapToHalf(point[1])]
|
return [snapToHalf(point[0]), snapToHalf(point[1])]
|
||||||
}
|
}
|
||||||
@@ -8624,24 +8623,23 @@ export function FloorplanPanel({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isCeilingBuildActive) {
|
if (isCeilingBuildActive) {
|
||||||
const bypassSnap = shiftPressed || event.shiftKey
|
// Polygon vertex snapping is governed by the active snapping mode (the
|
||||||
// Polygon vertex: grid (snapToHalf) or 15° angle snap from the
|
// chip on the right): `grid` quantizes via `snapToHalf` (whose step is
|
||||||
// previous vertex. Wall magnetic snap may still win, while
|
// 0 — i.e. off — in any non-grid mode), `angles` locks to 15° rays from
|
||||||
// generic alignment runs only when angle snap is OFF (first vertex,
|
// the previous vertex, `lines` pulls onto wall corners / alignment
|
||||||
// or Shift held) so it does not pull a locked angle sideways.
|
// guides, `off` is free. No Shift hold-to-bypass; Alt forces (skips
|
||||||
const angleSnap = ceilingDraftPoints.length > 0 && !bypassSnap
|
// alignment).
|
||||||
|
const angleSnap = ceilingDraftPoints.length > 0 && isAngleSnapActive()
|
||||||
const fallbackPoint = snapPolygonDraftPoint({
|
const fallbackPoint = snapPolygonDraftPoint({
|
||||||
point: planPoint,
|
point: planPoint,
|
||||||
start: ceilingDraftPoints[ceilingDraftPoints.length - 1],
|
start: ceilingDraftPoints[ceilingDraftPoints.length - 1],
|
||||||
angleSnap,
|
angleSnap,
|
||||||
bypassSnap,
|
|
||||||
})
|
})
|
||||||
const snappedPoint = resolveCeilingPlanPointSnap({
|
const snappedPoint = resolveCeilingPlanPointSnap({
|
||||||
rawPoint: planPoint,
|
rawPoint: planPoint,
|
||||||
fallbackPoint,
|
fallbackPoint,
|
||||||
levelId,
|
levelId,
|
||||||
altKey: event.altKey,
|
altKey: event.altKey,
|
||||||
shiftKey: bypassSnap,
|
|
||||||
align: !angleSnap,
|
align: !angleSnap,
|
||||||
}).point
|
}).point
|
||||||
|
|
||||||
@@ -8719,13 +8717,15 @@ export function FloorplanPanel({
|
|||||||
// the local polygon-draft state actually updates as the cursor
|
// the local polygon-draft state actually updates as the cursor
|
||||||
// moves (the catch-all would otherwise swallow the move event).
|
// moves (the catch-all would otherwise swallow the move event).
|
||||||
if (isPolygonBuildActive) {
|
if (isPolygonBuildActive) {
|
||||||
const bypassSnap = shiftPressed || event.shiftKey
|
// Mode-driven (matches the chip): `grid` quantizes (`snapToHalf`'s step
|
||||||
const angleSnap = activePolygonDraftPoints.length > 0 && !bypassSnap
|
// is 0 in non-grid modes), `angles` locks 15° rays from the previous
|
||||||
|
// vertex, `lines` snaps onto wall corners / alignment guides, `off` is
|
||||||
|
// free. No Shift bypass; Alt forces (skips alignment).
|
||||||
|
const angleSnap = activePolygonDraftPoints.length > 0 && isAngleSnapActive()
|
||||||
const fallbackPoint = snapPolygonDraftPoint({
|
const fallbackPoint = snapPolygonDraftPoint({
|
||||||
point: planPoint,
|
point: planPoint,
|
||||||
start: activePolygonDraftPoints[activePolygonDraftPoints.length - 1],
|
start: activePolygonDraftPoints[activePolygonDraftPoints.length - 1],
|
||||||
angleSnap,
|
angleSnap,
|
||||||
bypassSnap,
|
|
||||||
})
|
})
|
||||||
let snappedPoint = fallbackPoint
|
let snappedPoint = fallbackPoint
|
||||||
if (isSlabBuildActive) {
|
if (isSlabBuildActive) {
|
||||||
@@ -8734,14 +8734,13 @@ export function FloorplanPanel({
|
|||||||
fallbackPoint,
|
fallbackPoint,
|
||||||
levelId,
|
levelId,
|
||||||
altKey: event.altKey,
|
altKey: event.altKey,
|
||||||
shiftKey: bypassSnap,
|
|
||||||
align: !angleSnap,
|
align: !angleSnap,
|
||||||
}).point
|
}).point
|
||||||
} else if (angleSnap) {
|
} else if (angleSnap) {
|
||||||
useAlignmentGuides.getState().clear()
|
useAlignmentGuides.getState().clear()
|
||||||
} else {
|
} else {
|
||||||
snappedPoint = alignFloorplanDraftPoint(fallbackPoint, {
|
snappedPoint = alignFloorplanDraftPoint(fallbackPoint, {
|
||||||
bypass: event.altKey || bypassSnap,
|
bypass: event.altKey || !isMagneticSnapActive(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8751,7 +8750,7 @@ export function FloorplanPanel({
|
|||||||
|
|
||||||
setCursorPoint((previousPoint) => {
|
setCursorPoint((previousPoint) => {
|
||||||
const hasChanged = !(previousPoint && pointsEqual(previousPoint, snappedPoint))
|
const hasChanged = !(previousPoint && pointsEqual(previousPoint, snappedPoint))
|
||||||
if (!bypassSnap && hasChanged && activePolygonDraftPoints.length > 0) {
|
if (hasChanged && activePolygonDraftPoints.length > 0) {
|
||||||
sfxEmitter.emit('sfx:grid-snap')
|
sfxEmitter.emit('sfx:grid-snap')
|
||||||
}
|
}
|
||||||
return snappedPoint
|
return snappedPoint
|
||||||
@@ -9396,13 +9395,11 @@ export function FloorplanPanel({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const bypassSnap = shiftPressed || event.shiftKey
|
const angleSnap = activePolygonDraftPoints.length > 0 && isAngleSnapActive()
|
||||||
const angleSnap = activePolygonDraftPoints.length > 0 && !bypassSnap
|
|
||||||
const fallbackPoint = snapPolygonDraftPoint({
|
const fallbackPoint = snapPolygonDraftPoint({
|
||||||
point: planPoint,
|
point: planPoint,
|
||||||
start: activePolygonDraftPoints[activePolygonDraftPoints.length - 1],
|
start: activePolygonDraftPoints[activePolygonDraftPoints.length - 1],
|
||||||
angleSnap,
|
angleSnap,
|
||||||
bypassSnap,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isCeilingBuildActive) {
|
if (isCeilingBuildActive) {
|
||||||
@@ -9411,7 +9408,6 @@ export function FloorplanPanel({
|
|||||||
fallbackPoint,
|
fallbackPoint,
|
||||||
levelId,
|
levelId,
|
||||||
altKey: event.altKey,
|
altKey: event.altKey,
|
||||||
shiftKey: bypassSnap,
|
|
||||||
align: !angleSnap,
|
align: !angleSnap,
|
||||||
}).point
|
}).point
|
||||||
emitFloorplanGridEvent('double-click', snappedPoint, event)
|
emitFloorplanGridEvent('double-click', snappedPoint, event)
|
||||||
@@ -9427,7 +9423,6 @@ export function FloorplanPanel({
|
|||||||
fallbackPoint,
|
fallbackPoint,
|
||||||
levelId,
|
levelId,
|
||||||
altKey: event.altKey,
|
altKey: event.altKey,
|
||||||
shiftKey: bypassSnap,
|
|
||||||
align: !angleSnap,
|
align: !angleSnap,
|
||||||
}).point
|
}).point
|
||||||
// Slab is registry-driven: forward the double-click so the 3D tool
|
// Slab is registry-driven: forward the double-click so the 3D tool
|
||||||
@@ -9448,7 +9443,6 @@ export function FloorplanPanel({
|
|||||||
isRoofBuildActive,
|
isRoofBuildActive,
|
||||||
isZoneBuildActive,
|
isZoneBuildActive,
|
||||||
levelId,
|
levelId,
|
||||||
shiftPressed,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ type UseFloorplanBackgroundPlacementArgs = {
|
|||||||
point: WallPlanPoint
|
point: WallPlanPoint
|
||||||
start?: WallPlanPoint
|
start?: WallPlanPoint
|
||||||
angleSnap: boolean
|
angleSnap: boolean
|
||||||
bypassSnap?: boolean
|
|
||||||
}) => WallPlanPoint
|
}) => WallPlanPoint
|
||||||
toPoint2D: (point: WallPlanPoint) => { x: number; y: number }
|
toPoint2D: (point: WallPlanPoint) => { x: number; y: number }
|
||||||
walls: WallNode[]
|
walls: WallNode[]
|
||||||
@@ -161,24 +160,21 @@ export function useFloorplanBackgroundPlacement({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isCeilingBuildActive) {
|
if (isCeilingBuildActive) {
|
||||||
const bypassSnap = shiftPressed || event.shiftKey
|
// Align the committed vertex the same way the move-preview did, so the
|
||||||
// Align the committed vertex the same way the move-preview did, so
|
// placed point matches what the user saw — mode-driven (the chip):
|
||||||
// the placed point matches what the user saw. Wall magnetic snap may
|
// `grid` quantizes, `angles` locks 15° rays, `lines` snaps onto walls /
|
||||||
// still win; generic alignment is skipped when angle snap owns the
|
// alignment, `off` is free. Alt forces (skips alignment).
|
||||||
// vertex (matches the move branch).
|
const angleSnap = ceilingDraftPoints.length > 0 && isAngleSnapActive()
|
||||||
const angleSnap = ceilingDraftPoints.length > 0 && !bypassSnap
|
|
||||||
const fallbackPoint = snapPolygonDraftPoint({
|
const fallbackPoint = snapPolygonDraftPoint({
|
||||||
point: planPoint,
|
point: planPoint,
|
||||||
start: ceilingDraftPoints[ceilingDraftPoints.length - 1],
|
start: ceilingDraftPoints[ceilingDraftPoints.length - 1],
|
||||||
angleSnap,
|
angleSnap,
|
||||||
bypassSnap,
|
|
||||||
})
|
})
|
||||||
const snappedPoint = resolveCeilingPlanPointSnap({
|
const snappedPoint = resolveCeilingPlanPointSnap({
|
||||||
rawPoint: planPoint,
|
rawPoint: planPoint,
|
||||||
fallbackPoint,
|
fallbackPoint,
|
||||||
levelId,
|
levelId,
|
||||||
altKey: event.altKey,
|
altKey: event.altKey,
|
||||||
shiftKey: bypassSnap,
|
|
||||||
align: !angleSnap,
|
align: !angleSnap,
|
||||||
}).point
|
}).point
|
||||||
|
|
||||||
@@ -272,13 +268,11 @@ export function useFloorplanBackgroundPlacement({
|
|||||||
// swallow the click and skip local draft state updates — leaving
|
// swallow the click and skip local draft state updates — leaving
|
||||||
// the 2D draft polygon invisible while the 3D tool builds fine).
|
// the 2D draft polygon invisible while the 3D tool builds fine).
|
||||||
if (isPolygonBuildActive) {
|
if (isPolygonBuildActive) {
|
||||||
const bypassSnap = shiftPressed || event.shiftKey
|
const angleSnap = activePolygonDraftPoints.length > 0 && isAngleSnapActive()
|
||||||
const angleSnap = activePolygonDraftPoints.length > 0 && !bypassSnap
|
|
||||||
const fallbackPoint = snapPolygonDraftPoint({
|
const fallbackPoint = snapPolygonDraftPoint({
|
||||||
point: planPoint,
|
point: planPoint,
|
||||||
start: activePolygonDraftPoints[activePolygonDraftPoints.length - 1],
|
start: activePolygonDraftPoints[activePolygonDraftPoints.length - 1],
|
||||||
angleSnap,
|
angleSnap,
|
||||||
bypassSnap,
|
|
||||||
})
|
})
|
||||||
let snappedPoint = fallbackPoint
|
let snappedPoint = fallbackPoint
|
||||||
if (isSlabBuildActive) {
|
if (isSlabBuildActive) {
|
||||||
@@ -287,12 +281,11 @@ export function useFloorplanBackgroundPlacement({
|
|||||||
fallbackPoint,
|
fallbackPoint,
|
||||||
levelId,
|
levelId,
|
||||||
altKey: event.altKey,
|
altKey: event.altKey,
|
||||||
shiftKey: bypassSnap,
|
|
||||||
align: !angleSnap,
|
align: !angleSnap,
|
||||||
}).point
|
}).point
|
||||||
} else if (!angleSnap) {
|
} else if (!angleSnap) {
|
||||||
snappedPoint = alignFloorplanDraftPoint(fallbackPoint, {
|
snappedPoint = alignFloorplanDraftPoint(fallbackPoint, {
|
||||||
bypass: event.altKey || bypassSnap,
|
bypass: event.altKey || !isMagneticSnapActive(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user