feat(editor): MEP placement migration — Shift=cycle / mode-driven snapping
Migrate all 9 MEP kinds' placement tools onto the unified snapping model:
declare snapProfile ('item' for point-placed hvac-equipment / duct-terminal /
duct-fitting / pipe-fitting / pipe-trap; 'structural' for directional runs
duct-segment / pipe-segment / liquid-line / lineset), and replace the legacy
shiftKey-bypass reads with mode-driven isGridSnapActive / isMagneticSnapActive /
isAngleSnapActive. For runs the 45° lock becomes the cyclable 'angles' mode;
Alt stays the vertical-riser modifier (run drafting has no validity gate to
force). Port mating gated on "mode != off". Dropped stale "⇧ smooth/free" hints.
The bespoke MEP move-tool/selection (endpoint) tools stay on the legacy model —
they use setMovingNode(null) so no moving-scope context resolves yet; migrating
them needs scope-wiring first (follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
76096ffe72
commit
2fdbcf03c3
@@ -20,6 +20,7 @@ export const hvacEquipmentDefinition: NodeDefinition<typeof HvacEquipmentNode> =
|
||||
schema: HvacEquipmentNode,
|
||||
category: 'utility',
|
||||
distributionRole: 'equipment',
|
||||
snapProfile: 'item',
|
||||
|
||||
defaults: () => ({
|
||||
object: 'node',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { emitter, type GridEvent, HvacEquipmentNode, useScene } from '@pascal-app/core'
|
||||
import { triggerSFX, useEditor } from '@pascal-app/editor'
|
||||
import { isGridSnapActive, isMagneticSnapActive, triggerSFX, useEditor } from '@pascal-app/editor'
|
||||
import { useViewer } from '@pascal-app/viewer'
|
||||
import { Html } from '@react-three/drei'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
@@ -53,17 +53,18 @@ const HvacEquipmentTool = () => {
|
||||
if (!activeLevelId) return
|
||||
|
||||
const resolve = (event: GridEvent): [number, number, number] => {
|
||||
const step = event.nativeEvent?.shiftKey === true ? 0 : useEditor.getState().gridSnapStep
|
||||
const step = isGridSnapActive() ? useEditor.getState().gridSnapStep : 0
|
||||
return [snap(event.localPosition[0], step), 0, snap(event.localPosition[2], step)]
|
||||
}
|
||||
|
||||
// Grid-snap the cursor, then layer Figma-style alignment so the unit lines
|
||||
// up with ducts, other equipment, and items as it's placed (Shift = free,
|
||||
// no snap + no guides).
|
||||
// up with ducts, other equipment, and items as it's placed. Grid + lines
|
||||
// follow the active snapping mode (the contextual HUD chip — Shift cycles
|
||||
// it); `'off'` is the no-snap bypass.
|
||||
const resolveAligned = (event: GridEvent): [number, number, number] =>
|
||||
alignDrawPoint(resolve(event), {
|
||||
applySnap: true,
|
||||
bypass: event.nativeEvent?.shiftKey === true,
|
||||
bypass: !isMagneticSnapActive(),
|
||||
})
|
||||
|
||||
const onMove = (event: GridEvent) => setCursor(resolveAligned(event))
|
||||
@@ -122,10 +123,6 @@ const HvacEquipmentTool = () => {
|
||||
>
|
||||
<div className="flex items-center gap-2 whitespace-nowrap rounded-full border border-border/60 bg-background/90 px-4 py-1.5 text-xs tabular-nums shadow-sm backdrop-blur">
|
||||
<span className="font-medium text-foreground">R/T rotate</span>
|
||||
<span aria-hidden className="text-muted-foreground">
|
||||
·
|
||||
</span>
|
||||
<span className="text-muted-foreground">⇧ smooth</span>
|
||||
</div>
|
||||
</Html>
|
||||
</LevelOffsetGroup>
|
||||
|
||||
Reference in New Issue
Block a user