@pascal-app/editor 1.0.7 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -15
- package/src/components/editor/action-menu-button.tsx +1 -1
- package/src/components/editor/alignment-3d-guide-layer.tsx +1 -1
- package/src/components/editor/custom-camera-controls.tsx +101 -45
- package/src/components/editor/duplicate-node.ts +2 -1
- package/src/components/editor/editor-layout-v2.tsx +1 -1
- package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
- package/src/components/editor/first-person-controls.tsx +20 -1983
- package/src/components/editor/fit-scene-framing.test.ts +318 -0
- package/src/components/editor/fit-scene-framing.ts +142 -0
- package/src/components/editor/floating-action-menu.tsx +5 -3
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floor-openings-overlay.tsx +1 -1
- package/src/components/editor/floor-region-controls.tsx +8 -4
- package/src/components/editor/floorplan-navigation-presentation.test.ts +0 -40
- package/src/components/editor/floorplan-navigation-presentation.ts +0 -66
- package/src/components/editor/floorplan-panel.tsx +199 -30
- package/src/components/editor/floorplan-wall-typing-keys.test.tsx +152 -0
- package/src/components/editor/floorplan-wall-typing-keys.ts +79 -0
- package/src/components/editor/follow-pascal.test.tsx +1 -2
- package/src/components/editor/follow-pascal.tsx +1 -1
- package/src/components/editor/footprint-height-handle.tsx +2 -2
- package/src/components/editor/group-actions.ts +1 -2
- package/src/components/editor/group-floating-action-menu.tsx +1 -1
- package/src/components/editor/group-move-3d.ts +1 -2
- package/src/components/editor/group-rotate-handle.tsx +1 -2
- package/src/components/editor/group-transform-shared.ts +3 -1
- package/src/components/editor/handles/use-handle-drag.ts +1 -2
- package/src/components/editor/index.tsx +43 -13
- package/src/components/editor/inspector-extension-actions.tsx +1 -1
- package/src/components/editor/measurement-pill.tsx +1 -1
- package/src/components/editor/node-arrow-handles.tsx +2 -2
- package/src/components/editor/open-wall-ends-3d-layer.tsx +2 -3
- package/src/components/editor/open-wall-ends-hint.tsx +1 -1
- package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
- package/src/components/editor/quick-measurement-card.tsx +1 -1
- package/src/components/editor/room-controls.tsx +10 -6
- package/src/components/editor/room-handles-2d.tsx +3 -3
- package/src/components/editor/room-handles.tsx +4 -5
- package/src/components/editor/room-highlight.tsx +6 -105
- package/src/components/editor/room-labels.test.ts +8 -822
- package/src/components/editor/room-labels.tsx +41 -1670
- package/src/components/editor/room-webgpu-materials.test.ts +1 -1
- package/src/components/editor/selection-manager.tsx +5 -3
- package/src/components/editor/site-edge-labels.tsx +7 -3
- package/src/components/editor/snapshot-capture-overlay.tsx +2 -5
- package/src/components/editor/use-floorplan-background-placement.ts +2 -1
- package/src/components/editor/views/plugins-view.tsx +1 -1
- package/src/components/editor/views/view-bar.tsx +9 -9
- package/src/components/editor/wall-measurement-label.tsx +31 -15
- package/src/components/editor/wall-move-side-handles.tsx +11 -7
- package/src/components/editor/wall-pull-grip.tsx +2 -2
- package/src/components/editor/wall-region-handles.tsx +2 -2
- package/src/components/editor/wall-snap-beacon-layer.tsx +53 -32
- package/src/components/editor-2d/floorplan-group-move.tsx +3 -4
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +13 -13
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +2 -3
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +2 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +23 -6
- package/src/components/editor-2d/renderers/floorplan-annotation-layout.test.ts +0 -37
- package/src/components/editor-2d/renderers/floorplan-annotation-layout.ts +0 -30
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +13 -3
- package/src/components/editor-2d/renderers/floorplan-draft-measurement.tsx +7 -1
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +14 -3
- package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.test.tsx +35 -0
- package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.tsx +65 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +30 -10
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +39 -35
- package/src/components/systems/roof/roof-edit-system.test.tsx +15 -1
- package/src/components/systems/roof/roof-edit-system.tsx +33 -21
- package/src/components/tools/distribution/distribution-run-tool.test.tsx +371 -3
- package/src/components/tools/distribution/distribution-run-tool.tsx +151 -33
- package/src/components/tools/item/move-tool.tsx +1 -6
- package/src/components/tools/item/placement-history.test.tsx +1 -1
- package/src/components/tools/item/use-placement-coordinator.tsx +3 -4
- package/src/components/tools/paint-region/wall-paint-region-tool.tsx +7 -3
- package/src/components/tools/registry/move-registry-node-tool.test.ts +42 -1
- package/src/components/tools/registry/move-registry-node-tool.tsx +39 -16
- package/src/components/tools/shared/cursor-sphere.tsx +1 -1
- package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
- package/src/components/tools/shared/placement-box.tsx +2 -6
- package/src/components/tools/shared/placement-coordinate-input.tsx +94 -0
- package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
- package/src/components/tools/shared/segment-angle.ts +0 -4
- package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
- package/src/components/tools/tool-manager.tsx +1 -24
- package/src/components/tools/wall/wall-typed-entry.test.ts +99 -0
- package/src/components/tools/wall/wall-typed-entry.ts +70 -0
- package/src/components/ui/action-menu/camera-actions.tsx +2 -2
- package/src/components/ui/action-menu/control-modes.tsx +1 -1
- package/src/components/ui/action-menu/follow-pascal-pill.tsx +1 -1
- package/src/components/ui/action-menu/index.tsx +3 -3
- package/src/components/ui/action-menu/measurement-control.tsx +3 -3
- package/src/components/ui/action-menu/placement.ts +0 -18
- package/src/components/ui/action-menu/point-ask-button.tsx +1 -1
- package/src/components/ui/action-menu/view-toggles.tsx +5 -5
- package/src/components/ui/controls/finish-swatch.tsx +1 -1
- package/src/components/ui/controls/material-paint-panel.tsx +2 -2
- package/src/components/ui/controls/scene-material-list.tsx +2 -2
- package/src/components/ui/controls/select-control.tsx +1 -1
- package/src/components/ui/controls/slider-control.tsx +18 -6
- package/src/components/ui/controls/terrain-sculpt-panel.tsx +1 -1
- package/src/components/ui/controls/tool-options-panel.tsx +74 -44
- package/src/components/ui/floating-level-selector.tsx +3 -3
- package/src/components/ui/helpers/contextual-helper-panel.tsx +1 -1
- package/src/components/ui/helpers/helper-manager.test.tsx +54 -1
- package/src/components/ui/helpers/helper-manager.tsx +55 -1
- package/src/components/ui/level-duplicate-dialog.tsx +1 -1
- package/src/components/ui/panels/collections/collections-popover.tsx +2 -2
- package/src/components/ui/panels/floor-foundation-panel.tsx +3 -3
- package/src/components/ui/panels/floor-opening-panel.tsx +5 -5
- package/src/components/ui/panels/mobile-selection-bar.tsx +28 -24
- package/src/components/ui/panels/multi-height-mode.tsx +3 -3
- package/src/components/ui/panels/multi-selection-panel.tsx +1 -1
- package/src/components/ui/panels/node-display.ts +1 -1
- package/src/components/ui/panels/panel-manager.tsx +5 -3
- package/src/components/ui/panels/panel-wrapper.tsx +42 -19
- package/src/components/ui/panels/parametric-inspector.tsx +1 -1
- package/src/components/ui/panels/room-construction-rows.tsx +7 -7
- package/src/components/ui/panels/room-name-options.ts +1 -5
- package/src/components/ui/panels/room-openings-field.tsx +1 -1
- package/src/components/ui/panels/room-panel.tsx +7 -9
- package/src/components/ui/primitives/color-dot.tsx +2 -2
- package/src/components/ui/primitives/opacity-control.tsx +2 -2
- package/src/components/ui/primitives/sidebar.tsx +3 -3
- package/src/components/ui/right-stack.test.ts +13 -6
- package/src/components/ui/right-stack.tsx +22 -11
- package/src/components/ui/sidebar/icon-rail.tsx +1 -1
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +1 -1
- package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +2 -2
- package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.tsx +1 -1
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +2 -2
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +1 -1
- package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +2 -6
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +3 -10
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +46 -7
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/tree-node-drag.tsx +14 -5
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +3 -3
- package/src/components/ui/sidebar/tab-bar.tsx +1 -1
- package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +2 -2
- package/src/components/ui/wall-type/wall-type-menu.tsx +1 -1
- package/src/components/viewer-overlay.tsx +1 -2
- package/src/hooks/use-auto-frame.ts +1 -2
- package/src/hooks/use-keyboard.ts +1 -2
- package/src/hooks/use-selected-room.ts +5 -7
- package/src/i18n/floor-footprint-name.ts +1 -1
- package/src/i18n/locales/en/messages.po +1036 -306
- package/src/i18n/locales/en/messages.ts +1 -1
- package/src/i18n/locales/en-XA/messages.ts +1 -1
- package/src/i18n/locales/es/messages.po +2267 -439
- package/src/i18n/locales/fr/messages.po +2267 -439
- package/src/i18n/locales/ja/messages.po +2267 -439
- package/src/i18n/locales/zh-CN/messages.po +2267 -439
- package/src/i18n/locales.test.ts +1 -1
- package/src/index.tsx +183 -178
- package/src/lib/below-grade.test.tsx +4 -4
- package/src/lib/camera-facing.test.ts +1 -1
- package/src/lib/camera-facing.ts +2 -3
- package/src/lib/contextual-help.ts +6 -0
- package/src/lib/editor-level-display.ts +1 -1
- package/src/lib/elevation-guides.ts +21 -6
- package/src/lib/elevator-support.ts +1 -72
- package/src/lib/floor-footprints.test.ts +3 -3
- package/src/lib/floor-footprints.ts +1 -9
- package/src/lib/floor-foundation-support.test.ts +8 -3
- package/src/lib/floor-opening-draft.ts +2 -2
- package/src/lib/floor-region-session.ts +1 -1
- package/src/lib/floorplan/annotation-visibility.test.ts +1 -1
- package/src/lib/floorplan/annotation-visibility.ts +1 -1
- package/src/lib/floorplan/floorplan-export.test.ts +1 -1
- package/src/lib/floorplan/floorplan-export.tsx +8 -11
- package/src/lib/floorplan/floorplan-extension.ts +4 -157
- package/src/lib/floorplan/floorplan-mode.ts +2 -1
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +1 -1
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +8 -6
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +3 -4
- package/src/lib/floorplan/geometry.ts +2 -112
- package/src/lib/floorplan/index.ts +0 -9
- package/src/lib/floorplan/items.ts +2 -1
- package/src/lib/floorplan/open-wall-ends.ts +2 -1
- package/src/lib/floorplan/schedules.ts +6 -3
- package/src/lib/floorplan/sheet-export.ts +7 -3
- package/src/lib/floorplan/site-plan/site-parts.ts +7 -1
- package/src/lib/floorplan/site-plan/site-plan-layer.tsx +1 -1
- package/src/lib/follow-pascal-presentation.ts +1 -1
- package/src/lib/gesture-lifecycle.ts +3 -1
- package/src/lib/glb-export.ts +1 -1
- package/src/lib/interaction/scope.ts +1 -0
- package/src/lib/layer-thickness.ts +1 -1
- package/src/lib/level-selection.test.ts +18 -2
- package/src/lib/level-selection.ts +31 -8
- package/src/lib/linear-display.ts +1 -1
- package/src/lib/live-gesture-round-trip.test.ts +1 -1
- package/src/lib/material-paint.ts +1 -1
- package/src/lib/measurement-label.test.ts +1 -1
- package/src/lib/measurements.test.ts +7 -5
- package/src/lib/mezzanine-selection.test.ts +1 -1
- package/src/lib/node-action-movement.test.tsx +115 -0
- package/src/lib/node-action-movement.ts +2 -0
- package/src/lib/paint-region-mode.ts +1 -1
- package/src/lib/placement-metadata.ts +4 -0
- package/src/lib/point-ask/describe-target.ts +6 -8
- package/src/lib/point-ask/region.ts +1 -1
- package/src/lib/point-ask/session.ts +1 -2
- package/src/lib/print-shell-compiler.ts +12 -1
- package/src/lib/print-wall-solids.test.ts +11 -0
- package/src/lib/print-wall-solids.ts +9 -0
- package/src/lib/roof-duplication.test.ts +100 -0
- package/src/lib/roof-duplication.ts +30 -5
- package/src/lib/room-built-on.test.ts +2 -5
- package/src/lib/room-built-on.ts +1 -15
- package/src/lib/room-construction-commands.ts +3 -2
- package/src/lib/room-controls-phase6.test.ts +1 -1
- package/src/lib/room-handle-drag.ts +8 -37
- package/src/lib/room-name-catalog.test.ts +2 -1
- package/src/lib/room-name-messages.test.ts +2 -1
- package/src/lib/room-name-messages.ts +1 -118
- package/src/lib/room-name-storage.test.ts +2 -1
- package/src/lib/room-push-dimensions.test.ts +1 -1
- package/src/lib/room-selection-commands.ts +2 -2
- package/src/lib/room-selection.test.ts +6 -6
- package/src/lib/room-transform-session.ts +1 -2
- package/src/lib/room-zone-routing.ts +2 -2
- package/src/lib/scene-clipboard.ts +2 -1
- package/src/lib/selection-routing.test.ts +0 -28
- package/src/lib/selection-routing.ts +1 -40
- package/src/lib/sfx-bus.ts +8 -32
- package/src/lib/sfx-player.test.ts +72 -1
- package/src/lib/sfx-player.ts +49 -337
- package/src/lib/snapping-mode.test.ts +23 -1
- package/src/lib/viewer-selection.test.ts +38 -10
- package/src/lib/wall-pull.test.ts +1 -1
- package/src/lib/wall-pull.ts +1 -27
- package/src/lib/wall-region-list.ts +3 -1
- package/src/lib/window-interaction.test.ts +4 -2
- package/src/store/use-editor.tsx +22 -26
- package/src/store/use-floorplan-annotation-visibility.ts +5 -5
- package/src/store/use-placement-typing.test.ts +165 -0
- package/src/store/use-placement-typing.ts +186 -0
- package/src/store/use-wall-region-handles-selection.ts +1 -2
- package/src/components/editor/__bench__/room-label-placement.bench.ts +0 -108
- package/src/components/editor/first-person/build-collider-world.test.ts +0 -596
- package/src/components/editor/first-person/build-collider-world.ts +0 -549
- package/src/components/editor/first-person/ground-holes.test.ts +0 -128
- package/src/components/editor/first-person/ground-holes.ts +0 -130
- package/src/components/editor/first-person/mechanism-targets.test.ts +0 -162
- package/src/components/editor/first-person/mechanism-targets.ts +0 -88
- package/src/components/editor/first-person/terrain-collider.test.ts +0 -132
- package/src/components/editor/first-person/terrain-collider.ts +0 -212
- package/src/components/editor/floorplan-camera-sync.test.ts +0 -249
- package/src/components/editor/floorplan-camera-sync.ts +0 -213
- package/src/components/editor/localized-html.tsx +0 -22
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.test.tsx +0 -236
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +0 -708
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +0 -385
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +0 -670
- package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +0 -89
- package/src/components/editor-2d/renderers/floorplan-label-angle.ts +0 -106
- package/src/components/editor-2d/svg-paths.ts +0 -124
- package/src/components/tools/elevator/elevator-defaults.ts +0 -8
- package/src/components/tools/elevator/elevator-tool.tsx +0 -372
- package/src/components/tools/elevator/move-elevator-tool.tsx +0 -262
- package/src/components/ui/action-menu/action-button.tsx +0 -89
- package/src/components/ui/action-menu/follow-pascal-toggle.tsx +0 -42
- package/src/components/ui/primitives/button.tsx +0 -69
- package/src/components/ui/primitives/dropdown-menu.tsx +0 -232
- package/src/components/ui/primitives/popover.tsx +0 -42
- package/src/components/ui/primitives/switch.tsx +0 -29
- package/src/components/ui/primitives/tooltip.tsx +0 -57
- package/src/components/ui/scene-loader.tsx +0 -73
- package/src/components/viewer/floorplan-compass-button.tsx +0 -67
- package/src/components/viewer/floorplan-preview-geometry.test.ts +0 -107
- package/src/components/viewer/floorplan-preview-geometry.ts +0 -281
- package/src/components/viewer/floorplan-preview-navigation.test.ts +0 -43
- package/src/components/viewer/floorplan-preview-navigation.ts +0 -62
- package/src/components/viewer/floorplan-preview-visibility.test.ts +0 -35
- package/src/components/viewer/floorplan-preview-visibility.ts +0 -19
- package/src/components/viewer/floorplan-preview.tsx +0 -1065
- package/src/components/viewer/render-settings-menu.tsx +0 -294
- package/src/components/viewer/sky-control.tsx +0 -710
- package/src/components/viewer/use-viewer-camera-navigation-sync.ts +0 -140
- package/src/components/viewer/use-viewer-ui-sounds.test.ts +0 -15
- package/src/components/viewer/use-viewer-ui-sounds.ts +0 -46
- package/src/components/viewer/viewer-controls-bar.tsx +0 -496
- package/src/components/viewer/viewer-hover-label.tsx +0 -42
- package/src/components/viewer/viewer-rooms.tsx +0 -248
- package/src/components/viewer/viewer-scene-header.tsx +0 -286
- package/src/components/viewer/viewer-selection-manager.tsx +0 -124
- package/src/components/viewer/viewer-stage-modes.test.ts +0 -38
- package/src/components/viewer/viewer-stage-modes.ts +0 -49
- package/src/components/viewer/viewer-stage-switcher.tsx +0 -97
- package/src/components/viewer/viewer-stage.test.tsx +0 -47
- package/src/components/viewer/viewer-stage.tsx +0 -221
- package/src/components/walkthrough-hud.tsx +0 -193
- package/src/hooks/use-mobile.ts +0 -19
- package/src/i18n/level-name.ts +0 -39
- package/src/i18n/scene-theme-name.ts +0 -16
- package/src/lib/below-grade.ts +0 -133
- package/src/lib/camera-pose.test.ts +0 -237
- package/src/lib/camera-pose.ts +0 -172
- package/src/lib/door-interaction.ts +0 -102
- package/src/lib/floorplan/floorplan-extension.test.ts +0 -42
- package/src/lib/floorplan/floorplan-readonly.test.ts +0 -47
- package/src/lib/floorplan/floorplan-readonly.ts +0 -95
- package/src/lib/floorplan/marks.test.ts +0 -213
- package/src/lib/floorplan/marks.ts +0 -238
- package/src/lib/measurement-label.ts +0 -68
- package/src/lib/measurements.ts +0 -225
- package/src/lib/node-display-label.ts +0 -37
- package/src/lib/room-assembly-overlay.test.ts +0 -275
- package/src/lib/room-assembly-overlay.ts +0 -265
- package/src/lib/room-selection.ts +0 -417
- package/src/lib/scene-bounds.test.ts +0 -183
- package/src/lib/scene-bounds.ts +0 -169
- package/src/lib/typing-target.ts +0 -9
- package/src/lib/viewer-selection.ts +0 -595
- package/src/lib/window-interaction.ts +0 -142
- package/src/lib/zone-content.ts +0 -133
- package/src/store/camera-pose-store.test.ts +0 -29
- package/src/store/camera-pose-store.ts +0 -27
- package/src/store/navigation-sync-pose-store.test.ts +0 -34
- package/src/store/navigation-sync-pose-store.ts +0 -27
- package/src/store/use-audio.test.ts +0 -146
- package/src/store/use-audio.tsx +0 -169
- package/src/store/use-first-person-hud.ts +0 -40
- package/src/store/use-follow-pascal.test.ts +0 -62
- package/src/store/use-follow-pascal.ts +0 -53
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import {
|
|
4
4
|
type AnyNodeId,
|
|
5
5
|
emitter,
|
|
6
|
+
formatFixed,
|
|
6
7
|
type GridEvent,
|
|
7
8
|
sceneRegistry,
|
|
8
9
|
usePascalI18n,
|
|
@@ -18,18 +19,19 @@ import {
|
|
|
18
19
|
isGridSnapActive,
|
|
19
20
|
isMagneticSnapActive,
|
|
20
21
|
isTypingTarget,
|
|
22
|
+
isViewVisible,
|
|
21
23
|
markToolCancelConsumed,
|
|
22
24
|
publishPlacementSurface,
|
|
23
25
|
triggerSFX,
|
|
24
26
|
useEditor,
|
|
25
27
|
useInteractionScope,
|
|
26
28
|
} from '@pascal-app/editor'
|
|
29
|
+
import { LocalizedHtml as Html } from '@pascal-app/viewer/ui'
|
|
27
30
|
import { useThree } from '@react-three/fiber'
|
|
28
31
|
import { type RefObject, useCallback, useEffect, useRef, useState } from 'react'
|
|
29
32
|
import { createPortal } from 'react-dom'
|
|
30
33
|
import { type Group, Vector3 } from 'three'
|
|
31
34
|
import { messageWithSlot } from '../../../i18n/message-slot'
|
|
32
|
-
import { Html } from '../../editor/localized-html'
|
|
33
35
|
import type { RunSurfaceBounds, RunSurfaceTarget } from './distribution-run-contract'
|
|
34
36
|
import { clearDrawAlignment } from './draw-alignment'
|
|
35
37
|
import type { RunBodyHit, ScenePort } from './ports'
|
|
@@ -226,6 +228,11 @@ export type DistributionRunToolConfig = {
|
|
|
226
228
|
active: boolean
|
|
227
229
|
levelId: AnyNodeId | null
|
|
228
230
|
toolName: string
|
|
231
|
+
routingMode?: 'surface' | 'horizontal' | 'vertical'
|
|
232
|
+
/** Level-local centreline elevation, or the target height for a vertical leg. */
|
|
233
|
+
elevation?: number
|
|
234
|
+
targetElevation?: number
|
|
235
|
+
connectCrossings?: boolean
|
|
229
236
|
initialStart?: RunPoint | null
|
|
230
237
|
initialConnection?: RunConnection | null
|
|
231
238
|
getPorts: () => ScenePort[]
|
|
@@ -524,6 +531,7 @@ function surfacePointFromEvent(
|
|
|
524
531
|
|
|
525
532
|
export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
526
533
|
const { camera, gl } = useThree()
|
|
534
|
+
const planVisible = useEditor((state) => isViewVisible(state, '2d'))
|
|
527
535
|
const initialStartRef = useRef<RunPoint | null>(
|
|
528
536
|
config.initialStart ? [...config.initialStart] : null,
|
|
529
537
|
)
|
|
@@ -534,6 +542,7 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
534
542
|
const [cursor, setCursor] = useState<RunPoint | null>(initialStartRef.current)
|
|
535
543
|
const [snapTarget, setSnapTarget] = useState<RunPoint | null>(null)
|
|
536
544
|
const [snapScreen, setSnapScreen] = useState<PortScreenPoint | null>(null)
|
|
545
|
+
const [cursorScreen, setCursorScreen] = useState<PortScreenPoint | null>(null)
|
|
537
546
|
const [endConnection, setEndConnection] = useState<RunConnection>({
|
|
538
547
|
port: null,
|
|
539
548
|
body: null,
|
|
@@ -592,6 +601,7 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
592
601
|
const typed = Number.parseFloat(lengthInputRef.current)
|
|
593
602
|
if (
|
|
594
603
|
!currentStart ||
|
|
604
|
+
configRef.current.routingMode === 'vertical' ||
|
|
595
605
|
!Number.isFinite(typed) ||
|
|
596
606
|
typed <= 0 ||
|
|
597
607
|
resolved.port ||
|
|
@@ -617,28 +627,47 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
617
627
|
}
|
|
618
628
|
}
|
|
619
629
|
|
|
630
|
+
// A vertical route rises from its start to the target elevation wherever the
|
|
631
|
+
// pointer is, so it resolves without a pointer event.
|
|
632
|
+
const verticalTarget = (currentStart: RunPoint): ResolvedRunPoint => ({
|
|
633
|
+
point: [
|
|
634
|
+
currentStart[0],
|
|
635
|
+
configRef.current.targetElevation ?? currentStart[1],
|
|
636
|
+
currentStart[2],
|
|
637
|
+
],
|
|
638
|
+
frame: createRunSurfaceFrame(currentStart),
|
|
639
|
+
surfaceTarget: null,
|
|
640
|
+
snapped: null,
|
|
641
|
+
directionMode: 'vertical',
|
|
642
|
+
port: null,
|
|
643
|
+
body: null,
|
|
644
|
+
})
|
|
645
|
+
|
|
620
646
|
const resolvePoint = (event: GridEvent): ResolvedRunPoint => {
|
|
621
647
|
const adapter = configRef.current
|
|
622
648
|
const hit = surfacePointFromEvent(event, adapter.levelId)
|
|
623
649
|
const currentStart = startRef.current
|
|
624
650
|
const previous = lastResolvedRef.current
|
|
625
|
-
const
|
|
651
|
+
const explicitRoute = adapter.routingMode && adapter.routingMode !== 'surface'
|
|
652
|
+
const lockedWallTarget = currentStart && !explicitRoute ? lockedWallTargetRef.current : null
|
|
626
653
|
const lockedWallHit =
|
|
627
654
|
lockedWallTarget && isSameRunWallSurface(lockedWallTarget, event.surfaceHit)
|
|
628
|
-
const working =
|
|
629
|
-
?
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
:
|
|
641
|
-
|
|
655
|
+
const working = explicitRoute
|
|
656
|
+
? createRunSurfaceFrame([0, currentStart?.[1] ?? adapter.elevation ?? 0, 0])
|
|
657
|
+
: lockedWallTarget
|
|
658
|
+
? {
|
|
659
|
+
...lockedWallTarget.frame,
|
|
660
|
+
origin: lockedWallTarget.frame.origin.map(
|
|
661
|
+
(value, index) =>
|
|
662
|
+
value +
|
|
663
|
+
lockedWallTarget.frame.normal[index]! *
|
|
664
|
+
(adapter.surfaceClearance?.(lockedWallTarget) ?? 0),
|
|
665
|
+
) as RunPoint,
|
|
666
|
+
}
|
|
667
|
+
: !event.surfaceHit && previous?.surfaceTarget?.kind === 'wall' && currentStart
|
|
668
|
+
? createRunSurfaceFrame(currentStart, UP)
|
|
669
|
+
: (previous?.frame ?? (currentStart ? createRunSurfaceFrame(currentStart) : null))
|
|
670
|
+
const hasSurface = !explicitRoute && (!!event.surfaceHit || !working || !event.localRay)
|
|
642
671
|
const target = lockedWallTarget ?? (hasSurface ? hit.target : null)
|
|
643
672
|
const hitForCursor = lockedWallTarget
|
|
644
673
|
? lockedWallHit
|
|
@@ -651,16 +680,25 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
651
680
|
hit: hitForCursor,
|
|
652
681
|
working,
|
|
653
682
|
ray: event.localRay,
|
|
654
|
-
fallback: previous?.point ?? currentStart ?? hit.point,
|
|
683
|
+
fallback: explicitRoute ? [...hit.point] : (previous?.point ?? currentStart ?? hit.point),
|
|
655
684
|
clearance: adapter.surfaceClearance?.(target) ?? 0,
|
|
656
685
|
})
|
|
686
|
+
const routeElevation = currentStart?.[1] ?? adapter.elevation ?? 0
|
|
687
|
+
if (explicitRoute) {
|
|
688
|
+
resolved.point[1] = routeElevation
|
|
689
|
+
if (currentStart && adapter.routingMode === 'vertical') return verticalTarget(currentStart)
|
|
690
|
+
}
|
|
657
691
|
const bypass = event.nativeEvent?.altKey === true
|
|
658
692
|
const gridStep = !bypass && isGridSnapActive() ? useEditor.getState().gridSnapStep : 0
|
|
659
693
|
const angleLocked = !bypass && (isAngleSnapActive() || isGridSnapActive())
|
|
660
694
|
let point = currentStart
|
|
661
695
|
? resolved.point
|
|
662
696
|
: snapRunPointToSurface(resolved.point, resolved.frame, gridStep)
|
|
663
|
-
const
|
|
697
|
+
const selectedDirection = forcedDirectionRef.current
|
|
698
|
+
const forcedDirection =
|
|
699
|
+
explicitRoute && selectedDirection && Math.abs(selectedDirection[1]) > 1e-6
|
|
700
|
+
? null
|
|
701
|
+
: selectedDirection
|
|
664
702
|
if (currentStart && forcedDirection) {
|
|
665
703
|
point = projectRunToDirection(currentStart, point, forcedDirection)
|
|
666
704
|
}
|
|
@@ -673,7 +711,11 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
673
711
|
if (currentStart && gridStep > 0) {
|
|
674
712
|
point = snapRunLength(currentStart, point, gridStep)
|
|
675
713
|
}
|
|
676
|
-
const
|
|
714
|
+
const drawingTarget: RunSurfaceTarget | null =
|
|
715
|
+
explicitRoute && adapter.levelId
|
|
716
|
+
? { kind: 'surface', levelId: adapter.levelId, frame: resolved.frame }
|
|
717
|
+
: target
|
|
718
|
+
const sample = { frame: resolved.frame, surfaceTarget: drawingTarget }
|
|
677
719
|
const native = (event.nativeEvent ?? {}) as {
|
|
678
720
|
clientX?: number
|
|
679
721
|
clientY?: number
|
|
@@ -713,11 +755,22 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
713
755
|
const screen = projectConnection(candidate)
|
|
714
756
|
return !!screen && Math.hypot(screen.x - pointer[0], screen.y - pointer[1]) <= 12
|
|
715
757
|
}
|
|
758
|
+
// An explicit route may only connect at the height its free end reaches above that
|
|
759
|
+
// plan point, which includes the adapter's fall.
|
|
760
|
+
const routeHeightAt = (candidate: readonly number[]): number => {
|
|
761
|
+
const level: RunPoint = [candidate[0]!, routeElevation, candidate[2]!]
|
|
762
|
+
return currentStart
|
|
763
|
+
? (adapter.resolveFreeEnd?.(currentStart, level, startConnectionRef.current) ?? level)[1]
|
|
764
|
+
: routeElevation
|
|
765
|
+
}
|
|
766
|
+
const onRoute = (candidate: readonly number[]) =>
|
|
767
|
+
!explicitRoute || Math.abs(candidate[1]! - routeHeightAt(candidate)) < 1e-4
|
|
716
768
|
const resolveConnection = (candidate: RunPoint): ResolvedRunPoint | null => {
|
|
717
769
|
if (bypass || !(isGridSnapActive() || isMagneticSnapActive() || isAngleSnapActive()))
|
|
718
770
|
return null
|
|
719
771
|
const source = startConnectionRef.current.port
|
|
720
|
-
const
|
|
772
|
+
const ports = adapter.getPorts().filter((port) => onRoute(port.position))
|
|
773
|
+
const hit = findScreenPort(ports, pointer, projectConnection, source)
|
|
721
774
|
if (hit) {
|
|
722
775
|
return {
|
|
723
776
|
frame: createRunSurfaceFrame([...hit.port.position]),
|
|
@@ -730,8 +783,19 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
730
783
|
body: null,
|
|
731
784
|
}
|
|
732
785
|
}
|
|
733
|
-
const body =
|
|
734
|
-
|
|
786
|
+
const body =
|
|
787
|
+
adapter.connectCrossings === false
|
|
788
|
+
? null
|
|
789
|
+
: adapter.findBody(
|
|
790
|
+
explicitRoute ? [candidate[0], routeHeightAt(candidate), candidate[2]] : candidate,
|
|
791
|
+
drawingTarget,
|
|
792
|
+
)
|
|
793
|
+
if (
|
|
794
|
+
body &&
|
|
795
|
+
onRoute(body.point) &&
|
|
796
|
+
acceptsConnection(body.point) &&
|
|
797
|
+
body.nodeId !== source?.nodeId
|
|
798
|
+
) {
|
|
735
799
|
return {
|
|
736
800
|
...sample,
|
|
737
801
|
point: body.point,
|
|
@@ -747,7 +811,7 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
747
811
|
if (connection) return connection
|
|
748
812
|
// Rank displayed directions before a surface projection discards height.
|
|
749
813
|
if (currentStart && event.localRay && !bypass && (forcedDirection || angleLocked)) {
|
|
750
|
-
const source = startConnectionRef.current.port?.direction ?? null
|
|
814
|
+
const source = explicitRoute ? null : (startConnectionRef.current.port?.direction ?? null)
|
|
751
815
|
const candidates = forcedDirection
|
|
752
816
|
? [forcedDirection]
|
|
753
817
|
: source
|
|
@@ -803,7 +867,7 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
803
867
|
forcedDirection ||
|
|
804
868
|
(Math.abs(directionHit.direction[1]) > 1e-6 && target?.kind === 'floor')
|
|
805
869
|
? null
|
|
806
|
-
:
|
|
870
|
+
: drawingTarget,
|
|
807
871
|
snapped: null,
|
|
808
872
|
directionMode: 'angle',
|
|
809
873
|
port: null,
|
|
@@ -951,7 +1015,12 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
951
1015
|
setAltActive(false)
|
|
952
1016
|
}
|
|
953
1017
|
|
|
1018
|
+
const updateCursorScreen = (event: GridEvent) => {
|
|
1019
|
+
const pointer = getGridEventScreenProjection(event)?.pointer
|
|
1020
|
+
setCursorScreen(pointer ? { x: pointer[0], y: pointer[1], depth: 0 } : null)
|
|
1021
|
+
}
|
|
954
1022
|
const onMove = (event: RunPointerEvent) => {
|
|
1023
|
+
updateCursorScreen(event)
|
|
955
1024
|
lastPointerRef.current = event
|
|
956
1025
|
const clientY = (event.nativeEvent as { clientY?: number } | undefined)?.clientY
|
|
957
1026
|
if (typeof clientY === 'number') lastClientYRef.current = clientY
|
|
@@ -973,6 +1042,7 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
973
1042
|
}
|
|
974
1043
|
|
|
975
1044
|
const onClick = (event: GridEvent) => {
|
|
1045
|
+
updateCursorScreen(event)
|
|
976
1046
|
event.nativeEvent?.stopPropagation?.()
|
|
977
1047
|
const currentStart = startRef.current
|
|
978
1048
|
if (altAnchorRef.current && currentStart) {
|
|
@@ -993,8 +1063,10 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
993
1063
|
port: resolved.port,
|
|
994
1064
|
body: resolved.port ? null : resolved.body,
|
|
995
1065
|
}
|
|
996
|
-
if (
|
|
997
|
-
|
|
1066
|
+
if (!configRef.current.routingMode || configRef.current.routingMode === 'surface') {
|
|
1067
|
+
if (shouldLockRunWallSurface(resolved.surfaceTarget, connection)) {
|
|
1068
|
+
lockedWallTargetRef.current = resolved.surfaceTarget
|
|
1069
|
+
}
|
|
998
1070
|
}
|
|
999
1071
|
triggerSFX('sfx:grid-snap')
|
|
1000
1072
|
startConnectionRef.current = connection
|
|
@@ -1005,6 +1077,10 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
1005
1077
|
setSnapTarget(resolved.snapped)
|
|
1006
1078
|
setSnapScreen(resolved.snapScreen ?? null)
|
|
1007
1079
|
setEndConnection({ port: null, body: null })
|
|
1080
|
+
// A vertical route ends at the target, not under the pointer: show that
|
|
1081
|
+
// riser now so the next click or Enter places what the preview shows.
|
|
1082
|
+
if (configRef.current.routingMode === 'vertical')
|
|
1083
|
+
updateCursor(verticalTarget(resolved.point))
|
|
1008
1084
|
return
|
|
1009
1085
|
}
|
|
1010
1086
|
const typedResolved = applyTypedLength(resolved)
|
|
@@ -1036,6 +1112,11 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
1036
1112
|
setAltActive(true)
|
|
1037
1113
|
return
|
|
1038
1114
|
}
|
|
1115
|
+
if (
|
|
1116
|
+
configRef.current.routingMode === 'vertical' &&
|
|
1117
|
+
(event.key === 'Tab' || /^[0-9.,]$/.test(event.key))
|
|
1118
|
+
)
|
|
1119
|
+
return
|
|
1039
1120
|
if (
|
|
1040
1121
|
event.key === 'Tab' &&
|
|
1041
1122
|
startRef.current &&
|
|
@@ -1045,7 +1126,10 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
1045
1126
|
) {
|
|
1046
1127
|
event.preventDefault()
|
|
1047
1128
|
event.stopImmediatePropagation()
|
|
1048
|
-
const source =
|
|
1129
|
+
const source =
|
|
1130
|
+
configRef.current.routingMode === 'horizontal'
|
|
1131
|
+
? null
|
|
1132
|
+
: startConnectionRef.current.port?.direction
|
|
1049
1133
|
const candidates = source
|
|
1050
1134
|
? run3DDirectionCandidates(source)
|
|
1051
1135
|
: runHorizontalDirectionCandidates(null)
|
|
@@ -1117,7 +1201,10 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
1117
1201
|
}
|
|
1118
1202
|
|
|
1119
1203
|
refreshCursorRef.current = () => {
|
|
1120
|
-
|
|
1204
|
+
const currentStart = startRef.current
|
|
1205
|
+
if (currentStart && configRef.current.routingMode === 'vertical')
|
|
1206
|
+
updateCursor(verticalTarget(currentStart))
|
|
1207
|
+
else if (lastPointerRef.current)
|
|
1121
1208
|
updateCursor(applyTypedLength(resolvePoint(lastPointerRef.current)))
|
|
1122
1209
|
else if (lastResolvedRef.current) updateCursor(applyTypedLength(lastResolvedRef.current))
|
|
1123
1210
|
}
|
|
@@ -1151,18 +1238,39 @@ export function useDistributionRunTool(config: DistributionRunToolConfig) {
|
|
|
1151
1238
|
}
|
|
1152
1239
|
}, [camera, gl, config.active, updateLengthInput])
|
|
1153
1240
|
|
|
1241
|
+
// The resolver reads config through a ref; changing these controls must refresh a stationary cursor.
|
|
1242
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: configuration changes invalidate the cursor
|
|
1243
|
+
useEffect(() => {
|
|
1244
|
+
forcedDirectionRef.current = null
|
|
1245
|
+
if (config.routingMode !== 'surface') lockedWallTargetRef.current = null
|
|
1246
|
+
refreshCursor()
|
|
1247
|
+
}, [
|
|
1248
|
+
config.routingMode,
|
|
1249
|
+
config.elevation,
|
|
1250
|
+
config.targetElevation,
|
|
1251
|
+
config.connectCrossings,
|
|
1252
|
+
refreshCursor,
|
|
1253
|
+
])
|
|
1254
|
+
|
|
1154
1255
|
return {
|
|
1155
1256
|
refreshCursor,
|
|
1257
|
+
routingMode: config.routingMode,
|
|
1156
1258
|
start,
|
|
1157
1259
|
cursor,
|
|
1158
1260
|
snapTarget,
|
|
1159
1261
|
snapScreen,
|
|
1262
|
+
cursorScreen: planVisible ? cursorScreen : null,
|
|
1160
1263
|
altActive,
|
|
1161
1264
|
directionMode,
|
|
1162
1265
|
lengthInput,
|
|
1163
1266
|
validationMessage,
|
|
1164
1267
|
onLengthInputChange: updateLengthInput,
|
|
1165
1268
|
onDirectionSelect: (direction: RunPoint) => {
|
|
1269
|
+
if (
|
|
1270
|
+
configRef.current.routingMode === 'vertical' ||
|
|
1271
|
+
(configRef.current.routingMode === 'horizontal' && Math.abs(direction[1]) > 1e-6)
|
|
1272
|
+
)
|
|
1273
|
+
return
|
|
1166
1274
|
forcedDirectionRef.current = direction
|
|
1167
1275
|
setDirectionMode('angle')
|
|
1168
1276
|
refreshCursorRef.current()
|
|
@@ -1183,6 +1291,7 @@ export function DistributionRunCursor({
|
|
|
1183
1291
|
extraParts = [],
|
|
1184
1292
|
cursorRef,
|
|
1185
1293
|
directionMode,
|
|
1294
|
+
routingMode,
|
|
1186
1295
|
startDirection,
|
|
1187
1296
|
lengthInput,
|
|
1188
1297
|
validationMessage,
|
|
@@ -1198,6 +1307,7 @@ export function DistributionRunCursor({
|
|
|
1198
1307
|
extraParts?: DimensionPillPart[]
|
|
1199
1308
|
cursorRef?: RefObject<Group | null>
|
|
1200
1309
|
directionMode: RunDirectionMode
|
|
1310
|
+
routingMode?: 'surface' | 'horizontal' | 'vertical'
|
|
1201
1311
|
startDirection?: readonly [number, number, number] | null
|
|
1202
1312
|
lengthInput?: string
|
|
1203
1313
|
validationMessage?: string | null
|
|
@@ -1208,8 +1318,8 @@ export function DistributionRunCursor({
|
|
|
1208
1318
|
const i18n = usePascalI18n()
|
|
1209
1319
|
const lengthFieldRef = useRef<HTMLInputElement>(null)
|
|
1210
1320
|
useEffect(() => {
|
|
1211
|
-
if (start) lengthFieldRef.current?.focus()
|
|
1212
|
-
}, [start])
|
|
1321
|
+
if (start && routingMode !== 'vertical') lengthFieldRef.current?.focus()
|
|
1322
|
+
}, [start, routingMode])
|
|
1213
1323
|
if (!cursor) return null
|
|
1214
1324
|
const parts: DimensionPillPart[] = [
|
|
1215
1325
|
...(start
|
|
@@ -1270,9 +1380,9 @@ export function DistributionRunCursor({
|
|
|
1270
1380
|
cursor={cursor}
|
|
1271
1381
|
mode={directionMode}
|
|
1272
1382
|
snapped={!!snapTarget}
|
|
1273
|
-
sourceDirection={startDirection ?? null}
|
|
1383
|
+
sourceDirection={routingMode === 'horizontal' ? null : (startDirection ?? null)}
|
|
1274
1384
|
start={start}
|
|
1275
|
-
onDirectionSelect={onDirectionSelect}
|
|
1385
|
+
onDirectionSelect={routingMode === 'vertical' ? undefined : onDirectionSelect}
|
|
1276
1386
|
/>
|
|
1277
1387
|
)}
|
|
1278
1388
|
<CursorSphere
|
|
@@ -1291,7 +1401,15 @@ export function DistributionRunCursor({
|
|
|
1291
1401
|
>
|
|
1292
1402
|
<div className="flex flex-col items-center gap-1">
|
|
1293
1403
|
<DimensionPill parts={parts} primary={primary} unit={unit} />
|
|
1294
|
-
{start ? (
|
|
1404
|
+
{start && routingMode === 'vertical' ? (
|
|
1405
|
+
<div className="rounded-full border border-border/60 bg-background/90 px-3 py-1 text-xs shadow-sm">
|
|
1406
|
+
{i18n._({
|
|
1407
|
+
id: 'editor.tools.distribution.distributionRunTool.targetElevation',
|
|
1408
|
+
message: 'Target elevation: {elevation} m · click or Enter to place',
|
|
1409
|
+
values: { elevation: formatFixed(cursor[1], 2, i18n.getLocale()) },
|
|
1410
|
+
})}
|
|
1411
|
+
</div>
|
|
1412
|
+
) : start ? (
|
|
1295
1413
|
<label className="rounded-full border border-border/60 bg-background/90 px-3 py-1 text-[11px] tabular-nums text-muted-foreground shadow-sm backdrop-blur">
|
|
1296
1414
|
{messageWithSlot(
|
|
1297
1415
|
i18n,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type { AnyNodeId,
|
|
1
|
+
import type { AnyNodeId, SpawnNode } from '@pascal-app/core'
|
|
2
2
|
import { createSceneApi, nodeRegistry, useScene } from '@pascal-app/core'
|
|
3
3
|
import { Suspense, useMemo } from 'react'
|
|
4
4
|
import { useMovingNode } from '../../../store/use-interaction-scope'
|
|
5
|
-
import { MoveElevatorTool } from '../elevator/move-elevator-tool'
|
|
6
5
|
import { MoveRegistryNodeTool } from '../registry/move-registry-node-tool'
|
|
7
6
|
import { getRegistryAffordanceTool } from '../shared/affordance-dispatch'
|
|
8
7
|
|
|
@@ -20,8 +19,6 @@ import { getRegistryAffordanceTool } from '../shared/affordance-dispatch'
|
|
|
20
19
|
* 2. `MoveRegistryNodeTool` — generic translate-on-XZ for kinds that only
|
|
21
20
|
* declare `capabilities.movable` (shelf, spawn, duct-terminal,
|
|
22
21
|
* hvac-equipment, …).
|
|
23
|
-
* 3. `elevator` is the lone remaining legacy arm — its bespoke cab/shaft
|
|
24
|
-
* mover hasn't been ported to a kind-owned affordance yet.
|
|
25
22
|
*/
|
|
26
23
|
export const MoveTool: React.FC<{
|
|
27
24
|
onNodeMoved?: (nodeId: AnyNodeId) => void
|
|
@@ -47,7 +44,5 @@ export const MoveTool: React.FC<{
|
|
|
47
44
|
return <MoveRegistryNodeTool node={movingNode} />
|
|
48
45
|
}
|
|
49
46
|
|
|
50
|
-
if (movingNode.type === 'elevator')
|
|
51
|
-
return <MoveElevatorTool node={movingNode as ElevatorNode} onCommitted={onNodeMoved} />
|
|
52
47
|
return null
|
|
53
48
|
}
|
|
@@ -22,12 +22,12 @@ import {
|
|
|
22
22
|
WallNode,
|
|
23
23
|
} from '@pascal-app/core'
|
|
24
24
|
import { useViewer } from '@pascal-app/viewer'
|
|
25
|
+
import { LocalizedHtml as Html } from '@pascal-app/viewer/ui'
|
|
25
26
|
import { act, create } from '@react-three/test-renderer'
|
|
26
27
|
import { Children, Component, cloneElement, isValidElement, type ReactNode } from 'react'
|
|
27
28
|
import { Group } from 'three'
|
|
28
29
|
import useEditor from '../../../store/use-editor'
|
|
29
30
|
import useInteractionScope from '../../../store/use-interaction-scope'
|
|
30
|
-
import { Html } from '../../editor/localized-html'
|
|
31
31
|
import { useDraftNode } from './use-draft-node'
|
|
32
32
|
import { usePlacementCoordinator } from './use-placement-coordinator'
|
|
33
33
|
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
collectAlignmentAnchors,
|
|
10
10
|
emitter,
|
|
11
11
|
findLevelAncestorId,
|
|
12
|
+
formatLinearMeasurement,
|
|
12
13
|
type GridEvent,
|
|
13
14
|
getScaledDimensions,
|
|
14
15
|
getWallBodyCenterOffset,
|
|
@@ -29,7 +30,8 @@ import {
|
|
|
29
30
|
useSpatialQuery,
|
|
30
31
|
type WallEvent,
|
|
31
32
|
} from '@pascal-app/core'
|
|
32
|
-
import { useViewer } from '@pascal-app/viewer'
|
|
33
|
+
import { isTypingTarget, useViewer } from '@pascal-app/viewer'
|
|
34
|
+
import { LocalizedHtml as Html } from '@pascal-app/viewer/ui'
|
|
33
35
|
import { useFrame, useThree } from '@react-three/fiber'
|
|
34
36
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
35
37
|
import {
|
|
@@ -49,7 +51,6 @@ import {
|
|
|
49
51
|
publishPlacementSurface,
|
|
50
52
|
} from '../../../lib/active-placement-surface'
|
|
51
53
|
import { EDITOR_LAYER } from '../../../lib/constants'
|
|
52
|
-
import { formatLinearMeasurement } from '../../../lib/measurements'
|
|
53
54
|
import { isFreshPlacementMetadata } from '../../../lib/placement-metadata'
|
|
54
55
|
import { createMovementSfxTick } from '../../../lib/sfx/movement-tick'
|
|
55
56
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
|
@@ -58,7 +59,6 @@ import {
|
|
|
58
59
|
surfaceFramePose,
|
|
59
60
|
updateSurfaceNode as writeSurfaceNode,
|
|
60
61
|
} from '../../../lib/surface-attachment'
|
|
61
|
-
import { isTypingTarget } from '../../../lib/typing-target'
|
|
62
62
|
import { createWallPointerTracker } from '../../../lib/wall-placement-pointer'
|
|
63
63
|
import {
|
|
64
64
|
projectAlignmentGuidesWorldToActiveBuildingLocal,
|
|
@@ -73,7 +73,6 @@ import useEditor, {
|
|
|
73
73
|
import useFacingPose from '../../../store/use-facing-pose'
|
|
74
74
|
import { getMovingNode } from '../../../store/use-interaction-scope'
|
|
75
75
|
import usePlacementPreview from '../../../store/use-placement-preview'
|
|
76
|
-
import { Html } from '../../editor/localized-html'
|
|
77
76
|
import {
|
|
78
77
|
createItemSurfaceGridDispatch,
|
|
79
78
|
createItemSurfacePointerArbitration,
|
|
@@ -4,12 +4,18 @@ import {
|
|
|
4
4
|
type AnyNode,
|
|
5
5
|
type AnyNodeId,
|
|
6
6
|
emitter,
|
|
7
|
+
formatLinearMeasurement,
|
|
7
8
|
sceneRegistry,
|
|
8
9
|
useScene,
|
|
9
10
|
type WallFace,
|
|
10
11
|
type WallNode,
|
|
11
12
|
} from '@pascal-app/core'
|
|
12
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getSceneTheme,
|
|
15
|
+
isTypingTarget,
|
|
16
|
+
setSurfaceRaycastLayers,
|
|
17
|
+
useViewer,
|
|
18
|
+
} from '@pascal-app/viewer'
|
|
13
19
|
import { useFrame, useThree } from '@react-three/fiber'
|
|
14
20
|
import { useEffect, useMemo, useRef } from 'react'
|
|
15
21
|
import {
|
|
@@ -27,9 +33,7 @@ import {
|
|
|
27
33
|
import { LineBasicNodeMaterial, MeshBasicNodeMaterial } from 'three/webgpu'
|
|
28
34
|
import { markToolCancelConsumed } from '../../../hooks/use-keyboard'
|
|
29
35
|
import { EDITOR_LAYER } from '../../../lib/constants'
|
|
30
|
-
import { formatLinearMeasurement } from '../../../lib/measurements'
|
|
31
36
|
import { paintRegionTargets, usePaintRegionMode } from '../../../lib/paint-region-mode'
|
|
32
|
-
import { isTypingTarget } from '../../../lib/typing-target'
|
|
33
37
|
import {
|
|
34
38
|
activeWallRegionGesture,
|
|
35
39
|
activeWallRegionTarget,
|
|
@@ -107,6 +107,7 @@ test('focus completion: mounted registry mover owns only outside R T and Alt', a
|
|
|
107
107
|
cancelAnimationFrame: { configurable: true, value: (key: number) => frames.delete(key) },
|
|
108
108
|
IS_REACT_ACT_ENVIRONMENT: { configurable: true, value: true },
|
|
109
109
|
})
|
|
110
|
+
let supportY = 0
|
|
110
111
|
core.registerNode({
|
|
111
112
|
kind: node.type,
|
|
112
113
|
schemaVersion: 1,
|
|
@@ -114,7 +115,14 @@ test('focus completion: mounted registry mover owns only outside R T and Alt', a
|
|
|
114
115
|
schema: z.object({ type: z.literal(node.type) }).passthrough(),
|
|
115
116
|
defaults: () => ({}),
|
|
116
117
|
capabilities: {
|
|
117
|
-
movable: {
|
|
118
|
+
movable: {
|
|
119
|
+
isValidPosition: () => false,
|
|
120
|
+
resolvePosition: ({ position }: { position: [number, number, number] }) => [
|
|
121
|
+
position[0],
|
|
122
|
+
supportY,
|
|
123
|
+
position[2],
|
|
124
|
+
],
|
|
125
|
+
},
|
|
118
126
|
floorPlaced: {
|
|
119
127
|
collides: true,
|
|
120
128
|
footprint: (candidate: AnyNode) => ({
|
|
@@ -251,6 +259,39 @@ test('focus completion: mounted registry mover owns only outside R T and Alt', a
|
|
|
251
259
|
expect((await key('t', false)).defaultPrevented).toBe(true)
|
|
252
260
|
expect(mesh.rotation.y).toBeCloseTo(0.25)
|
|
253
261
|
expect(core.useLiveTransforms.getState().transforms.get(id)?.rotation).toBeCloseTo(0.25)
|
|
262
|
+
supportY = 0.75
|
|
263
|
+
await act(async () => {
|
|
264
|
+
core.emitter.emit('grid:move', {
|
|
265
|
+
nativeEvent: {
|
|
266
|
+
target: { closest: () => null },
|
|
267
|
+
} as unknown as import('@pascal-app/core').GridEvent['nativeEvent'],
|
|
268
|
+
position: [7, 0, 3],
|
|
269
|
+
localPosition: [7, 0, 3],
|
|
270
|
+
stopPropagation() {},
|
|
271
|
+
} as import('@pascal-app/core').GridEvent)
|
|
272
|
+
flushFrames()
|
|
273
|
+
})
|
|
274
|
+
expect(mesh.position.y).toBeCloseTo(0.75)
|
|
275
|
+
expect(core.useLiveTransforms.getState().transforms.get(id)?.position?.[1]).toBeCloseTo(0.75)
|
|
276
|
+
expect(
|
|
277
|
+
(core.useScene.getState().nodes[id] as unknown as { position: number[] }).position[1],
|
|
278
|
+
).toBe(0)
|
|
279
|
+
supportY = 1.25
|
|
280
|
+
await key('Alt', false)
|
|
281
|
+
await act(async () => {
|
|
282
|
+
core.emitter.emit('grid:click', {
|
|
283
|
+
nativeEvent: {
|
|
284
|
+
target: { closest: () => null },
|
|
285
|
+
} as unknown as import('@pascal-app/core').GridEvent['nativeEvent'],
|
|
286
|
+
position: [7, 0, 3],
|
|
287
|
+
localPosition: [7, 0, 3],
|
|
288
|
+
stopPropagation() {},
|
|
289
|
+
} as import('@pascal-app/core').GridEvent)
|
|
290
|
+
flushFrames()
|
|
291
|
+
})
|
|
292
|
+
expect(
|
|
293
|
+
(core.useScene.getState().nodes[id] as unknown as { position: number[] }).position[1],
|
|
294
|
+
).toBeCloseTo(1.25)
|
|
254
295
|
} finally {
|
|
255
296
|
if (root)
|
|
256
297
|
await act(async () => {
|