feat(editor): MEP move-tools — mode-driven snapping (drop Shift=bypass)
The 5 bespoke MEP movers (duct/pipe-segment, liquid-line, lineset, duct-fitting)
now read the active snapping mode (isGridSnapActive / isMagneticSnapActive)
instead of shiftKey=bypass. The moving scope already carries the node
(setMovingNode → begin('moving')), so the per-kind context resolves with no
extra wiring. Grid and alignment are now independent reads.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
2fdbcf03c3
commit
8e9a45a9d5
@@ -13,6 +13,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
DragBoundingBox,
|
DragBoundingBox,
|
||||||
EDITOR_LAYER,
|
EDITOR_LAYER,
|
||||||
|
isGridSnapActive,
|
||||||
|
isMagneticSnapActive,
|
||||||
markToolCancelConsumed,
|
markToolCancelConsumed,
|
||||||
stripPlacementMetadataFlags,
|
stripPlacementMetadataFlags,
|
||||||
triggerSFX,
|
triggerSFX,
|
||||||
@@ -177,14 +179,14 @@ export const MoveDuctFittingTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
let lastPos: Vec3 = originalPosition
|
let lastPos: Vec3 = originalPosition
|
||||||
|
|
||||||
const onMove = (event: GridEvent) => {
|
const onMove = (event: GridEvent) => {
|
||||||
const bypass = event.nativeEvent?.shiftKey === true
|
const snap = isGridSnapActive() ? snapToGridStep : (v: number) => v
|
||||||
const snap = bypass ? (v: number) => v : snapToGridStep
|
|
||||||
let x = snap(event.localPosition[0])
|
let x = snap(event.localPosition[0])
|
||||||
let z = snap(event.localPosition[2])
|
let z = snap(event.localPosition[2])
|
||||||
|
|
||||||
// Alignment: snap the footprint box edges onto nearby geometry and
|
// Magnetic alignment: snap the footprint box edges onto nearby geometry
|
||||||
// publish guides (Alt / Shift bypass).
|
// and publish guides. Grid follows the snapping mode; lines follow
|
||||||
if (!bypass) {
|
// magnetic alignment — the two are independent.
|
||||||
|
if (isMagneticSnapActive()) {
|
||||||
const proposed: Aabb2D = {
|
const proposed: Aabb2D = {
|
||||||
minX: x + ox - hx,
|
minX: x + ox - hx,
|
||||||
maxX: x + ox + hx,
|
maxX: x + ox + hx,
|
||||||
@@ -200,7 +202,11 @@ export const MoveDuctFittingTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const next: Vec3 = [x, originalPosition[1], z]
|
const next: Vec3 = [x, originalPosition[1], z]
|
||||||
if (next[0] !== lastPos[0] || next[2] !== lastPos[2]) triggerSFX('sfx:grid-snap')
|
if (
|
||||||
|
(isGridSnapActive() || isMagneticSnapActive()) &&
|
||||||
|
(next[0] !== lastPos[0] || next[2] !== lastPos[2])
|
||||||
|
)
|
||||||
|
triggerSFX('sfx:grid-snap')
|
||||||
lastPos = next
|
lastPos = next
|
||||||
hasMoved = true
|
hasMoved = true
|
||||||
setCursorPos(next)
|
setCursorPos(next)
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
DragBoundingBox,
|
DragBoundingBox,
|
||||||
EDITOR_LAYER,
|
EDITOR_LAYER,
|
||||||
|
isGridSnapActive,
|
||||||
|
isMagneticSnapActive,
|
||||||
markToolCancelConsumed,
|
markToolCancelConsumed,
|
||||||
stripPlacementMetadataFlags,
|
stripPlacementMetadataFlags,
|
||||||
triggerSFX,
|
triggerSFX,
|
||||||
@@ -144,14 +146,14 @@ export const MoveDuctSegmentTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onMove = (event: GridEvent) => {
|
const onMove = (event: GridEvent) => {
|
||||||
const bypass = event.nativeEvent?.shiftKey === true
|
const snap = isGridSnapActive() ? snapToGridStep : (v: number) => v
|
||||||
const snap = bypass ? (v: number) => v : snapToGridStep
|
|
||||||
let dx = snap(event.localPosition[0] - centerX)
|
let dx = snap(event.localPosition[0] - centerX)
|
||||||
let dz = snap(event.localPosition[2] - centerZ)
|
let dz = snap(event.localPosition[2] - centerZ)
|
||||||
|
|
||||||
// Figma-style alignment: snap the run's footprint box edges onto
|
// Figma-style magnetic alignment: snap the run's footprint box edges onto
|
||||||
// nearby geometry and publish the guides (Alt / Shift bypass).
|
// nearby geometry and publish the guides. Grid follows the snapping mode;
|
||||||
if (!bypass) {
|
// lines follow magnetic alignment — the two are independent.
|
||||||
|
if (isMagneticSnapActive()) {
|
||||||
const proposed: Aabb2D = {
|
const proposed: Aabb2D = {
|
||||||
minX: baseAabb.minX + dx,
|
minX: baseAabb.minX + dx,
|
||||||
maxX: baseAabb.maxX + dx,
|
maxX: baseAabb.maxX + dx,
|
||||||
@@ -168,7 +170,7 @@ export const MoveDuctSegmentTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
|
|
||||||
const cur: [number, number] = [centerX + dx, centerZ + dz]
|
const cur: [number, number] = [centerX + dx, centerZ + dz]
|
||||||
if (
|
if (
|
||||||
!bypass &&
|
(isGridSnapActive() || isMagneticSnapActive()) &&
|
||||||
(!prevSnapRef.current ||
|
(!prevSnapRef.current ||
|
||||||
prevSnapRef.current[0] !== cur[0] ||
|
prevSnapRef.current[0] !== cur[0] ||
|
||||||
prevSnapRef.current[1] !== cur[1])
|
prevSnapRef.current[1] !== cur[1])
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
DragBoundingBox,
|
DragBoundingBox,
|
||||||
EDITOR_LAYER,
|
EDITOR_LAYER,
|
||||||
|
isGridSnapActive,
|
||||||
|
isMagneticSnapActive,
|
||||||
markToolCancelConsumed,
|
markToolCancelConsumed,
|
||||||
stripPlacementMetadataFlags,
|
stripPlacementMetadataFlags,
|
||||||
triggerSFX,
|
triggerSFX,
|
||||||
@@ -143,14 +145,14 @@ export const MoveLinesetTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onMove = (event: GridEvent) => {
|
const onMove = (event: GridEvent) => {
|
||||||
const bypass = event.nativeEvent?.shiftKey === true
|
const snap = isGridSnapActive() ? snapToGridStep : (v: number) => v
|
||||||
const snap = bypass ? (v: number) => v : snapToGridStep
|
|
||||||
let dx = snap(event.localPosition[0] - centerX)
|
let dx = snap(event.localPosition[0] - centerX)
|
||||||
let dz = snap(event.localPosition[2] - centerZ)
|
let dz = snap(event.localPosition[2] - centerZ)
|
||||||
|
|
||||||
// Figma-style alignment: snap the run's footprint box edges onto
|
// Figma-style magnetic alignment: snap the run's footprint box edges onto
|
||||||
// nearby geometry and publish the guides (Shift bypass).
|
// nearby geometry and publish the guides. Grid follows the snapping mode;
|
||||||
if (!bypass) {
|
// lines follow magnetic alignment — the two are independent.
|
||||||
|
if (isMagneticSnapActive()) {
|
||||||
const proposed: Aabb2D = {
|
const proposed: Aabb2D = {
|
||||||
minX: baseAabb.minX + dx,
|
minX: baseAabb.minX + dx,
|
||||||
maxX: baseAabb.maxX + dx,
|
maxX: baseAabb.maxX + dx,
|
||||||
@@ -167,7 +169,7 @@ export const MoveLinesetTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
|
|
||||||
const cur: [number, number] = [centerX + dx, centerZ + dz]
|
const cur: [number, number] = [centerX + dx, centerZ + dz]
|
||||||
if (
|
if (
|
||||||
!bypass &&
|
(isGridSnapActive() || isMagneticSnapActive()) &&
|
||||||
(!prevSnapRef.current ||
|
(!prevSnapRef.current ||
|
||||||
prevSnapRef.current[0] !== cur[0] ||
|
prevSnapRef.current[0] !== cur[0] ||
|
||||||
prevSnapRef.current[1] !== cur[1])
|
prevSnapRef.current[1] !== cur[1])
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
DragBoundingBox,
|
DragBoundingBox,
|
||||||
EDITOR_LAYER,
|
EDITOR_LAYER,
|
||||||
|
isGridSnapActive,
|
||||||
|
isMagneticSnapActive,
|
||||||
markToolCancelConsumed,
|
markToolCancelConsumed,
|
||||||
stripPlacementMetadataFlags,
|
stripPlacementMetadataFlags,
|
||||||
triggerSFX,
|
triggerSFX,
|
||||||
@@ -139,14 +141,14 @@ export const MoveLiquidLineTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onMove = (event: GridEvent) => {
|
const onMove = (event: GridEvent) => {
|
||||||
const bypass = event.nativeEvent?.shiftKey === true
|
const snap = isGridSnapActive() ? snapToGridStep : (v: number) => v
|
||||||
const snap = bypass ? (v: number) => v : snapToGridStep
|
|
||||||
let dx = snap(event.localPosition[0] - centerX)
|
let dx = snap(event.localPosition[0] - centerX)
|
||||||
let dz = snap(event.localPosition[2] - centerZ)
|
let dz = snap(event.localPosition[2] - centerZ)
|
||||||
|
|
||||||
// Figma-style alignment: snap the run's footprint box edges onto nearby
|
// Figma-style magnetic alignment: snap the run's footprint box edges onto
|
||||||
// geometry and publish the guides (Shift bypass).
|
// nearby geometry and publish the guides. Grid follows the snapping mode;
|
||||||
if (!bypass) {
|
// lines follow magnetic alignment — the two are independent.
|
||||||
|
if (isMagneticSnapActive()) {
|
||||||
const proposed: Aabb2D = {
|
const proposed: Aabb2D = {
|
||||||
minX: baseAabb.minX + dx,
|
minX: baseAabb.minX + dx,
|
||||||
maxX: baseAabb.maxX + dx,
|
maxX: baseAabb.maxX + dx,
|
||||||
@@ -163,7 +165,7 @@ export const MoveLiquidLineTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
|
|
||||||
const cur: [number, number] = [centerX + dx, centerZ + dz]
|
const cur: [number, number] = [centerX + dx, centerZ + dz]
|
||||||
if (
|
if (
|
||||||
!bypass &&
|
(isGridSnapActive() || isMagneticSnapActive()) &&
|
||||||
(!prevSnapRef.current ||
|
(!prevSnapRef.current ||
|
||||||
prevSnapRef.current[0] !== cur[0] ||
|
prevSnapRef.current[0] !== cur[0] ||
|
||||||
prevSnapRef.current[1] !== cur[1])
|
prevSnapRef.current[1] !== cur[1])
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
DragBoundingBox,
|
DragBoundingBox,
|
||||||
EDITOR_LAYER,
|
EDITOR_LAYER,
|
||||||
|
isGridSnapActive,
|
||||||
|
isMagneticSnapActive,
|
||||||
markToolCancelConsumed,
|
markToolCancelConsumed,
|
||||||
stripPlacementMetadataFlags,
|
stripPlacementMetadataFlags,
|
||||||
triggerSFX,
|
triggerSFX,
|
||||||
@@ -141,14 +143,14 @@ export const MovePipeSegmentTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onMove = (event: GridEvent) => {
|
const onMove = (event: GridEvent) => {
|
||||||
const bypass = event.nativeEvent?.shiftKey === true
|
const snap = isGridSnapActive() ? snapToGridStep : (v: number) => v
|
||||||
const snap = bypass ? (v: number) => v : snapToGridStep
|
|
||||||
let dx = snap(event.localPosition[0] - centerX)
|
let dx = snap(event.localPosition[0] - centerX)
|
||||||
let dz = snap(event.localPosition[2] - centerZ)
|
let dz = snap(event.localPosition[2] - centerZ)
|
||||||
|
|
||||||
// Figma-style alignment: snap the run's footprint box edges onto
|
// Figma-style magnetic alignment: snap the run's footprint box edges onto
|
||||||
// nearby geometry and publish the guides (Shift bypass).
|
// nearby geometry and publish the guides. Grid follows the snapping mode;
|
||||||
if (!bypass) {
|
// lines follow magnetic alignment — the two are independent.
|
||||||
|
if (isMagneticSnapActive()) {
|
||||||
const proposed: Aabb2D = {
|
const proposed: Aabb2D = {
|
||||||
minX: baseAabb.minX + dx,
|
minX: baseAabb.minX + dx,
|
||||||
maxX: baseAabb.maxX + dx,
|
maxX: baseAabb.maxX + dx,
|
||||||
@@ -165,7 +167,7 @@ export const MovePipeSegmentTool: React.FC<{ node: AnyNode }> = ({ node }) => {
|
|||||||
|
|
||||||
const cur: [number, number] = [centerX + dx, centerZ + dz]
|
const cur: [number, number] = [centerX + dx, centerZ + dz]
|
||||||
if (
|
if (
|
||||||
!bypass &&
|
(isGridSnapActive() || isMagneticSnapActive()) &&
|
||||||
(!prevSnapRef.current ||
|
(!prevSnapRef.current ||
|
||||||
prevSnapRef.current[0] !== cur[0] ||
|
prevSnapRef.current[0] !== cur[0] ||
|
||||||
prevSnapRef.current[1] !== cur[1])
|
prevSnapRef.current[1] !== cur[1])
|
||||||
|
|||||||
Reference in New Issue
Block a user