@pascal-app/editor 1.0.5 → 1.0.7
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 +20 -17
- package/src/components/editor/action-menu-button.tsx +4 -1
- package/src/components/editor/alignment-3d-guide-layer.tsx +102 -11
- package/src/components/editor/bake-thumbnail.tsx +3 -2
- package/src/components/editor/custom-camera-controls.tsx +20 -8
- package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
- package/src/components/editor/first-person-controls.tsx +72 -61
- package/src/components/editor/floating-action-menu.tsx +27 -9
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floorplan-panel.test.ts +220 -0
- package/src/components/editor/floorplan-panel.tsx +285 -132
- package/src/components/editor/footprint-height-handle.tsx +1 -1
- package/src/components/editor/grid.tsx +24 -15
- package/src/components/editor/group-actions.ts +19 -1
- package/src/components/editor/group-floating-action-menu.tsx +1 -1
- package/src/components/editor/group-move-3d.ts +27 -2
- package/src/components/editor/group-selection-box-3d.tsx +4 -1
- package/src/components/editor/group-transform-shared.ts +20 -0
- package/src/components/editor/index.tsx +130 -20
- package/src/components/editor/inspector-extension-actions.tsx +36 -0
- package/src/components/editor/localized-html.tsx +22 -0
- package/src/components/editor/node-action-menu.tsx +46 -12
- package/src/components/editor/node-arrow-handles.tsx +1 -2
- package/src/components/editor/open-wall-ends-3d-layer.tsx +1 -1
- package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
- package/src/components/editor/registry-action-contributions.test.tsx +67 -6
- package/src/components/editor/registry-action-contributions.tsx +115 -15
- package/src/components/editor/riser-diagram-panel.tsx +59 -9
- package/src/components/editor/room-controls.tsx +1 -1
- package/src/components/editor/room-handles-2d.tsx +156 -6
- package/src/components/editor/room-handles.tsx +241 -51
- package/src/components/editor/room-highlight.tsx +2 -0
- package/src/components/editor/room-labels.tsx +29 -15
- package/src/components/editor/selection-manager.tsx +15 -102
- package/src/components/editor/separator-end-grip.tsx +74 -0
- package/src/components/editor/site-edge-labels.tsx +1 -1
- package/src/components/editor/thumbnail-generator.test.ts +15 -6
- package/src/components/editor/thumbnail-generator.tsx +26 -1
- package/src/components/editor/use-floorplan-background-placement.ts +55 -52
- package/src/components/editor/views/view-bar.tsx +111 -26
- package/src/components/editor/wall-measurement-label.tsx +1 -1
- package/src/components/editor/wall-move-side-handles.tsx +5 -1
- package/src/components/editor/wall-pull-grip.tsx +193 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +81 -1
- package/src/components/editor-2d/floorplan-adjacent-walls-layer.test.ts +76 -0
- package/src/components/editor-2d/floorplan-adjacent-walls-layer.tsx +186 -0
- package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +63 -3
- package/src/components/editor-2d/floorplan-group-move.tsx +31 -3
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +6 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +22 -13
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +11 -3
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +7 -1
- package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +96 -4
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +52 -12
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +12 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +197 -37
- package/src/components/procedural-items/inspector.tsx +59 -12
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +2 -0
- package/src/components/systems/ceiling/ceiling-system.test.ts +141 -0
- package/src/components/systems/ceiling/ceiling-system.tsx +53 -18
- package/src/components/tools/distribution/distribution-run-tool.tsx +50 -26
- package/src/components/tools/item/placement-history.test.tsx +1 -1
- package/src/components/tools/item/placement-strategies.test.ts +64 -1
- package/src/components/tools/item/placement-strategies.ts +5 -9
- package/src/components/tools/item/use-placement-coordinator.tsx +30 -34
- package/src/components/tools/registry/item-surface-move.test.ts +11 -0
- package/src/components/tools/select/select-candidates.test.ts +45 -0
- package/src/components/tools/select/select-candidates.ts +13 -2
- package/src/components/tools/shared/cursor-sphere.tsx +10 -3
- package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
- package/src/components/tools/shared/placement-box.tsx +1 -1
- package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
- package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
- package/src/components/tools/wall/wall-drafting.test.ts +299 -23
- package/src/components/tools/wall/wall-drafting.ts +132 -24
- package/src/components/tools/wall/wall-pull-snap.test.ts +274 -0
- package/src/components/tools/wall/wall-pull-snap.ts +563 -0
- package/src/components/tools/wall/wall-snap-geometry.test.ts +113 -1
- package/src/components/tools/wall/wall-snap-geometry.ts +137 -0
- package/src/components/tools/zone/zone-tool.tsx +3 -3
- package/src/components/ui/action-menu/action-button.tsx +2 -0
- package/src/components/ui/action-menu/camera-actions.tsx +8 -7
- package/src/components/ui/action-menu/control-modes.tsx +9 -5
- package/src/components/ui/action-menu/follow-pascal-pill.tsx +3 -1
- package/src/components/ui/action-menu/follow-pascal-toggle.tsx +7 -1
- package/src/components/ui/action-menu/furnish-tools.tsx +7 -6
- package/src/components/ui/action-menu/index.tsx +1 -0
- package/src/components/ui/action-menu/measurement-control.tsx +35 -29
- package/src/components/ui/action-menu/point-ask-button.tsx +4 -3
- package/src/components/ui/action-menu/structure-tools.tsx +25 -23
- package/src/components/ui/action-menu/view-toggles.test.ts +18 -0
- package/src/components/ui/action-menu/view-toggles.tsx +256 -51
- package/src/components/ui/build-panel/build-panel.tsx +163 -25
- package/src/components/ui/command-palette/editor-commands.tsx +38 -11
- package/src/components/ui/controls/finish-swatch.tsx +45 -19
- package/src/components/ui/controls/material-picker.tsx +24 -17
- package/src/components/ui/controls/metric-control.test.ts +52 -0
- package/src/components/ui/controls/metric-control.tsx +62 -24
- package/src/components/ui/controls/segmented-control.tsx +5 -2
- package/src/components/ui/controls/slider-control.test.tsx +23 -0
- package/src/components/ui/controls/slider-control.tsx +30 -14
- package/src/components/ui/floating-level-selector.tsx +100 -48
- package/src/components/ui/helpers/building-helper.tsx +5 -3
- package/src/components/ui/helpers/contextual-helper-panel.tsx +97 -37
- package/src/components/ui/helpers/helper-manager.test.tsx +53 -3
- package/src/components/ui/helpers/helper-manager.tsx +82 -57
- package/src/components/ui/helpers/item-helper.tsx +6 -4
- package/src/components/ui/helpers/registered-tool-helper.tsx +3 -1
- package/src/components/ui/level-duplicate-dialog.tsx +32 -23
- package/src/components/ui/level-wall-copy-prompt.tsx +124 -0
- package/src/components/ui/panels/collections/collections-popover.tsx +11 -11
- package/src/components/ui/panels/deck-boards-fields.tsx +136 -0
- package/src/components/ui/panels/floor-foundation-panel.tsx +113 -38
- package/src/components/ui/panels/floor-opening-panel.tsx +73 -25
- package/src/components/ui/panels/inspector-extensions.tsx +66 -0
- package/src/components/ui/panels/mobile-panel-sheet.tsx +3 -1
- package/src/components/ui/panels/mobile-selection-bar.tsx +8 -6
- package/src/components/ui/panels/multi-field-value.test.ts +27 -0
- package/src/components/ui/panels/multi-field-value.ts +20 -1
- package/src/components/ui/panels/multi-height-mode.tsx +8 -7
- package/src/components/ui/panels/multi-parametric-inspector.tsx +122 -34
- package/src/components/ui/panels/multi-selection-panel.tsx +75 -35
- package/src/components/ui/panels/node-display.ts +35 -24
- package/src/components/ui/panels/panel-manager.tsx +5 -3
- package/src/components/ui/panels/panel-wrapper.tsx +22 -109
- package/src/components/ui/panels/parametric-field-control.tsx +13 -10
- package/src/components/ui/panels/parametric-inspector.tsx +18 -12
- package/src/components/ui/panels/reference-panel.tsx +51 -35
- package/src/components/ui/panels/room-construction-rows.tsx +464 -65
- package/src/components/ui/panels/room-name-options.test.ts +36 -3
- package/src/components/ui/panels/room-name-options.ts +110 -11
- package/src/components/ui/panels/room-openings-field.tsx +21 -12
- package/src/components/ui/panels/room-panel.tsx +206 -99
- package/src/components/ui/panels/shape-choice.tsx +7 -5
- package/src/components/ui/panels/wall-region-list.tsx +7 -7
- package/src/components/ui/primitives/shortcut-token.tsx +10 -6
- package/src/components/ui/sidebar/icon-rail.tsx +11 -9
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +10 -10
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +10 -9
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +3 -0
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +102 -32
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +4 -3
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +7 -5
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/default-names.ts +75 -0
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +117 -64
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +36 -3
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +7 -4
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +8 -2
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +8 -5
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +8 -3
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +8 -7
- package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +7 -6
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +16 -1
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +15 -8
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-membership-checkbox.tsx +3 -1
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +18 -8
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +25 -14
- package/src/components/ui/sidebar/tab-bar.tsx +4 -1
- package/src/components/ui/sidebar/use-plugin-panels.tsx +19 -10
- package/src/components/ui/snap-target-badge.tsx +7 -6
- package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +721 -0
- package/src/components/ui/wall-type/wall-type-menu.tsx +167 -0
- package/src/components/viewer/floorplan-compass-button.tsx +12 -3
- package/src/components/viewer/floorplan-preview.tsx +27 -19
- package/src/components/viewer/render-settings-menu.tsx +294 -0
- package/src/components/viewer/sky-control.tsx +710 -0
- package/src/components/viewer/use-viewer-ui-sounds.test.ts +15 -0
- package/src/components/viewer/use-viewer-ui-sounds.ts +46 -0
- package/src/components/viewer/viewer-controls-bar.tsx +278 -260
- package/src/components/viewer/viewer-hover-label.tsx +12 -4
- package/src/components/viewer/viewer-rooms.tsx +61 -4
- package/src/components/viewer/viewer-scene-header.tsx +28 -17
- package/src/components/viewer/viewer-stage-switcher.tsx +4 -2
- package/src/components/viewer-overlay.tsx +8 -0
- package/src/components/walkthrough-hud.tsx +75 -2
- package/src/hooks/use-keyboard.test.ts +85 -4
- package/src/hooks/use-keyboard.ts +65 -7
- package/src/hooks/use-selected-room.ts +12 -1
- package/src/i18n/floor-footprint-name.ts +30 -0
- package/src/i18n/level-name.ts +39 -0
- package/src/i18n/locales/en/messages.po +15117 -0
- package/src/i18n/locales/en/messages.ts +1 -0
- package/src/i18n/locales/en-XA/messages.ts +1 -0
- package/src/i18n/locales/es/messages.po +37501 -0
- package/src/i18n/locales/es/messages.ts +1 -0
- package/src/i18n/locales/fr/messages.po +37501 -0
- package/src/i18n/locales/fr/messages.ts +1 -0
- package/src/i18n/locales/ja/messages.po +37501 -0
- package/src/i18n/locales/ja/messages.ts +1 -0
- package/src/i18n/locales/zh-CN/messages.po +37501 -0
- package/src/i18n/locales/zh-CN/messages.ts +1 -0
- package/src/i18n/locales.test.ts +63 -0
- package/src/i18n/locales.ts +32 -0
- package/src/i18n/message-slot.tsx +25 -0
- package/src/i18n/node-messages.ts +169 -0
- package/src/i18n/pseudo-locale.ts +17 -0
- package/src/i18n/published-entry.test.ts +17 -0
- package/src/i18n/scene-theme-name.ts +16 -0
- package/src/index.tsx +156 -4
- package/src/lib/add-storey.test.ts +3 -3
- package/src/lib/contextual-help.test.ts +20 -1
- package/src/lib/contextual-help.ts +95 -19
- package/src/lib/continuation.ts +38 -11
- package/src/lib/floor-footprints.test.ts +2 -1
- package/src/lib/floor-foundation-support.test.ts +7 -3
- package/src/lib/floorplan/dimension-drive.test.ts +36 -2
- package/src/lib/floorplan/dimension-drive.ts +68 -3
- package/src/lib/floorplan/floorplan-export.test.ts +16 -5
- package/src/lib/floorplan/floorplan-export.tsx +13 -16
- package/src/lib/floorplan/floorplan-extension.test.ts +11 -12
- package/src/lib/floorplan/floorplan-extension.ts +37 -18
- package/src/lib/floorplan/floorplan-mode.test.ts +13 -6
- package/src/lib/floorplan/floorplan-mode.ts +14 -12
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +1 -1
- package/src/lib/floorplan/floorplan-readonly.ts +3 -3
- package/src/lib/floorplan/schedules.ts +10 -6
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +46 -1
- package/src/lib/floorplan/site-plan/site-annotations.ts +3 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +6 -3
- package/src/lib/geometry-script/author.test.ts +114 -0
- package/src/lib/geometry-script/author.ts +55 -23
- package/src/lib/geometry-script/client.ts +3 -0
- package/src/lib/geometry-script/finish-scripts.ts +7 -7
- package/src/lib/glb-export.test.ts +327 -0
- package/src/lib/glb-export.ts +70 -2
- package/src/lib/grid-frame.test.ts +56 -3
- package/src/lib/grid-frame.ts +28 -1
- package/src/lib/history.test.ts +2 -1
- package/src/lib/hud-title.ts +48 -24
- package/src/lib/ifc-export.test.ts +41 -1
- package/src/lib/inspector-card-mode.test.ts +3 -31
- package/src/lib/inspector-card-mode.ts +5 -10
- package/src/lib/inspector-expanded.test.ts +13 -0
- package/src/lib/inspector-expanded.ts +13 -1
- package/src/lib/keyboard-pan.test.ts +7 -0
- package/src/lib/keyboard-pan.ts +9 -2
- package/src/lib/layer-thickness.test.ts +22 -0
- package/src/lib/layer-thickness.ts +79 -0
- package/src/lib/lot/drop-in.test.ts +59 -1
- package/src/lib/lot/drop-in.ts +42 -50
- package/src/lib/material-swatch-style.ts +24 -0
- package/src/lib/measurement-parser.ts +7 -3
- package/src/lib/measurements.test.ts +17 -0
- package/src/lib/measurements.ts +20 -5
- package/src/lib/mezzanine-selection.test.ts +3 -1
- package/src/lib/orbit-near-plane.test.ts +12 -0
- package/src/lib/orbit-near-plane.ts +13 -0
- package/src/lib/paint-regions.test.ts +8 -5
- package/src/lib/paint-scope.test.ts +53 -0
- package/src/lib/paint-scope.ts +18 -4
- package/src/lib/panel-rows.ts +17 -8
- package/src/lib/reference-scale.ts +83 -0
- package/src/lib/room-built-on.test.ts +26 -12
- package/src/lib/room-built-on.ts +20 -2
- package/src/lib/room-construction.test.ts +28 -1
- package/src/lib/room-construction.ts +14 -0
- package/src/lib/room-controls-phase6.test.ts +140 -0
- package/src/lib/room-handle-drag.ts +557 -42
- package/src/lib/room-name-catalog.test.ts +8 -0
- package/src/lib/room-name-catalog.ts +8 -1
- package/src/lib/room-name-messages.test.ts +132 -0
- package/src/lib/room-name-messages.ts +147 -0
- package/src/lib/room-name-storage.test.ts +334 -0
- package/src/lib/room-push-dimensions.test.ts +37 -0
- package/src/lib/room-push-dimensions.ts +226 -2
- package/src/lib/room-selection.test.ts +23 -6
- package/src/lib/scene-clipboard.test.ts +70 -49
- package/src/lib/scene-clipboard.ts +9 -4
- package/src/lib/selection-cycle.ts +63 -0
- package/src/lib/separator-edit.test.ts +431 -0
- package/src/lib/separator-edit.ts +179 -0
- package/src/lib/separator-lines.ts +38 -0
- package/src/lib/sfx-bus.ts +27 -1
- package/src/lib/sfx-player.ts +16 -0
- package/src/lib/tool-cursor-icon.ts +5 -1
- package/src/lib/tool-shortcuts.ts +130 -0
- package/src/lib/viewer-selection.test.ts +28 -24
- package/src/lib/viewer-selection.ts +20 -5
- package/src/lib/wall-draw-variant.test.ts +7 -6
- package/src/lib/wall-draw-variant.ts +107 -6
- package/src/lib/wall-nudge.test.ts +44 -0
- package/src/lib/wall-nudge.ts +131 -0
- package/src/lib/wall-parallel-snap.test.ts +83 -0
- package/src/lib/wall-parallel-snap.ts +176 -0
- package/src/lib/wall-placement-pointer.test.ts +23 -0
- package/src/lib/wall-placement-pointer.ts +18 -0
- package/src/lib/wall-pull.test.ts +245 -0
- package/src/lib/wall-pull.ts +209 -0
- package/src/lib/wall-push-line-up.test.ts +132 -0
- package/src/lib/wall-region-list.ts +14 -5
- package/src/lib/wall-run-snap.test.ts +188 -0
- package/src/lib/wall-run-snap.ts +83 -0
- package/src/lib/wall-type.test.ts +125 -0
- package/src/lib/wall-type.ts +119 -0
- package/src/lib/window-interaction.test.ts +42 -0
- package/src/lib/window-interaction.ts +47 -5
- package/src/lib/world-grid-snap.test.ts +139 -2
- package/src/lib/world-grid-snap.ts +102 -105
- package/src/store/use-alignment-guides.ts +27 -1
- package/src/store/use-editor.tsx +48 -0
- package/src/store/use-floorplan-annotation-visibility.test.ts +64 -0
- package/src/store/use-floorplan-annotation-visibility.ts +47 -22
- package/src/store/use-wall-snap-indicator.ts +15 -2
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
useScene,
|
|
11
11
|
} from '@pascal-app/core'
|
|
12
12
|
import { useViewer } from '@pascal-app/viewer'
|
|
13
|
-
import { Html } from '@react-three/drei'
|
|
14
13
|
import { type ThreeEvent, useFrame, useThree } from '@react-three/fiber'
|
|
15
14
|
import { useRef, useState } from 'react'
|
|
16
15
|
import { type Group, Plane, Vector2, Vector3 } from 'three'
|
|
@@ -26,6 +25,7 @@ import { runRoomHandleDrag } from '../../lib/room-handle-drag'
|
|
|
26
25
|
import useEditor, { isGridSnapActive } from '../../store/use-editor'
|
|
27
26
|
import useInteractionScope from '../../store/use-interaction-scope'
|
|
28
27
|
import { suppressBoxSelectForPointer } from '../tools/select/box-select-state'
|
|
28
|
+
import { Html } from './localized-html'
|
|
29
29
|
import { ElevationArrows, outlineCentroid, useHandleScale } from './room-handles'
|
|
30
30
|
|
|
31
31
|
export const FOOTPRINT_HEIGHT_DRAG_LABEL = 'footprint-height'
|
|
@@ -15,7 +15,8 @@ import { MeshBasicNodeMaterial } from 'three/webgpu'
|
|
|
15
15
|
import { useCeilingEvents } from '../../hooks/use-ceiling-events'
|
|
16
16
|
import { useGridEvents } from '../../hooks/use-grid-events'
|
|
17
17
|
import { getPlacementSurface, usesOrientedPlacementPlane } from '../../lib/active-placement-surface'
|
|
18
|
-
import { gridLocalNormal, gridLocalPoint } from '../../lib/grid-frame'
|
|
18
|
+
import { gridLocalNormal, gridLocalPoint, layHorizontalGrid } from '../../lib/grid-frame'
|
|
19
|
+
import { getActiveGridPose } from '../../lib/world-grid-snap'
|
|
19
20
|
import useEditor, { getActiveSnapContext, isGridSnapActive } from '../../store/use-editor'
|
|
20
21
|
import { getMovingNode } from '../../store/use-interaction-scope'
|
|
21
22
|
|
|
@@ -27,10 +28,9 @@ export const EDITOR_GRID_INPUT_NAME = 'pascal-editor-grid-input'
|
|
|
27
28
|
const PLACEMENT_REVEAL_RADIUS = 12
|
|
28
29
|
|
|
29
30
|
const UP = new Vector3(0, 1, 0)
|
|
30
|
-
// PlaneGeometry faces +Z;
|
|
31
|
-
//
|
|
31
|
+
// PlaneGeometry faces +Z; the oriented (wall-plane) lattice turns this normal
|
|
32
|
+
// into the host's. The flat lattice is laid by `layHorizontalGrid`.
|
|
32
33
|
const PLANE_LOCAL_NORMAL = new Vector3(0, 0, 1)
|
|
33
|
-
const HORIZONTAL_QUATERNION = new Quaternion().setFromUnitVectors(PLANE_LOCAL_NORMAL, UP)
|
|
34
34
|
|
|
35
35
|
export const Grid = ({
|
|
36
36
|
cellSize = 0.5,
|
|
@@ -184,10 +184,10 @@ export const Grid = ({
|
|
|
184
184
|
useCeilingEvents()
|
|
185
185
|
|
|
186
186
|
// Track the last world-space cursor hit. The reveal-fade shader reads
|
|
187
|
-
// `positionLocal.xy` (vertex position on the un-transformed plane),
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
187
|
+
// `positionLocal.xy` (vertex position on the un-transformed plane), so the
|
|
188
|
+
// cursor is taken into the plane's frame every frame from the stored world
|
|
189
|
+
// hit — the reveal stays put wherever the mesh origin sits and however it
|
|
190
|
+
// is turned.
|
|
191
191
|
const lastWorldCursorRef = useRef<Vector3 | null>(null)
|
|
192
192
|
useEffect(() => {
|
|
193
193
|
const onGridMove = (event: GridEvent) => {
|
|
@@ -279,20 +279,29 @@ export const Grid = ({
|
|
|
279
279
|
setGridY(surfacePoint.y)
|
|
280
280
|
}
|
|
281
281
|
} else {
|
|
282
|
-
// Horizontal:
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
//
|
|
282
|
+
// Horizontal: lay the lattice on the frame the tools snap in — the
|
|
283
|
+
// active building's yaw and offset, so a turned house's walls sit square
|
|
284
|
+
// on it, or world XZ (0,0) for site work; only the Y origin follows the
|
|
285
|
+
// surface height (floor / shelf top). Snap directly — the old lerp made
|
|
286
|
+
// the grid visibly drift up to a new floor height.
|
|
286
287
|
const targetY = surfacePoint ? surfacePoint.y : levelY
|
|
287
288
|
// Visual mesh rides 1mm above the surface: at exactly the surface Y the
|
|
288
289
|
// lattice is coplanar with the slab top and z-fights it. The event plane
|
|
289
290
|
// (`gridY` → useGridEvents) stays at the true surface height.
|
|
290
|
-
|
|
291
|
-
|
|
291
|
+
layHorizontalGrid(
|
|
292
|
+
getActiveGridPose(),
|
|
293
|
+
targetY + 0.001,
|
|
294
|
+
gridMesh.position,
|
|
295
|
+
gridMesh.quaternion,
|
|
296
|
+
)
|
|
297
|
+
// Cursor uniform → plane-local XY, as for the wall plane above.
|
|
292
298
|
const world = lastWorldCursorRef.current
|
|
293
299
|
if (world) {
|
|
300
|
+
invQuatRef.current.copy(gridMesh.quaternion).invert()
|
|
294
301
|
const local = gridLocalPoint(gridMesh.parent, world, localCursorRef.current)
|
|
295
|
-
|
|
302
|
+
.sub(gridMesh.position)
|
|
303
|
+
.applyQuaternion(invQuatRef.current)
|
|
304
|
+
cursorPositionRef.current.set(local.x, local.y)
|
|
296
305
|
}
|
|
297
306
|
if (lastGridYRef.current !== targetY) {
|
|
298
307
|
lastGridYRef.current = targetY
|
|
@@ -41,6 +41,7 @@ import useFloorplanMode from '../../store/use-floorplan-mode'
|
|
|
41
41
|
import useInteractionScope from '../../store/use-interaction-scope'
|
|
42
42
|
import { useFloorplanGroupDrag } from '../editor-2d/floorplan-group-move'
|
|
43
43
|
import {
|
|
44
|
+
beginGroupWallPush,
|
|
44
45
|
classifyParticipant,
|
|
45
46
|
collectParticipants,
|
|
46
47
|
computeGroupBox,
|
|
@@ -185,6 +186,9 @@ export function startGroupPickUp(
|
|
|
185
186
|
handle: GROUP_MOVE_DRAG_LABEL,
|
|
186
187
|
})
|
|
187
188
|
document.body.style.cursor = 'grabbing'
|
|
189
|
+
// Walls alone move as their push arrows do; copies (paste, duplicate) are
|
|
190
|
+
// carried rigidly, joined to nothing yet.
|
|
191
|
+
const push = opts.scopeToSelection ? null : beginGroupWallPush(starts, levelId)
|
|
188
192
|
|
|
189
193
|
const applyMove = (e: PointerEvent) => {
|
|
190
194
|
const plan = resolvePlanPoint(e)
|
|
@@ -202,7 +206,8 @@ export function startGroupPickUp(
|
|
|
202
206
|
let dx = snap ? Math.round(rawDx / step) * step : rawDx
|
|
203
207
|
let dz = snap ? Math.round(rawDz / step) * step : rawDz
|
|
204
208
|
|
|
205
|
-
|
|
209
|
+
// Pushed walls don't move as one footprint, so they have no box to align.
|
|
210
|
+
if (!push && isAlignmentGuideActive() && candidates.length > 0 && restAnchors.length > 0) {
|
|
206
211
|
const result = resolveAlignment({
|
|
207
212
|
moving: restAnchors.map((a) => ({ ...a, x: a.x + dx, z: a.z + dz })),
|
|
208
213
|
candidates,
|
|
@@ -225,6 +230,10 @@ export function startGroupPickUp(
|
|
|
225
230
|
sfxEmitter.emit('sfx:grid-snap')
|
|
226
231
|
lastDelta = [dx, dz]
|
|
227
232
|
}
|
|
233
|
+
if (push) {
|
|
234
|
+
push.preview([dx, dz])
|
|
235
|
+
return
|
|
236
|
+
}
|
|
228
237
|
|
|
229
238
|
const entries = translateGroupPatches(starts, links, dx, dz)
|
|
230
239
|
const patchById = new Map(entries)
|
|
@@ -252,6 +261,8 @@ export function startGroupPickUp(
|
|
|
252
261
|
// the current delta — the carried group turns exactly like the idle
|
|
253
262
|
// keyboard rotate, and the placement stays a single updateNodes.
|
|
254
263
|
const rotateCarried = (direction: 1 | -1) => {
|
|
264
|
+
// Pushed walls keep their bearing: there is nothing to turn.
|
|
265
|
+
if (push) return
|
|
255
266
|
const pivot = { x: restCenter[0], z: restCenter[1] }
|
|
256
267
|
const delta = -direction * (Math.PI / 4)
|
|
257
268
|
const rotated = rotateGroupSnapshots(starts, links, pivot, delta)
|
|
@@ -271,6 +282,7 @@ export function startGroupPickUp(
|
|
|
271
282
|
}
|
|
272
283
|
|
|
273
284
|
const clearLivePreviews = () => {
|
|
285
|
+
push?.cancel()
|
|
274
286
|
const overrides = useLiveNodeOverrides.getState()
|
|
275
287
|
const liveTransforms = useLiveTransforms.getState()
|
|
276
288
|
for (const id of affectedIds) {
|
|
@@ -303,6 +315,12 @@ export function startGroupPickUp(
|
|
|
303
315
|
const commit = () => {
|
|
304
316
|
swallowNextClick()
|
|
305
317
|
sfxEmitter.emit('sfx:item-place')
|
|
318
|
+
if (push) {
|
|
319
|
+
resumeSceneHistory(useScene)
|
|
320
|
+
push.commit()
|
|
321
|
+
teardown()
|
|
322
|
+
return
|
|
323
|
+
}
|
|
306
324
|
const overrides = useLiveNodeOverrides.getState()
|
|
307
325
|
const updates: { id: AnyNodeId; data: Partial<AnyNode> }[] = []
|
|
308
326
|
for (const id of affectedIds) {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { type AnyNodeId, useScene } from '@pascal-app/core'
|
|
4
4
|
import { useViewer } from '@pascal-app/viewer'
|
|
5
|
-
import { Html } from '@react-three/drei'
|
|
6
5
|
import { useFrame } from '@react-three/fiber'
|
|
7
6
|
import { useCallback, useMemo, useRef } from 'react'
|
|
8
7
|
import * as THREE from 'three'
|
|
@@ -21,6 +20,7 @@ import {
|
|
|
21
20
|
computeGroupPlanBox,
|
|
22
21
|
levelFrame,
|
|
23
22
|
} from './group-transform-shared'
|
|
23
|
+
import { Html } from './localized-html'
|
|
24
24
|
import { NodeActionMenu } from './node-action-menu'
|
|
25
25
|
import { useMeshSettleEpoch } from './use-mesh-settle-epoch'
|
|
26
26
|
|
|
@@ -16,6 +16,7 @@ import { useViewer } from '@pascal-app/viewer'
|
|
|
16
16
|
import { type Camera, Plane, type Raycaster, Vector2, Vector3 } from 'three'
|
|
17
17
|
import { GROUP_MOVE_DRAG_LABEL } from '../../lib/contextual-help'
|
|
18
18
|
import { isHistoryShortcut } from '../../lib/history'
|
|
19
|
+
import type { WallGroupPush } from '../../lib/room-handle-drag'
|
|
19
20
|
import { sfxEmitter } from '../../lib/sfx-bus'
|
|
20
21
|
import { isTypingTarget } from '../../lib/typing-target'
|
|
21
22
|
import useAlignmentGuides from '../../store/use-alignment-guides'
|
|
@@ -29,6 +30,7 @@ import { useFloorplanGroupDrag } from '../editor-2d/floorplan-group-move'
|
|
|
29
30
|
import { suppressBoxSelectForPointer } from '../tools/select/box-select-state'
|
|
30
31
|
import { startGroupPickUp } from './group-actions'
|
|
31
32
|
import {
|
|
33
|
+
beginGroupWallPush,
|
|
32
34
|
classifyParticipant,
|
|
33
35
|
collectParticipants,
|
|
34
36
|
computeGroupBox,
|
|
@@ -99,6 +101,8 @@ export function armGroupMove3d(args: {
|
|
|
99
101
|
startLocal: Vector3
|
|
100
102
|
frameInv: ReturnType<typeof levelFrame>['inverse']
|
|
101
103
|
lastDelta: Vec2 | null
|
|
104
|
+
/** Walls alone: each moves as its push arrow does instead of the rigid slide. */
|
|
105
|
+
push: WallGroupPush | null
|
|
102
106
|
}
|
|
103
107
|
let session: Session | null = null
|
|
104
108
|
|
|
@@ -179,6 +183,7 @@ export function armGroupMove3d(args: {
|
|
|
179
183
|
startLocal,
|
|
180
184
|
frameInv,
|
|
181
185
|
lastDelta: null,
|
|
186
|
+
push: beginGroupWallPush(starts, levelId),
|
|
182
187
|
}
|
|
183
188
|
}
|
|
184
189
|
|
|
@@ -202,8 +207,14 @@ export function armGroupMove3d(args: {
|
|
|
202
207
|
: moveLocal.z - s.startLocal.z
|
|
203
208
|
|
|
204
209
|
// Figma-style alignment layered on top: guides display in every mode
|
|
205
|
-
// except Off; the magnetic pull applies only in 'lines' mode.
|
|
206
|
-
|
|
210
|
+
// except Off; the magnetic pull applies only in 'lines' mode. Pushed walls
|
|
211
|
+
// don't move as one footprint, so they have no box to align.
|
|
212
|
+
if (
|
|
213
|
+
!s.push &&
|
|
214
|
+
isAlignmentGuideActive() &&
|
|
215
|
+
s.candidates.length > 0 &&
|
|
216
|
+
s.restAnchors.length > 0
|
|
217
|
+
) {
|
|
207
218
|
const result = resolveAlignment({
|
|
208
219
|
moving: s.restAnchors.map((a) => ({ ...a, x: a.x + dx, z: a.z + dz })),
|
|
209
220
|
candidates: s.candidates,
|
|
@@ -227,6 +238,10 @@ export function armGroupMove3d(args: {
|
|
|
227
238
|
sfxEmitter.emit('sfx:grid-snap')
|
|
228
239
|
s.lastDelta = [dx, dz]
|
|
229
240
|
}
|
|
241
|
+
if (s.push) {
|
|
242
|
+
s.push.preview([dx, dz])
|
|
243
|
+
return
|
|
244
|
+
}
|
|
230
245
|
|
|
231
246
|
const entries = translateGroupPatches(s.starts, s.links, dx, dz)
|
|
232
247
|
const patchById = new Map(entries)
|
|
@@ -255,6 +270,8 @@ export function armGroupMove3d(args: {
|
|
|
255
270
|
// current delta — the carried group turns exactly like the idle keyboard
|
|
256
271
|
// rotate, and the commit stays a single updateNodes.
|
|
257
272
|
const rotateSession = (s: Session, direction: 1 | -1) => {
|
|
273
|
+
// Pushed walls keep their bearing: there is nothing to turn.
|
|
274
|
+
if (s.push) return
|
|
258
275
|
const pivot = { x: s.restCenter[0], z: s.restCenter[1] }
|
|
259
276
|
const delta = -direction * (Math.PI / 4)
|
|
260
277
|
const rotated = rotateGroupSnapshots(s.starts, s.links, pivot, delta)
|
|
@@ -276,6 +293,7 @@ export function armGroupMove3d(args: {
|
|
|
276
293
|
}
|
|
277
294
|
|
|
278
295
|
const clearLivePreviews = (s: Session) => {
|
|
296
|
+
s.push?.cancel()
|
|
279
297
|
const overrides = useLiveNodeOverrides.getState()
|
|
280
298
|
const liveTransforms = useLiveTransforms.getState()
|
|
281
299
|
for (const id of s.affectedIds) {
|
|
@@ -342,6 +360,13 @@ export function armGroupMove3d(args: {
|
|
|
342
360
|
}
|
|
343
361
|
swallowNextClick()
|
|
344
362
|
sfxEmitter.emit('sfx:item-place')
|
|
363
|
+
if (session.push) {
|
|
364
|
+
resumeSceneHistory(useScene)
|
|
365
|
+
session.push.commit()
|
|
366
|
+
session = null
|
|
367
|
+
teardown()
|
|
368
|
+
return
|
|
369
|
+
}
|
|
345
370
|
const overrides = useLiveNodeOverrides.getState()
|
|
346
371
|
const updates: { id: AnyNodeId; data: Partial<AnyNode> }[] = []
|
|
347
372
|
for (const id of session.affectedIds) {
|
|
@@ -5,6 +5,7 @@ import { useViewer } from '@pascal-app/viewer'
|
|
|
5
5
|
import { type ThreeEvent, useThree } from '@react-three/fiber'
|
|
6
6
|
import { useEffect, useMemo } from 'react'
|
|
7
7
|
import * as THREE from 'three'
|
|
8
|
+
import { useRoomHandleDrag } from '../../lib/room-handle-drag'
|
|
8
9
|
import useEditor from '../../store/use-editor'
|
|
9
10
|
import { useMovingNode } from '../../store/use-interaction-scope'
|
|
10
11
|
import { useFloorplanGroupDrag } from '../editor-2d/floorplan-group-move'
|
|
@@ -38,6 +39,8 @@ export function GroupSelectionBox3D() {
|
|
|
38
39
|
const mode = useEditor((s) => s.mode)
|
|
39
40
|
const movingNode = useMovingNode()
|
|
40
41
|
const delta = useFloorplanGroupDrag((s) => s.delta)
|
|
42
|
+
// Pushed walls don't move as one box: the room widths they show take its place.
|
|
43
|
+
const wallPush = useRoomHandleDrag((s) => s.drag?.kind === 'wall-group-push')
|
|
41
44
|
const { camera, raycaster, gl } = useThree()
|
|
42
45
|
|
|
43
46
|
const participantIds = useMemo(
|
|
@@ -118,7 +121,7 @@ export function GroupSelectionBox3D() {
|
|
|
118
121
|
return new THREE.Vector3(delta[0], 0, delta[1]).applyMatrix4(matrix).sub(origin)
|
|
119
122
|
}, [delta, levelId])
|
|
120
123
|
|
|
121
|
-
if (!box || !edges || movingNode || mode !== 'select') return null
|
|
124
|
+
if (!box || !edges || movingNode || wallPush || mode !== 'select') return null
|
|
122
125
|
|
|
123
126
|
const handlePointerDown = (event: ThreeEvent<PointerEvent>) => {
|
|
124
127
|
const native = event.nativeEvent
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type WallNode,
|
|
11
11
|
} from '@pascal-app/core'
|
|
12
12
|
import { Box3, type BufferGeometry, Matrix4, type Object3D, Vector3 } from 'three'
|
|
13
|
+
import { beginWallGroupPush, type WallGroupPush } from '../../lib/room-handle-drag'
|
|
13
14
|
|
|
14
15
|
// Shared plumbing for the group transform gizmos (rotate + move). Both operate
|
|
15
16
|
// on the same multi-selection: classify each participant by how its placement
|
|
@@ -486,6 +487,25 @@ export function rotatePlanBounds(
|
|
|
486
487
|
return { minX, minZ, maxX, maxZ }
|
|
487
488
|
}
|
|
488
489
|
|
|
490
|
+
/**
|
|
491
|
+
* A group move of walls alone moves each wall as its push arrow does
|
|
492
|
+
* (`planWallGroupPush`) rather than sliding the selection rigidly: null for
|
|
493
|
+
* any other selection, which keeps the rigid slide. Doors and windows are not
|
|
494
|
+
* participants (they ride their wall), so they never make a selection mixed.
|
|
495
|
+
*/
|
|
496
|
+
export function beginGroupWallPush(
|
|
497
|
+
starts: readonly ParticipantStart[],
|
|
498
|
+
levelId: string | null,
|
|
499
|
+
): WallGroupPush | null {
|
|
500
|
+
const nodes = useScene.getState().nodes
|
|
501
|
+
if (!levelId || starts.length === 0) return null
|
|
502
|
+
if (!starts.every((s) => s.kind === 'endpoint' && nodes[s.id]?.type === 'wall')) return null
|
|
503
|
+
return beginWallGroupPush(
|
|
504
|
+
starts.map((s) => s.id),
|
|
505
|
+
levelId,
|
|
506
|
+
)
|
|
507
|
+
}
|
|
508
|
+
|
|
489
509
|
// Rigid group slide: shift every participant (and each linked neighbour's
|
|
490
510
|
// shared endpoint) by the same level-frame XZ delta. Y is untouched; the
|
|
491
511
|
// snapshot's rotation rides along because a mid-gesture R/T turns the
|