@pascal-app/editor 1.0.0-beta.5 → 1.0.0
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/LICENSE +21 -0
- package/bunfig.toml +4 -0
- package/package.json +13 -7
- package/scripts/generate-print-golden-house.ts +89 -0
- package/src/components/editor/bake-exporter.tsx +1 -0
- package/src/components/editor/camera-dragging-lifecycle.test.ts +41 -0
- package/src/components/editor/camera-dragging-lifecycle.ts +10 -2
- package/src/components/editor/capture-camera-rig.tsx +59 -0
- package/src/components/editor/custom-camera-controls.tsx +20 -11
- package/src/components/editor/editor-layout-mobile.tsx +6 -6
- package/src/components/editor/editor-layout-v2.tsx +9 -2
- package/src/components/editor/export-manager.tsx +175 -33
- package/src/components/editor/first-person/build-collider-world.test.ts +30 -0
- package/src/components/editor/first-person-controls.tsx +180 -12
- package/src/components/editor/floating-action-menu.tsx +42 -51
- package/src/components/editor/floorplan-panel.tsx +76 -12
- package/src/components/editor/grid.tsx +12 -7
- package/src/components/editor/group-actions.ts +34 -50
- package/src/components/editor/group-move-3d.ts +51 -21
- package/src/components/editor/group-transform-shared.test.ts +78 -0
- package/src/components/editor/group-transform-shared.ts +74 -7
- package/src/components/editor/handles/handle-arrow-raycast.test.ts +86 -0
- package/src/components/editor/handles/handle-arrow.tsx +109 -48
- package/src/components/editor/handles/linear-resize-drag.ts +67 -0
- package/src/components/editor/handles/resize-snap.test.ts +46 -0
- package/src/components/editor/handles/resize-snap.ts +10 -1
- package/src/components/editor/handles/use-handle-drag.ts +18 -1
- package/src/components/editor/index.tsx +140 -21
- package/src/components/editor/node-action-menu.tsx +14 -1
- package/src/components/editor/node-arrow-handles.tsx +47 -58
- package/src/components/editor/selection-manager.tsx +104 -42
- package/src/components/editor/snapshot-capture-overlay.tsx +298 -39
- package/src/components/editor/snapshot-capture.test.ts +422 -0
- package/src/components/editor/snapshot-capture.ts +145 -0
- package/src/components/editor/thumbnail-generator.tsx +284 -237
- package/src/components/editor/use-floorplan-background-placement.ts +53 -5
- package/src/components/editor/wall-move-side-handles.tsx +299 -7
- package/src/components/editor-2d/floorplan-group-move.tsx +67 -33
- package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +3 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +129 -38
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +14 -1
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +16 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +2 -0
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +52 -3
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +196 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +180 -32
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +402 -0
- package/src/components/systems/ceiling/ceiling-bracket-batch.ts +371 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +347 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +281 -305
- package/src/components/systems/roof/roof-edit-system.test.tsx +120 -0
- package/src/components/systems/roof/roof-edit-system.tsx +10 -19
- package/src/components/systems/selection-affordance-manager.tsx +59 -9
- package/src/components/systems/selection-affordance-services.ts +19 -0
- package/src/components/systems/stair/stair-edit-system.tsx +5 -2
- package/src/components/tools/fence/fence-drafting.test.ts +100 -0
- package/src/components/tools/fence/fence-drafting.ts +6 -25
- package/src/components/tools/item/draft-creation.test.ts +12 -0
- package/src/components/tools/item/draft-creation.ts +10 -0
- package/src/components/tools/item/face-host-commit.test.ts +65 -0
- package/src/components/tools/item/face-host-commit.ts +56 -0
- package/src/components/tools/item/face-host-preview.test.ts +77 -0
- package/src/components/tools/item/face-host-preview.ts +82 -0
- package/src/components/tools/item/move-tool.tsx +4 -3
- package/src/components/tools/item/placement-math.test.ts +11 -1
- package/src/components/tools/item/placement-math.ts +0 -20
- package/src/components/tools/item/placement-strategies.test.ts +331 -4
- package/src/components/tools/item/placement-strategies.ts +135 -2
- package/src/components/tools/item/placement-surface.test.ts +61 -0
- package/src/components/tools/item/placement-surface.ts +24 -0
- package/src/components/tools/item/placement-types.ts +6 -0
- package/src/components/tools/item/test-face-host.ts +121 -0
- package/src/components/tools/item/use-draft-node.test.tsx +336 -0
- package/src/components/tools/item/use-draft-node.ts +26 -4
- package/src/components/tools/item/use-placement-coordinator.tsx +428 -153
- package/src/components/tools/registered-tool-install-lifecycle.test.tsx +256 -0
- package/src/components/tools/registry/move-registry-node-tool.test.ts +10 -0
- package/src/components/tools/registry/move-registry-node-tool.tsx +306 -110
- package/src/components/tools/registry-tool-context.tsx +30 -0
- package/src/components/tools/select/box-select-tool.tsx +3 -1
- package/src/components/tools/select/marquee-footprint.test.ts +53 -0
- package/src/components/tools/select/marquee-footprint.ts +29 -0
- package/src/components/tools/shared/placement-box.tsx +57 -7
- package/src/components/tools/shared/placement-dimension-guides.tsx +121 -0
- package/src/components/tools/shared/pointer-support-cap.test.ts +319 -17
- package/src/components/tools/shared/pointer-support-cap.ts +45 -8
- package/src/components/tools/site/terrain-sculpt-grid.tsx +9 -5
- package/src/components/tools/site/terrain-sculpt-tool.tsx +1 -1
- package/src/components/tools/stair/stair-tool.tsx +127 -74
- package/src/components/tools/tool-manager.tsx +25 -8
- package/src/components/tools/wall/wall-drafting.test.ts +320 -2
- package/src/components/tools/wall/wall-drafting.ts +23 -114
- package/src/components/ui/action-menu/control-modes.tsx +9 -6
- package/src/components/ui/action-menu/index.tsx +11 -1
- package/src/components/ui/command-palette/editor-commands.tsx +9 -12
- package/src/components/ui/command-palette/index.tsx +7 -2
- package/src/components/ui/controls/material-paint-panel.tsx +6 -4
- package/src/components/ui/controls/material-picker.tsx +4 -4
- package/src/components/ui/controls/scene-material-list.tsx +6 -5
- package/src/components/ui/controls/segmented-control.tsx +8 -1
- package/src/components/ui/controls/slider-control.tsx +19 -3
- package/src/components/ui/controls/toggle-control.tsx +18 -5
- package/src/components/ui/controls/tool-options-panel.tsx +126 -0
- package/src/components/ui/floating-level-selector.tsx +22 -5
- package/src/components/ui/helpers/contextual-helper-panel.tsx +9 -0
- package/src/components/ui/helpers/helper-manager.tsx +47 -11
- package/src/components/ui/helpers/item-helper.tsx +1 -1
- package/src/components/ui/helpers/registered-tool-helper.tsx +21 -0
- package/src/components/ui/panels/homogeneous-selection.test.ts +80 -0
- package/src/components/ui/panels/homogeneous-selection.ts +47 -0
- package/src/components/ui/panels/mobile-selection-bar.tsx +11 -5
- package/src/components/ui/panels/multi-field-value.test.ts +209 -0
- package/src/components/ui/panels/multi-field-value.ts +163 -0
- package/src/components/ui/panels/multi-height-mode.tsx +185 -0
- package/src/components/ui/panels/multi-parametric-inspector.tsx +263 -0
- package/src/components/ui/panels/multi-selection-panel.tsx +48 -37
- package/src/components/ui/panels/node-display.ts +4 -0
- package/src/components/ui/panels/panel-manager.tsx +77 -3
- package/src/components/ui/panels/parametric-field-control.tsx +173 -0
- package/src/components/ui/panels/parametric-field-utils.ts +18 -0
- package/src/components/ui/panels/parametric-inspector.tsx +29 -162
- package/src/components/ui/panels/reference-panel.tsx +31 -1
- package/src/components/ui/primitives/shortcut-token.tsx +23 -1
- package/src/components/ui/scene-loader.tsx +35 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +390 -36
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +20 -23
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.test.ts +140 -0
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +109 -0
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +19 -5
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +116 -46
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +5 -2
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +28 -11
- package/src/components/ui/sidebar/panels/site-panel/tree-node.test.ts +8 -0
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +14 -6
- package/src/components/ui/sidebar/tab-bar.tsx +2 -0
- package/src/components/ui/sidebar/use-plugin-panels.tsx +17 -2
- package/src/components/viewer/viewer-scene-header.tsx +2 -1
- package/src/components/viewer/viewer-stage.tsx +4 -1
- package/src/components/viewer-overlay.tsx +1 -22
- package/src/components/viewer-zone-system.tsx +6 -2
- package/src/components/walkthrough-hud.tsx +19 -10
- package/src/hooks/use-auto-save.test.ts +73 -1
- package/src/hooks/use-auto-save.ts +76 -5
- package/src/hooks/use-ceiling-events.test.ts +122 -0
- package/src/hooks/use-drag-action.ts +17 -3
- package/src/hooks/use-grid-events.test.ts +240 -0
- package/src/hooks/use-grid-events.ts +190 -11
- package/src/hooks/use-keyboard.test.ts +244 -0
- package/src/hooks/use-keyboard.ts +129 -62
- package/src/hooks/use-registered-tool-enabled.ts +20 -0
- package/src/hooks/use-save-shortcut.ts +33 -0
- package/src/index.tsx +82 -4
- package/src/lib/active-placement-surface.test.ts +16 -0
- package/src/lib/active-placement-surface.ts +14 -0
- package/src/lib/ceiling-surface-raycast.test.ts +64 -0
- package/src/lib/ceiling-surface-raycast.ts +12 -0
- package/src/lib/contextual-help-extension.ts +17 -0
- package/src/lib/continuation.test.ts +11 -0
- package/src/lib/continuation.ts +8 -1
- package/src/lib/direct-manipulation.test.ts +132 -0
- package/src/lib/direct-manipulation.ts +38 -1
- package/src/lib/editor-api.ts +6 -3
- package/src/lib/elevation-guides.test.ts +28 -1
- package/src/lib/elevation-guides.ts +22 -7
- package/src/lib/floating-menu-scale.test.ts +29 -0
- package/src/lib/floating-menu-scale.ts +14 -0
- package/src/lib/floorplan/apply-alignment.test.ts +47 -2
- package/src/lib/floorplan/apply-alignment.ts +10 -1
- package/src/lib/floorplan/floorplan-export.test.ts +397 -1
- package/src/lib/floorplan/floorplan-export.tsx +111 -16
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +26 -0
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +32 -24
- package/src/lib/floorplan/floorplan-readonly.ts +1 -0
- package/src/lib/floorplan-grid-event-point.test.ts +29 -0
- package/src/lib/floorplan-grid-event-point.ts +13 -0
- package/src/lib/glb-export.test.ts +1169 -21
- package/src/lib/glb-export.ts +629 -101
- package/src/lib/grid-event-presentation.ts +16 -0
- package/src/lib/history.test.ts +528 -147
- package/src/lib/history.ts +81 -7
- package/src/lib/host-tree-children.test.ts +27 -0
- package/src/lib/host-tree-children.ts +79 -0
- package/src/lib/interaction/hot-set.test.ts +10 -1
- package/src/lib/interaction/hot-set.ts +3 -1
- package/src/lib/interaction/overlay-policy.test.ts +33 -1
- package/src/lib/interaction/overlay-policy.ts +16 -0
- package/src/lib/interaction/registered-drafting.ts +36 -0
- package/src/lib/interaction/scope.ts +31 -0
- package/src/lib/level-print-export.test.ts +484 -0
- package/src/lib/level-print-export.ts +526 -0
- package/src/lib/local-project-presentation-persistence.test.ts +302 -0
- package/src/lib/local-project-presentation-persistence.ts +335 -0
- package/src/lib/material-paint.ts +2 -0
- package/src/lib/model-export.ts +29 -0
- package/src/lib/paint-preview-owner.ts +71 -0
- package/src/lib/planar-cursor-placement.test.ts +178 -1
- package/src/lib/planar-cursor-placement.ts +61 -5
- package/src/lib/plugin-panels.test.ts +92 -1
- package/src/lib/plugin-panels.ts +48 -0
- package/src/lib/portable-export.test.ts +366 -0
- package/src/lib/portable-export.ts +969 -0
- package/src/lib/print-3mf.test.ts +86 -0
- package/src/lib/print-3mf.ts +161 -0
- package/src/lib/print-content-scope.test.ts +107 -0
- package/src/lib/print-content-scope.ts +90 -0
- package/src/lib/print-export.test.ts +294 -0
- package/src/lib/print-export.ts +735 -0
- package/src/lib/print-feature-thickness.test.ts +147 -0
- package/src/lib/print-feature-thickness.ts +180 -0
- package/src/lib/print-golden-house.test-fixture.ts +352 -0
- package/src/lib/print-golden-house.test.ts +249 -0
- package/src/lib/print-roof-solids.test.ts +133 -0
- package/src/lib/print-roof-solids.ts +630 -0
- package/src/lib/print-shell-compiler-baseline.test.ts +419 -0
- package/src/lib/print-shell-compiler-baseline.ts +225 -0
- package/src/lib/print-shell-compiler-manifold-core.ts +330 -0
- package/src/lib/print-shell-compiler-manifold-worker.ts +187 -0
- package/src/lib/print-shell-compiler-manifold.worker.ts +20 -0
- package/src/lib/print-shell-compiler-mesh-data.ts +32 -0
- package/src/lib/print-shell-compiler-protocol.ts +30 -0
- package/src/lib/print-shell-compiler.ts +273 -0
- package/src/lib/print-wall-solids.test.ts +194 -0
- package/src/lib/print-wall-solids.ts +328 -0
- package/src/lib/rigid-plan-svg-transform.test.ts +35 -0
- package/src/lib/rigid-plan-svg-transform.ts +24 -0
- package/src/lib/scene.ts +22 -8
- package/src/lib/selection-routing.test.ts +105 -1
- package/src/lib/selection-routing.ts +19 -0
- package/src/lib/sfx-bus.ts +4 -0
- package/src/lib/sfx-player.ts +8 -0
- package/src/lib/snapping-mode.test.ts +32 -4
- package/src/lib/snapping-mode.ts +11 -7
- package/src/lib/usdz-export.ts +38 -0
- package/src/lib/walkthrough-pointer-lock.ts +47 -0
- package/src/store/live-draft-preview-stores.test.ts +3 -0
- package/src/store/terrain-sculpt-mode.test.ts +3 -6
- package/src/store/tool-mode.test.ts +87 -0
- package/src/store/use-camera-hint-focus.ts +48 -0
- package/src/store/use-editor.tsx +299 -188
- package/src/store/use-floorplan-draft-preview.ts +9 -0
- package/src/store/use-interaction-scope.test.ts +23 -1
- package/src/store/use-interaction-scope.ts +63 -2
- package/src/store/use-placement-preview.ts +56 -3
- package/src/components/tools/roof/roof-tool.tsx +0 -656
- package/src/components/ui/helpers/roof-helper.tsx +0 -14
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getSelectableKinds,
|
|
12
12
|
type ItemNode,
|
|
13
13
|
isRegistrySelectable,
|
|
14
|
+
isSelectionHighlightEnabled,
|
|
14
15
|
type NodeEvent,
|
|
15
16
|
nodeRegistry,
|
|
16
17
|
type RoofEvent,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
createMaterial,
|
|
32
33
|
createMaterialFromPresetRef,
|
|
33
34
|
getRoofMaterialArray,
|
|
35
|
+
registerMaterialCacheCleanup,
|
|
34
36
|
useViewer,
|
|
35
37
|
} from '@pascal-app/viewer'
|
|
36
38
|
import { useThree } from '@react-three/fiber'
|
|
@@ -39,10 +41,14 @@ import { type BufferGeometry, Color, type Material, type Mesh, type Object3D, Ve
|
|
|
39
41
|
import {
|
|
40
42
|
canDirectMoveNode,
|
|
41
43
|
canDirectRotateNode,
|
|
44
|
+
pointerEventHitsEditorHandle,
|
|
45
|
+
resolveDirectManipulationNode,
|
|
42
46
|
resolveDirectRotationDragDelta,
|
|
43
47
|
resolveDirectRotationPatch,
|
|
48
|
+
shouldStartDirectMoveDrag,
|
|
44
49
|
} from '../../lib/direct-manipulation'
|
|
45
50
|
import { createEditorApi } from '../../lib/editor-api'
|
|
51
|
+
import { selectionEnabled } from '../../lib/interaction/scope'
|
|
46
52
|
import {
|
|
47
53
|
type ActivePaintMaterial,
|
|
48
54
|
buildRoofSegmentSurfaceMaterialPatch,
|
|
@@ -50,6 +56,11 @@ import {
|
|
|
50
56
|
hasActivePaintMaterial,
|
|
51
57
|
resolveActivePaintMaterialFromSelection,
|
|
52
58
|
} from '../../lib/material-paint'
|
|
59
|
+
import {
|
|
60
|
+
combinePaintPreviews,
|
|
61
|
+
createPaintPreviewOwner,
|
|
62
|
+
type PaintPreviewCleanup,
|
|
63
|
+
} from '../../lib/paint-preview-owner'
|
|
53
64
|
import {
|
|
54
65
|
availablePaintScopes,
|
|
55
66
|
commitPaintScopeFanout,
|
|
@@ -112,8 +123,6 @@ type SelectableNodeType =
|
|
|
112
123
|
| 'window'
|
|
113
124
|
| 'door'
|
|
114
125
|
|
|
115
|
-
type PaintPreviewCleanup = () => void
|
|
116
|
-
|
|
117
126
|
type PaintInteraction = {
|
|
118
127
|
key: string
|
|
119
128
|
apply: (() => void) | null
|
|
@@ -805,6 +814,7 @@ export const SelectionManager = () => {
|
|
|
805
814
|
if (mode !== 'material-paint') return
|
|
806
815
|
if (movingNode || isCurveReshape) return
|
|
807
816
|
|
|
817
|
+
const previewOwner = createPaintPreviewOwner()
|
|
808
818
|
let activePreview: { key: string; restore: PaintPreviewCleanup } | null = null
|
|
809
819
|
// The last hover event, replayed when the application scope cycles so the
|
|
810
820
|
// preview + chip update under a stationary cursor (Shift fires no pointer move).
|
|
@@ -826,7 +836,7 @@ export const SelectionManager = () => {
|
|
|
826
836
|
selectedMaterialTarget: useEditor.getState().selectedMaterialTarget,
|
|
827
837
|
})
|
|
828
838
|
|
|
829
|
-
const
|
|
839
|
+
const resolvePaintInteraction = (event: NodeEvent): PaintInteraction | null => {
|
|
830
840
|
const eraser = useEditor.getState().paintEraser
|
|
831
841
|
const activePaintMaterial = resolveActivePaintMaterial()
|
|
832
842
|
const node = event.node
|
|
@@ -949,27 +959,29 @@ export const SelectionManager = () => {
|
|
|
949
959
|
// paint capability builds the preview; restores combine.
|
|
950
960
|
const restores: PaintPreviewCleanup[] = []
|
|
951
961
|
const sceneNodes = useScene.getState().nodes
|
|
952
|
-
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
962
|
+
try {
|
|
963
|
+
for (const target of scopeTargets) {
|
|
964
|
+
const targetNode = sceneNodes[target.nodeId]
|
|
965
|
+
const targetRoot = getRegisteredNodeObject(target.nodeId)
|
|
966
|
+
const targetCap = targetNode
|
|
967
|
+
? nodeRegistry.get(targetNode.type)?.capabilities?.paint
|
|
968
|
+
: null
|
|
969
|
+
if (!(targetNode && targetRoot && targetCap)) continue
|
|
970
|
+
const restore = targetCap.applyPreview({
|
|
971
|
+
node: targetNode,
|
|
972
|
+
role: target.role,
|
|
973
|
+
material: paintSpec.material,
|
|
974
|
+
materialPreset: paintSpec.materialPreset,
|
|
975
|
+
root: targetRoot,
|
|
976
|
+
})
|
|
977
|
+
if (restore) restores.push(restore)
|
|
978
|
+
}
|
|
979
|
+
} catch (error) {
|
|
980
|
+
combinePaintPreviews(restores)()
|
|
981
|
+
throw error
|
|
967
982
|
}
|
|
968
983
|
if (restores.length === 0) return null
|
|
969
|
-
return ()
|
|
970
|
-
for (let index = restores.length - 1; index >= 0; index -= 1)
|
|
971
|
-
restores[index]?.()
|
|
972
|
-
}
|
|
984
|
+
return combinePaintPreviews(restores)
|
|
973
985
|
}
|
|
974
986
|
: () => previewCursor('not-allowed'),
|
|
975
987
|
}
|
|
@@ -1069,6 +1081,9 @@ export const SelectionManager = () => {
|
|
|
1069
1081
|
return null
|
|
1070
1082
|
}
|
|
1071
1083
|
|
|
1084
|
+
const getPaintInteraction = (event: NodeEvent) =>
|
|
1085
|
+
previewOwner.wrap(resolvePaintInteraction(event))
|
|
1086
|
+
|
|
1072
1087
|
const onEnter = (event: NodeEvent) => {
|
|
1073
1088
|
// A host-driven drag (handle resize/rotate) sets `inputDragging`.
|
|
1074
1089
|
// useNodeEvents now emits hover events during such a drag so surface
|
|
@@ -1239,8 +1254,10 @@ export const SelectionManager = () => {
|
|
|
1239
1254
|
if (movingNode || isCurveReshape) return
|
|
1240
1255
|
|
|
1241
1256
|
const onPointerDown = (event: NodeEvent) => {
|
|
1257
|
+
if (!selectionEnabled(useInteractionScope.getState().scope)) return
|
|
1242
1258
|
const pointer = pointerEventFromNodeEvent(event)
|
|
1243
1259
|
if (pointer.button !== 0) return
|
|
1260
|
+
const handleOwnsPointer = pointerEventHitsEditorHandle(event.nativeEvent)
|
|
1244
1261
|
|
|
1245
1262
|
// Plain press on a transformable member of a multi-selection arms the
|
|
1246
1263
|
// group move — dragging slides the whole selection on the ground plane
|
|
@@ -1248,6 +1265,7 @@ export const SelectionManager = () => {
|
|
|
1248
1265
|
// group-move gizmo cross). A plain click (no drag) still falls through
|
|
1249
1266
|
// to the normal click handling, which collapses to the pressed node.
|
|
1250
1267
|
if (
|
|
1268
|
+
!handleOwnsPointer &&
|
|
1251
1269
|
!(pointer.shiftKey || pointer.altKey || isCommandModifier(pointer)) &&
|
|
1252
1270
|
armGroupMove3d({
|
|
1253
1271
|
nodeId: event.node.id as AnyNodeId,
|
|
@@ -1263,8 +1281,6 @@ export const SelectionManager = () => {
|
|
|
1263
1281
|
return
|
|
1264
1282
|
}
|
|
1265
1283
|
|
|
1266
|
-
if (!isCommandModifier(pointer)) return
|
|
1267
|
-
|
|
1268
1284
|
const eventNode = useScene.getState().nodes[event.node.id as AnyNodeId] ?? event.node
|
|
1269
1285
|
const node = resolveCanvasSelectionNode({
|
|
1270
1286
|
node: eventNode,
|
|
@@ -1272,18 +1288,26 @@ export const SelectionManager = () => {
|
|
|
1272
1288
|
selectedIds: useViewer.getState().selection.selectedIds,
|
|
1273
1289
|
})
|
|
1274
1290
|
if (!canDirectMoveNode(node)) return
|
|
1275
|
-
// Sole selection only: per-node direct manipulation stands down for a
|
|
1276
|
-
// multi-selection (the group sessions own plain drags there, and Cmd is
|
|
1277
|
-
// the selection-toggle key — a wobbly Cmd+click must not yank one
|
|
1278
|
-
// member out of the group).
|
|
1279
1291
|
const currentSelectedIds = useViewer.getState().selection.selectedIds
|
|
1280
|
-
|
|
1292
|
+
const allowPlainDrag = nodeRegistry.get(node.type)?.capabilities?.movable?.directDrag === true
|
|
1293
|
+
if (
|
|
1294
|
+
!shouldStartDirectMoveDrag({
|
|
1295
|
+
allowPlainDrag,
|
|
1296
|
+
commandModifier: isCommandModifier(pointer),
|
|
1297
|
+
handleOwnsPointer,
|
|
1298
|
+
nodeId: node.id,
|
|
1299
|
+
selectedIds: currentSelectedIds,
|
|
1300
|
+
})
|
|
1301
|
+
) {
|
|
1302
|
+
return
|
|
1303
|
+
}
|
|
1281
1304
|
|
|
1282
1305
|
const startX = pointer.clientX
|
|
1283
1306
|
const startY = pointer.clientY
|
|
1284
1307
|
const pointerId = pointer.pointerId
|
|
1285
1308
|
const pointerTarget = pointer.target instanceof EventTarget ? pointer.target : null
|
|
1286
1309
|
let engaged = false
|
|
1310
|
+
let engagedTargetId: AnyNodeId | null = null
|
|
1287
1311
|
|
|
1288
1312
|
const cleanup = () => {
|
|
1289
1313
|
window.removeEventListener('pointermove', onMove)
|
|
@@ -1305,8 +1329,9 @@ export const SelectionManager = () => {
|
|
|
1305
1329
|
useViewer.getState().setInputDragging(true)
|
|
1306
1330
|
swallowNextClick()
|
|
1307
1331
|
createEditorApi().engageMoveDrag(node)
|
|
1332
|
+
engagedTargetId = (getMovingNode()?.id as AnyNodeId | undefined) ?? null
|
|
1308
1333
|
requestAnimationFrame(() => {
|
|
1309
|
-
if (getMovingNode()
|
|
1334
|
+
if (!getMovingNode()) return
|
|
1310
1335
|
pointerTarget?.dispatchEvent(
|
|
1311
1336
|
new PointerEvent('pointermove', {
|
|
1312
1337
|
altKey: moveEvent.altKey,
|
|
@@ -1330,7 +1355,7 @@ export const SelectionManager = () => {
|
|
|
1330
1355
|
if (engaged) {
|
|
1331
1356
|
requestAnimationFrame(() => {
|
|
1332
1357
|
const editor = useEditor.getState()
|
|
1333
|
-
if (getMovingNode()?.id !==
|
|
1358
|
+
if (getMovingNode()?.id !== engagedTargetId || !editor.placementDragMode) return
|
|
1334
1359
|
editor.setMovingNode(null)
|
|
1335
1360
|
})
|
|
1336
1361
|
}
|
|
@@ -1392,7 +1417,7 @@ export const SelectionManager = () => {
|
|
|
1392
1417
|
if (key === prevKey) return
|
|
1393
1418
|
prevKey = key
|
|
1394
1419
|
let wantsMove = false
|
|
1395
|
-
if (hoveredId && !getMovingNode()) {
|
|
1420
|
+
if (hoveredId && !getMovingNode() && selectionEnabled(useInteractionScope.getState().scope)) {
|
|
1396
1421
|
if (sole === hoveredId) {
|
|
1397
1422
|
const node = useScene.getState().nodes[sole as AnyNodeId]
|
|
1398
1423
|
wantsMove = !!node && canDirectMoveNode(node)
|
|
@@ -1557,6 +1582,7 @@ export const SelectionManager = () => {
|
|
|
1557
1582
|
// body click so only the reshape tool handles the release. (Scoped to
|
|
1558
1583
|
// `endpoint`: hole-edit relies on node clicks to exit, just below.)
|
|
1559
1584
|
const activeScope = useInteractionScope.getState().scope
|
|
1585
|
+
if (activeScope.kind === 'mesh-editing') return
|
|
1560
1586
|
if (activeScope.kind === 'reshaping' && activeScope.reshape === 'endpoint') return
|
|
1561
1587
|
|
|
1562
1588
|
if (dispatchSceneAction(event.node, getEventObject(event))) {
|
|
@@ -1655,9 +1681,16 @@ export const SelectionManager = () => {
|
|
|
1655
1681
|
const hasModifier = nativeEvent.shiftKey || isCommandModifier(nativeEvent)
|
|
1656
1682
|
const isAlreadySole =
|
|
1657
1683
|
selectedIdsBeforeRouting.length === 1 && selectedIdsBeforeRouting[0] === nodeToSelect.id
|
|
1658
|
-
if (
|
|
1684
|
+
if (
|
|
1685
|
+
useEditor.getState().mode !== 'delete' &&
|
|
1686
|
+
!hasModifier &&
|
|
1687
|
+
isAlreadySole &&
|
|
1688
|
+
!getMovingNode() &&
|
|
1689
|
+
canDirectMoveNode(nodeToSelect)
|
|
1690
|
+
) {
|
|
1659
1691
|
sfxEmitter.emit('sfx:item-pick')
|
|
1660
|
-
|
|
1692
|
+
const moveTarget = resolveDirectManipulationNode(nodeToSelect, useScene.getState().nodes)
|
|
1693
|
+
useEditor.getState().setMovingNode(moveTarget as never)
|
|
1661
1694
|
useViewer.getState().setSelection({ selectedIds: [] })
|
|
1662
1695
|
return
|
|
1663
1696
|
}
|
|
@@ -1773,6 +1806,7 @@ export const SelectionManager = () => {
|
|
|
1773
1806
|
const onGridClick = (event: GridEvent) => {
|
|
1774
1807
|
if (clickHandledRef.current) return
|
|
1775
1808
|
if (boxSelectHandled) return
|
|
1809
|
+
if (useInteractionScope.getState().scope.kind === 'mesh-editing') return
|
|
1776
1810
|
const nativeEvent = event.nativeEvent
|
|
1777
1811
|
if (nativeEvent?.metaKey || nativeEvent?.ctrlKey || nativeEvent?.shiftKey) return
|
|
1778
1812
|
const { phase, structureLayer } = useEditor.getState()
|
|
@@ -1804,6 +1838,7 @@ export const SelectionManager = () => {
|
|
|
1804
1838
|
if (movingNode || isCurveReshape) return
|
|
1805
1839
|
|
|
1806
1840
|
const onEnter = (event: NodeEvent) => {
|
|
1841
|
+
if (useInteractionScope.getState().scope.kind === 'mesh-editing') return
|
|
1807
1842
|
// A host-driven drag (handle resize/rotate, box-select) sets
|
|
1808
1843
|
// `inputDragging`. useNodeEvents still emits hover events during it so
|
|
1809
1844
|
// surface move tools keep tracking — but the select-hover outline must
|
|
@@ -1852,6 +1887,7 @@ export const SelectionManager = () => {
|
|
|
1852
1887
|
}
|
|
1853
1888
|
|
|
1854
1889
|
const onDoubleClick = (event: NodeEvent) => {
|
|
1890
|
+
if (useInteractionScope.getState().scope.kind === 'mesh-editing') return
|
|
1855
1891
|
let node = resolveCanvasSelectionNode({
|
|
1856
1892
|
node: resolveSelectModeNodeTarget(event),
|
|
1857
1893
|
nodes: useScene.getState().nodes,
|
|
@@ -2119,6 +2155,7 @@ const SelectionMaterialSync = () => {
|
|
|
2119
2155
|
const previewSelectedIds = useViewer((s) => s.previewSelectedIds)
|
|
2120
2156
|
const hoveredId = useViewer((s) => s.hoveredId)
|
|
2121
2157
|
const hoverHighlightMode = useViewer((s) => s.hoverHighlightMode)
|
|
2158
|
+
const registryVersion = useRegistryVersion()
|
|
2122
2159
|
const geometryRevision = useViewer((s) => s.geometryRevision)
|
|
2123
2160
|
const activeHighlightKindsRef = useRef(new Map<string, HighlightKind>())
|
|
2124
2161
|
const highlightedMaterialsRef = useRef(
|
|
@@ -2141,6 +2178,10 @@ const SelectionMaterialSync = () => {
|
|
|
2141
2178
|
continue
|
|
2142
2179
|
}
|
|
2143
2180
|
|
|
2181
|
+
if (node && !isSelectionHighlightEnabled(node.type)) {
|
|
2182
|
+
continue
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2144
2185
|
const rootObject = sceneRegistry.nodes.get(id)
|
|
2145
2186
|
if (!rootObject) {
|
|
2146
2187
|
continue
|
|
@@ -2196,6 +2237,7 @@ const SelectionMaterialSync = () => {
|
|
|
2196
2237
|
}, [])
|
|
2197
2238
|
|
|
2198
2239
|
useEffect(() => {
|
|
2240
|
+
void registryVersion
|
|
2199
2241
|
void geometryRevision
|
|
2200
2242
|
const nextHighlightKinds = new Map<string, HighlightKind>()
|
|
2201
2243
|
|
|
@@ -2211,6 +2253,7 @@ const SelectionMaterialSync = () => {
|
|
|
2211
2253
|
syncSelectionMaterials()
|
|
2212
2254
|
}, [
|
|
2213
2255
|
geometryRevision,
|
|
2256
|
+
registryVersion,
|
|
2214
2257
|
hoverHighlightMode,
|
|
2215
2258
|
hoveredId,
|
|
2216
2259
|
previewSelectedIds,
|
|
@@ -2251,7 +2294,7 @@ const SelectionMaterialSync = () => {
|
|
|
2251
2294
|
}, [])
|
|
2252
2295
|
|
|
2253
2296
|
useEffect(() => {
|
|
2254
|
-
|
|
2297
|
+
const clearHighlights = () => {
|
|
2255
2298
|
for (const [mesh, entry] of highlightedMaterialsRef.current.entries()) {
|
|
2256
2299
|
if (mesh.material === entry.highlightedMaterial) {
|
|
2257
2300
|
mesh.material = entry.originalMaterial
|
|
@@ -2261,6 +2304,11 @@ const SelectionMaterialSync = () => {
|
|
|
2261
2304
|
|
|
2262
2305
|
highlightedMaterialsRef.current.clear()
|
|
2263
2306
|
}
|
|
2307
|
+
const unsubscribe = registerMaterialCacheCleanup(clearHighlights)
|
|
2308
|
+
return () => {
|
|
2309
|
+
unsubscribe()
|
|
2310
|
+
clearHighlights()
|
|
2311
|
+
}
|
|
2264
2312
|
}, [])
|
|
2265
2313
|
|
|
2266
2314
|
return null
|
|
@@ -2272,11 +2320,13 @@ const EditorOutlinerSync = () => {
|
|
|
2272
2320
|
const previewSelectedIds = useViewer((s) => s.previewSelectedIds)
|
|
2273
2321
|
const hoveredId = useViewer((s) => s.hoveredId)
|
|
2274
2322
|
const geometryRevision = useViewer((s) => s.geometryRevision)
|
|
2323
|
+
const registryVersion = useRegistryVersion()
|
|
2275
2324
|
const outliner = useViewer((s) => s.outliner)
|
|
2276
2325
|
const nodes = useScene((s) => s.nodes)
|
|
2277
2326
|
|
|
2278
2327
|
useEffect(() => {
|
|
2279
2328
|
void geometryRevision
|
|
2329
|
+
void registryVersion
|
|
2280
2330
|
let idsToHighlight: string[] = []
|
|
2281
2331
|
|
|
2282
2332
|
// 1. Determine what should be highlighted based on Phase
|
|
@@ -2311,7 +2361,8 @@ const EditorOutlinerSync = () => {
|
|
|
2311
2361
|
// 2. Sync with the imperative outliner arrays (mutate in place to keep references)
|
|
2312
2362
|
outliner.selectedObjects.length = 0
|
|
2313
2363
|
for (const id of idsToHighlight) {
|
|
2314
|
-
|
|
2364
|
+
const node = nodes[id as AnyNodeId]
|
|
2365
|
+
if (!(node && isSelectionHighlightEnabled(node.type))) continue
|
|
2315
2366
|
const obj = sceneRegistry.nodes.get(id)
|
|
2316
2367
|
if (obj?.parent) outliner.selectedObjects.push(obj)
|
|
2317
2368
|
}
|
|
@@ -2322,14 +2373,25 @@ const EditorOutlinerSync = () => {
|
|
|
2322
2373
|
useViewer.setState({ hoveredId: null })
|
|
2323
2374
|
} else {
|
|
2324
2375
|
const hoveredNode = nodes[hoveredId as AnyNodeId]
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2376
|
+
if (hoveredNode && isSelectionHighlightEnabled(hoveredNode.type)) {
|
|
2377
|
+
const obj =
|
|
2378
|
+
hoveredNode.type === 'roof-segment'
|
|
2379
|
+
? (getHoveredRoofSegmentOutlineProxy(hoveredId) ?? sceneRegistry.nodes.get(hoveredId))
|
|
2380
|
+
: sceneRegistry.nodes.get(hoveredId)
|
|
2381
|
+
if (obj?.parent) outliner.hoveredObjects.push(obj)
|
|
2382
|
+
}
|
|
2330
2383
|
}
|
|
2331
2384
|
}
|
|
2332
|
-
}, [
|
|
2385
|
+
}, [
|
|
2386
|
+
geometryRevision,
|
|
2387
|
+
registryVersion,
|
|
2388
|
+
phase,
|
|
2389
|
+
previewSelectedIds,
|
|
2390
|
+
selection,
|
|
2391
|
+
hoveredId,
|
|
2392
|
+
outliner,
|
|
2393
|
+
nodes,
|
|
2394
|
+
])
|
|
2333
2395
|
|
|
2334
2396
|
return null
|
|
2335
2397
|
}
|