@pascal-app/editor 1.0.3 → 1.0.5
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/README.md +106 -0
- package/package.json +42 -38
- package/src/components/editor/__bench__/room-label-placement.bench.ts +108 -0
- package/src/components/editor/action-menu-button.tsx +83 -0
- package/src/components/editor/bake-thumbnail.tsx +307 -5
- package/src/components/editor/capture-level-picker.tsx +72 -0
- package/src/components/editor/construction-reveal-audio.test.tsx +150 -0
- package/src/components/editor/construction-reveal-audio.tsx +40 -0
- package/src/components/editor/custom-camera-controls.tsx +275 -57
- package/src/components/editor/delete-confirmation-dialog.tsx +297 -7
- package/src/components/editor/duplicate-node.ts +244 -0
- package/src/components/editor/editor-layout-mobile.tsx +37 -26
- package/src/components/editor/editor-layout-v2.tsx +132 -59
- package/src/components/editor/export-manager.tsx +47 -26
- package/src/components/editor/first-person/build-collider-world.test.ts +320 -0
- package/src/components/editor/first-person/build-collider-world.ts +28 -7
- package/src/components/editor/first-person/ground-holes.test.ts +128 -0
- package/src/components/editor/first-person/ground-holes.ts +130 -0
- package/src/components/editor/first-person/mechanism-targets.test.ts +162 -0
- package/src/components/editor/first-person/mechanism-targets.ts +88 -0
- package/src/components/editor/first-person-controls.tsx +220 -9
- package/src/components/editor/floating-action-menu.tsx +63 -207
- package/src/components/editor/floor-aware-camera-controls.test.ts +466 -0
- package/src/components/editor/floor-aware-camera-controls.ts +324 -0
- package/src/components/editor/floor-openings-overlay.tsx +243 -0
- package/src/components/editor/floor-region-controls.tsx +846 -0
- package/src/components/editor/floor-region-webgpu-materials.test.ts +12 -0
- package/src/components/editor/floor-surface.test.ts +165 -0
- package/src/components/editor/floor-surface.ts +182 -0
- package/src/components/editor/floorplan-background-selection.test.ts +11 -2
- package/src/components/editor/floorplan-background-selection.ts +1 -3
- package/src/components/editor/floorplan-mode-coordinator.tsx +2 -2
- package/src/components/editor/floorplan-panel.tsx +414 -363
- package/src/components/editor/follow-pascal.test.tsx +912 -0
- package/src/components/editor/follow-pascal.tsx +354 -0
- package/src/components/editor/footprint-height-handle.tsx +204 -0
- package/src/components/editor/group-actions.ts +56 -2
- package/src/components/editor/group-move-3d.ts +2 -0
- package/src/components/editor/group-rotate-handle.tsx +2 -0
- package/src/components/editor/group-transform-shared.test.ts +460 -0
- package/src/components/editor/handles/handle-hit-priority.test.tsx +63 -0
- package/src/components/editor/handles/handle-hit-priority.tsx +37 -0
- package/src/components/editor/handles/swallow-next-click.test.ts +28 -0
- package/src/components/editor/handles/use-handle-drag.ts +269 -197
- package/src/components/editor/index.tsx +409 -197
- package/src/components/editor/join-walls-pill.tsx +44 -0
- package/src/components/editor/node-action-menu.tsx +68 -70
- package/src/components/editor/node-arrow-handles.tsx +25 -6
- package/src/components/editor/open-wall-ends-3d-layer.tsx +273 -0
- package/src/components/editor/open-wall-ends-hint.tsx +89 -0
- package/src/components/editor/quick-measurement-hud.tsx +3 -2
- package/src/components/editor/registry-action-contributions.test.tsx +63 -0
- package/src/components/editor/registry-action-contributions.tsx +80 -1
- package/src/components/editor/riser-diagram-panel.tsx +14 -7
- package/src/components/editor/room-controls.tsx +847 -0
- package/src/components/editor/room-floor-highlight.tsx +128 -0
- package/src/components/editor/room-handles-2d.tsx +354 -0
- package/src/components/editor/room-handles.tsx +675 -0
- package/src/components/editor/room-highlight.tsx +233 -0
- package/src/components/editor/room-labels.test.ts +850 -0
- package/src/components/editor/room-labels.tsx +1890 -0
- package/src/components/editor/room-transform-ghost.tsx +256 -0
- package/src/components/editor/room-webgpu-materials.test.ts +27 -0
- package/src/components/editor/selection-manager.tsx +474 -419
- package/src/components/editor/slab-hole-highlights.tsx +5 -2
- package/src/components/editor/snapshot-capture-overlay.tsx +7 -2
- package/src/components/editor/snapshot-capture.test.ts +119 -60
- package/src/components/editor/snapshot-capture.ts +36 -5
- package/src/components/editor/thumbnail-generator.test.ts +654 -0
- package/src/components/editor/thumbnail-generator.tsx +1103 -223
- package/src/components/editor/use-floorplan-background-placement.ts +34 -12
- package/src/components/editor/vector-edge-extractor.tsx +121 -0
- package/src/components/editor/views/host-view.tsx +61 -0
- package/src/components/editor/views/plugins-view.tsx +663 -0
- package/src/components/editor/views/scene-region.tsx +59 -0
- package/src/components/editor/views/use-editor-views.ts +57 -0
- package/src/components/editor/views/view-bar.test.tsx +54 -0
- package/src/components/editor/views/view-bar.tsx +343 -0
- package/src/components/editor/wall-justification.test.ts +140 -0
- package/src/components/editor/wall-move-side-handles.tsx +43 -142
- package/src/components/editor/wall-push-arrow.tsx +92 -0
- package/src/components/editor/wall-region-handles.tsx +509 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +8 -4
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
- package/src/components/editor-2d/floorplan-group-move.tsx +3 -0
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.test.tsx +86 -0
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +290 -0
- package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +2 -2
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +116 -106
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.test.ts +156 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +278 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +77 -0
- package/src/components/editor-2d/renderers/{floorplan-draft-wall-measurement.tsx → floorplan-draft-measurement.tsx} +8 -8
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +38 -8
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +319 -233
- package/src/components/editor-2d/renderers/floorplan-reveal.test.tsx +254 -0
- package/src/components/editor-2d/renderers/floorplan-reveal.ts +141 -0
- package/src/components/editor-2d/svg-paths.ts +12 -15
- package/src/components/point-ask/bubble.tsx +538 -0
- package/src/components/point-ask/hover-chip.tsx +91 -0
- package/src/components/point-ask/mode-pill.tsx +92 -0
- package/src/components/point-ask/optional-motion.ts +22 -0
- package/src/components/point-ask/outline-corners.tsx +93 -0
- package/src/components/point-ask/pins.tsx +711 -0
- package/src/components/point-ask/point-ask-layer.tsx +83 -0
- package/src/components/point-ask/point-ask-styles.ts +105 -0
- package/src/components/point-ask/region-marquee-styles.ts +15 -0
- package/src/components/point-ask/region-marquee.tsx +64 -0
- package/src/components/procedural-items/inspector.tsx +40 -15
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +12 -2
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +97 -14
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +18 -18
- package/src/components/systems/roof/roof-edit-system.tsx +1 -0
- package/src/components/systems/zone/zone-system.test.ts +47 -0
- package/src/components/systems/zone/zone-system.tsx +29 -68
- package/src/components/tools/distribution/connection-compatibility.test.ts +34 -0
- package/src/components/tools/distribution/connection-compatibility.ts +43 -0
- package/src/components/tools/distribution/distribution-public.ts +9 -0
- package/src/components/tools/distribution/distribution-run-contract.test.ts +26 -0
- package/src/components/tools/distribution/distribution-run-contract.ts +94 -0
- package/src/components/tools/distribution/distribution-run-tool.test.ts +241 -0
- package/src/components/tools/distribution/distribution-run-tool.test.tsx +125 -0
- package/src/components/tools/distribution/distribution-run-tool.tsx +1323 -0
- package/src/components/tools/distribution/draw-alignment.ts +35 -0
- package/src/components/tools/distribution/ports.test.ts +56 -0
- package/src/components/tools/distribution/ports.ts +378 -0
- package/src/components/tools/distribution/run-cursor.test.ts +135 -0
- package/src/components/tools/distribution/run-cursor.ts +36 -0
- package/src/components/tools/distribution/run-direction-feedback.test.ts +60 -0
- package/src/components/tools/distribution/run-direction-feedback.tsx +251 -0
- package/src/components/tools/distribution/run-port-snap.test.ts +79 -0
- package/src/components/tools/distribution/run-port-snap.ts +25 -0
- package/src/components/tools/distribution/run-screen-direction.test.ts +23 -0
- package/src/components/tools/distribution/run-screen-direction.ts +30 -0
- package/src/components/tools/elevator/elevator-tool.tsx +3 -6
- package/src/components/tools/elevator/move-elevator-tool.tsx +2 -3
- package/src/components/tools/item/item-surface-strategy-resolver.test.ts +67 -68
- package/src/components/tools/item/placement-history.test.tsx +512 -0
- package/src/components/tools/item/placement-strategies.test.ts +46 -1
- package/src/components/tools/item/placement-strategies.ts +11 -10
- package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +56 -56
- package/src/components/tools/item/use-draft-node.test.tsx +432 -1
- package/src/components/tools/item/use-draft-node.ts +166 -95
- package/src/components/tools/item/use-placement-coordinator.tsx +148 -95
- package/src/components/tools/paint-region/use-paint-tint.ts +37 -0
- package/src/components/tools/paint-region/wall-paint-region-tool.tsx +466 -0
- package/src/components/tools/registry/item-surface-move.test.ts +62 -63
- package/src/components/tools/registry/item-surface-move.ts +2 -0
- package/src/components/tools/registry/item-surface-resolver.test.ts +26 -26
- package/src/components/tools/registry/move-registry-node-tool.test.ts +268 -1
- package/src/components/tools/registry/move-registry-node-tool.tsx +66 -30
- package/src/components/tools/select/box-select-tool.tsx +2 -12
- package/src/components/tools/select/plane-box-select-tool.test.tsx +79 -0
- package/src/components/tools/select/plane-box-select-tool.tsx +35 -22
- package/src/components/tools/select/select-candidates.test.ts +37 -4
- package/src/components/tools/select/select-candidates.ts +15 -11
- package/src/components/tools/shared/cursor-sphere.tsx +36 -12
- package/src/components/tools/site/site-boundary-editor.tsx +1 -2
- package/src/components/tools/tool-manager.tsx +59 -24
- package/src/components/tools/wall/wall-drafting.test.ts +175 -47
- package/src/components/tools/zone/zone-tool.tsx +4 -12
- package/src/components/ui/action-menu/action-button.tsx +9 -1
- package/src/components/ui/action-menu/camera-actions.tsx +9 -4
- package/src/components/ui/action-menu/control-modes.tsx +15 -42
- package/src/components/ui/action-menu/follow-pascal-pill.tsx +32 -0
- package/src/components/ui/action-menu/follow-pascal-toggle.tsx +36 -0
- package/src/components/ui/action-menu/index.tsx +56 -33
- package/src/components/ui/action-menu/measurement-control.tsx +14 -19
- package/src/components/ui/action-menu/placement.ts +20 -0
- package/src/components/ui/action-menu/point-ask-button.tsx +52 -0
- package/src/components/ui/action-menu/view-toggles.tsx +47 -42
- package/src/components/ui/build-panel/build-panel.tsx +242 -0
- package/src/components/ui/command-palette/editor-commands.tsx +7 -42
- package/src/components/ui/command-palette/index.tsx +50 -39
- package/src/components/ui/command-palette/ranked-groups.test.ts +31 -0
- package/src/components/ui/controls/action-button.tsx +5 -2
- package/src/components/ui/controls/finish-swatch.tsx +110 -0
- package/src/components/ui/controls/material-paint-panel.tsx +97 -26
- package/src/components/ui/controls/material-picker.tsx +4 -1
- package/src/components/ui/controls/metric-control.tsx +1 -1
- package/src/components/ui/controls/scene-material-list.tsx +4 -32
- package/src/components/ui/controls/segmented-control.tsx +21 -8
- package/src/components/ui/controls/select-control.tsx +65 -0
- package/src/components/ui/controls/slider-control.tsx +1 -1
- package/src/components/ui/controls/toggle-control.tsx +3 -3
- package/src/components/ui/controls/tool-options-panel.tsx +1 -1
- package/src/components/ui/floating-level-selector.tsx +159 -59
- package/src/components/ui/helpers/building-helper.tsx +4 -1
- package/src/components/ui/helpers/contextual-helper-panel.tsx +181 -72
- package/src/components/ui/helpers/helper-manager.test.tsx +121 -0
- package/src/components/ui/helpers/helper-manager.tsx +331 -14
- package/src/components/ui/helpers/item-helper.tsx +17 -1
- package/src/components/ui/helpers/registered-tool-helper.tsx +8 -1
- package/src/components/ui/icon-ref.tsx +45 -1
- package/src/components/ui/level-duplicate-dialog.tsx +1 -1
- package/src/components/ui/node-icon-image.tsx +16 -0
- package/src/components/ui/panels/collections/collections-popover.tsx +28 -22
- package/src/components/ui/panels/floor-foundation-panel.tsx +551 -0
- package/src/components/ui/panels/floor-opening-panel.tsx +143 -0
- package/src/components/ui/panels/mobile-panel-sheet.tsx +4 -4
- package/src/components/ui/panels/mobile-selection-bar.tsx +8 -5
- package/src/components/ui/panels/multi-field-value.test.ts +26 -3
- package/src/components/ui/panels/multi-field-value.ts +6 -3
- package/src/components/ui/panels/multi-parametric-inspector.tsx +52 -2
- package/src/components/ui/panels/multi-selection-panel.tsx +161 -5
- package/src/components/ui/panels/node-display.ts +8 -3
- package/src/components/ui/panels/panel-manager.tsx +103 -68
- package/src/components/ui/panels/panel-wrapper.tsx +130 -62
- package/src/components/ui/panels/parametric-field-control.tsx +3 -3
- package/src/components/ui/panels/parametric-inspector.tsx +7 -7
- package/src/components/ui/panels/reference-panel.tsx +2 -3
- package/src/components/ui/panels/room-construction-rows.tsx +774 -0
- package/src/components/ui/panels/room-name-options.test.ts +55 -0
- package/src/components/ui/panels/room-name-options.ts +47 -0
- package/src/components/ui/panels/room-openings-field.tsx +110 -0
- package/src/components/ui/panels/room-panel.tsx +790 -0
- package/src/components/ui/panels/shape-choice.tsx +58 -0
- package/src/components/ui/panels/wall-region-list.tsx +109 -0
- package/src/components/ui/primitives/badge.tsx +8 -0
- package/src/components/ui/primitives/color-dot.tsx +36 -24
- package/src/components/ui/primitives/sidebar.tsx +3 -1
- package/src/components/ui/right-stack.test.ts +29 -0
- package/src/components/ui/right-stack.tsx +164 -0
- package/src/components/ui/sidebar/app-sidebar.tsx +1 -1
- package/src/components/ui/sidebar/icon-rail.tsx +8 -3
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +1 -1
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +6 -6
- package/src/components/ui/sidebar/panels/settings-panel/animation-speed-setting.tsx +40 -0
- package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +7 -8
- package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.test.tsx +170 -0
- package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.tsx +92 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +138 -27
- package/src/components/ui/sidebar/panels/settings-panel/interface-theme.test.tsx +20 -0
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +5 -5
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +4 -10
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -11
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -4
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +197 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +24 -11
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +181 -223
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +30 -8
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +4 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +1 -4
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +21 -13
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +13 -37
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -3
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +9 -4
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +2 -4
- package/src/components/ui/sidebar/tab-bar.tsx +14 -4
- package/src/components/ui/sidebar/use-plugin-panels.tsx +15 -5
- package/src/components/viewer/floorplan-compass-button.tsx +9 -1
- package/src/components/viewer/floorplan-preview.tsx +5 -6
- package/src/components/viewer/viewer-controls-bar.tsx +18 -6
- package/src/components/viewer/viewer-hover-label.tsx +34 -0
- package/src/components/viewer/viewer-rooms.tsx +191 -0
- package/src/components/viewer/viewer-scene-header.tsx +181 -145
- package/src/components/viewer/viewer-selection-manager.tsx +124 -0
- package/src/components/viewer/viewer-stage.tsx +2 -2
- package/src/components/viewer-zone-system.tsx +3 -8
- package/src/hooks/use-auto-save.integration.test.tsx +215 -43
- package/src/hooks/use-auto-save.ts +60 -9
- package/src/hooks/use-ceiling-events.test.ts +123 -118
- package/src/hooks/use-grid-events.test.ts +105 -105
- package/src/hooks/use-keyboard.test.ts +243 -19
- package/src/hooks/use-keyboard.ts +118 -83
- package/src/hooks/use-open-wall-ends.test.ts +99 -0
- package/src/hooks/use-open-wall-ends.ts +238 -0
- package/src/hooks/use-placement-notice.test.ts +71 -0
- package/src/hooks/use-placement-notice.ts +73 -0
- package/src/hooks/use-selected-room.ts +143 -0
- package/src/index.tsx +312 -58
- package/src/lib/__fixtures__/owner-victor-seattle-parking.json +1 -0
- package/src/lib/__fixtures__/owner-villa-aurelia-basement.json +1 -0
- package/src/lib/add-storey.test.ts +177 -0
- package/src/lib/add-storey.ts +59 -0
- package/src/lib/below-grade.test.tsx +171 -0
- package/src/lib/below-grade.ts +133 -0
- package/src/lib/camera-facing.test.ts +24 -0
- package/src/lib/camera-facing.ts +24 -0
- package/src/lib/ceiling-edit-session.test.ts +321 -0
- package/src/lib/ceiling-edit-session.ts +206 -0
- package/src/lib/ceiling-region-session.test.ts +179 -0
- package/src/lib/continuation.test.ts +19 -1
- package/src/lib/continuation.ts +25 -4
- package/src/lib/direct-manipulation.ts +2 -0
- package/src/lib/door-interaction.ts +14 -0
- package/src/lib/download-project-file.ts +17 -0
- package/src/lib/editor-level-display.test.ts +96 -0
- package/src/lib/editor-level-display.ts +55 -0
- package/src/lib/editor-views.ts +178 -0
- package/src/lib/export-geometry-wait.test.ts +90 -0
- package/src/lib/export-texture-utils.ts +2 -4
- package/src/lib/floor-edit-session.ts +153 -0
- package/src/lib/floor-footprints.test.ts +563 -0
- package/src/lib/floor-footprints.ts +395 -0
- package/src/lib/floor-foundation-support.test.ts +284 -0
- package/src/lib/floor-opening-draft.ts +218 -0
- package/src/lib/floor-openings.test.ts +216 -0
- package/src/lib/floor-region-geometry.ts +191 -0
- package/src/lib/floor-region-session.test.ts +485 -0
- package/src/lib/floor-region-session.ts +589 -0
- package/src/lib/floor-region-snap.ts +101 -0
- package/src/lib/floor-step-hover.test.ts +32 -0
- package/src/lib/floorplan/annotation-visibility.ts +17 -0
- package/src/lib/floorplan/dimension-drive.test.ts +360 -0
- package/src/lib/floorplan/dimension-drive.ts +479 -0
- package/src/lib/floorplan/floorplan-export.test.ts +12 -8
- package/src/lib/floorplan/floorplan-export.tsx +32 -8
- package/src/lib/floorplan/floorplan-extension.ts +32 -0
- package/src/lib/floorplan/floorplan-mode.test.ts +4 -0
- package/src/lib/floorplan/floorplan-mode.ts +2 -0
- package/src/lib/floorplan/floorplan-pdfkit-document.ts +363 -27
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +177 -8
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +60 -25
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +138 -0
- package/src/lib/floorplan/floorplan-readonly.ts +6 -0
- package/src/lib/floorplan/geometry.ts +13 -6
- package/src/lib/floorplan/index.ts +0 -9
- package/src/lib/floorplan/items.ts +5 -2
- package/src/lib/floorplan/marks.test.ts +213 -0
- package/src/lib/floorplan/marks.ts +238 -0
- package/src/lib/floorplan/open-wall-ends.test.ts +268 -0
- package/src/lib/floorplan/open-wall-ends.ts +162 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Regular.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-GeistMono.txt +92 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-LiberationSans.txt +102 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Regular.ttf +0 -0
- package/src/lib/floorplan/pdfkit-standard-fonts.d.ts +5 -0
- package/src/lib/floorplan/schedules.test.ts +189 -0
- package/src/lib/floorplan/schedules.ts +339 -0
- package/src/lib/floorplan/sheet-export.test.ts +124 -0
- package/src/lib/floorplan/sheet-export.ts +318 -0
- package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +768 -0
- package/src/lib/floorplan/site-plan/contours.test.ts +43 -0
- package/src/lib/floorplan/site-plan/contributors.ts +97 -0
- package/src/lib/floorplan/site-plan/coverage.ts +230 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.test.ts +15 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.tsx +86 -0
- package/src/lib/floorplan/site-plan/front-edge.test.ts +103 -0
- package/src/lib/floorplan/site-plan/front-edge.ts +157 -0
- package/src/lib/floorplan/site-plan/geometry.test.ts +269 -0
- package/src/lib/floorplan/site-plan/geometry.ts +142 -0
- package/src/lib/floorplan/site-plan/index.ts +55 -0
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +483 -0
- package/src/lib/floorplan/site-plan/site-annotations.ts +1084 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +287 -0
- package/src/lib/floorplan/site-plan/site-plan-layer.tsx +138 -0
- package/src/lib/floorplan/site-plan/street-name.test.ts +69 -0
- package/src/lib/floorplan/site-plan/street-name.ts +302 -0
- package/src/lib/floorplan/site-plan/yards.test.ts +70 -0
- package/src/lib/floorplan/types.ts +1 -27
- package/src/lib/follow-pascal-exterior.test-fixture.ts +17 -0
- package/src/lib/follow-pascal-exterior.test.ts +171 -0
- package/src/lib/follow-pascal-exterior.ts +39 -0
- package/src/lib/follow-pascal-presentation.test.ts +203 -0
- package/src/lib/follow-pascal-presentation.ts +129 -0
- package/src/lib/follow-pascal-sight.ts +84 -0
- package/src/lib/follow-pascal-targets.test.ts +406 -0
- package/src/lib/follow-pascal-targets.ts +198 -0
- package/src/lib/follow-pascal.test.ts +350 -0
- package/src/lib/follow-pascal.ts +284 -0
- package/src/lib/fresh-planar-placement.test.ts +148 -0
- package/src/lib/fresh-planar-placement.ts +114 -37
- package/src/lib/geometry-script/author.ts +157 -0
- package/src/lib/geometry-script/client.ts +159 -0
- package/src/lib/geometry-script/finish-scripts.ts +127 -0
- package/src/lib/geometry-script/protocol.ts +13 -0
- package/src/lib/gesture-lifecycle.test.ts +595 -0
- package/src/lib/gesture-lifecycle.ts +290 -0
- package/src/lib/glb-export-zone-holes.test.ts +41 -0
- package/src/lib/glb-export.test.ts +636 -126
- package/src/lib/glb-export.ts +241 -120
- package/src/lib/history.test.ts +18 -7
- package/src/lib/history.ts +7 -0
- package/src/lib/hud-title.ts +109 -0
- package/src/lib/hydration-gated-storage.ts +49 -0
- package/src/lib/ifc-export.test.ts +174 -0
- package/src/lib/ifc-export.ts +201 -0
- package/src/lib/ifc-parts.ts +15 -0
- package/src/lib/inspector-expanded.test.ts +44 -0
- package/src/lib/inspector-expanded.ts +36 -0
- package/src/lib/interaction/scope.ts +22 -0
- package/src/lib/keyboard-pan.test.ts +129 -7
- package/src/lib/keyboard-pan.ts +161 -12
- package/src/lib/level-selection.test.ts +57 -0
- package/src/lib/level-selection.ts +16 -1
- package/src/lib/live-gesture-round-trip.test.ts +360 -0
- package/src/lib/lot/__fixtures__/map-dossier-miami-shores.json +1 -0
- package/src/lib/lot/__fixtures__/map-dossier-stpete.json +1 -0
- package/src/lib/lot/clean-ring.test.ts +187 -0
- package/src/lib/lot/clean-ring.ts +217 -0
- package/src/lib/lot/dossier.test.ts +202 -0
- package/src/lib/lot/dossier.ts +498 -0
- package/src/lib/lot/drop-in.test.ts +326 -0
- package/src/lib/lot/drop-in.ts +392 -0
- package/src/lib/lot/index.ts +43 -0
- package/src/lib/lot/lot-patch.test.ts +340 -0
- package/src/lib/lot/lot-patch.ts +394 -0
- package/src/lib/lot/parcel-provider.ts +43 -0
- package/src/lib/lot/terrain.test.ts +161 -0
- package/src/lib/lot/terrain.ts +266 -0
- package/src/lib/material-catalog-model.ts +3 -1
- package/src/lib/material-paint-panel-model.ts +9 -5
- package/src/lib/material-paint.ts +4 -0
- package/src/lib/measurement-kind.test.ts +6 -8
- package/src/lib/measurements.test.ts +23 -0
- package/src/lib/mezzanine-actions.test.ts +342 -0
- package/src/lib/mezzanine-draft.test.ts +501 -0
- package/src/lib/mezzanine-draft.ts +278 -0
- package/src/lib/mezzanine-messages.ts +26 -0
- package/src/lib/mezzanine-selection.test.ts +207 -0
- package/src/lib/mezzanine-stairs.ts +46 -0
- package/src/lib/model-export.ts +3 -1
- package/src/lib/node-action-movement.ts +17 -6
- package/src/lib/node-display-label.ts +37 -0
- package/src/lib/orbit-floor-clearance.test.ts +201 -0
- package/src/lib/orbit-floor-clearance.ts +154 -0
- package/src/lib/paint-commit-route.test.ts +37 -0
- package/src/lib/paint-commit-route.ts +17 -0
- package/src/lib/paint-eyedropper.test.tsx +165 -0
- package/src/lib/paint-eyedropper.ts +99 -0
- package/src/lib/paint-pass-through.test.ts +122 -0
- package/src/lib/paint-pass-through.ts +78 -0
- package/src/lib/paint-region-hover.ts +25 -0
- package/src/lib/paint-region-mode.ts +154 -0
- package/src/lib/paint-regions.test.ts +166 -0
- package/src/lib/paint-regions.ts +192 -0
- package/src/lib/paint-scope.test.ts +317 -35
- package/src/lib/paint-scope.ts +267 -25
- package/src/lib/panel-rows.ts +21 -0
- package/src/lib/parametric-node-update.ts +16 -7
- package/src/lib/plate-paint-affected.test.ts +300 -0
- package/src/lib/plate-paint-affected.ts +177 -0
- package/src/lib/plugin-chat.ts +65 -0
- package/src/lib/plugin-panels.test.ts +25 -0
- package/src/lib/plugin-panels.ts +94 -0
- package/src/lib/point-ask/__fixtures__/consumer-browser.tsx +76 -0
- package/src/lib/point-ask/__fixtures__/house.ts +81 -0
- package/src/lib/point-ask/ask-created.test.ts +195 -0
- package/src/lib/point-ask/ask-flow.test.ts +380 -0
- package/src/lib/point-ask/build-context.test.ts +173 -0
- package/src/lib/point-ask/build-context.ts +94 -0
- package/src/lib/point-ask/capture-crop.ts +129 -0
- package/src/lib/point-ask/choreography.ts +69 -0
- package/src/lib/point-ask/compare.test.ts +145 -0
- package/src/lib/point-ask/compare.ts +73 -0
- package/src/lib/point-ask/describe-target.test.ts +165 -0
- package/src/lib/point-ask/describe-target.ts +234 -0
- package/src/lib/point-ask/dictation.ts +79 -0
- package/src/lib/point-ask/edge-marker.test.ts +61 -0
- package/src/lib/point-ask/edge-marker.ts +27 -0
- package/src/lib/point-ask/frame-loop.ts +25 -0
- package/src/lib/point-ask/history-marks.test.ts +94 -0
- package/src/lib/point-ask/history-marks.ts +39 -0
- package/src/lib/point-ask/host-api.ts +32 -0
- package/src/lib/point-ask/outline-box.test.ts +30 -0
- package/src/lib/point-ask/outline-box.ts +11 -0
- package/src/lib/point-ask/overlay-metrics.ts +6 -0
- package/src/lib/point-ask/pick.test.ts +220 -0
- package/src/lib/point-ask/pick.ts +121 -0
- package/src/lib/point-ask/pin-machine.test.ts +494 -0
- package/src/lib/point-ask/pin-machine.ts +217 -0
- package/src/lib/point-ask/placement.test.ts +203 -0
- package/src/lib/point-ask/placement.ts +141 -0
- package/src/lib/point-ask/pointer-tracker.ts +34 -0
- package/src/lib/point-ask/projector.ts +83 -0
- package/src/lib/point-ask/region.test.ts +157 -0
- package/src/lib/point-ask/region.ts +136 -0
- package/src/lib/point-ask/retry.test.ts +217 -0
- package/src/lib/point-ask/retry.ts +37 -0
- package/src/lib/point-ask/scene-commit.test.ts +312 -0
- package/src/lib/point-ask/scene-commit.ts +32 -0
- package/src/lib/point-ask/session.ts +1391 -0
- package/src/lib/point-ask/spring-keyframes.test.ts +45 -0
- package/src/lib/point-ask/spring-keyframes.ts +38 -0
- package/src/lib/point-ask/spring.test.ts +63 -0
- package/src/lib/point-ask/spring.ts +107 -0
- package/src/lib/point-ask/suggestions.test.ts +80 -0
- package/src/lib/point-ask/suggestions.ts +125 -0
- package/src/lib/point-ask/target-scope.test.ts +622 -0
- package/src/lib/point-ask/target-scope.ts +338 -0
- package/src/lib/point-ask/types.ts +48 -0
- package/src/lib/portable-export.test.ts +34 -1
- package/src/lib/portable-export.ts +119 -53
- package/src/lib/print-feature-thickness.ts +10 -1
- package/src/lib/print-roof-solids.test.ts +39 -1
- package/src/lib/print-roof-solids.ts +43 -10
- package/src/lib/print-shell-compiler-manifold-core.ts +1 -1
- package/src/lib/print-shell-compiler.ts +1 -0
- package/src/lib/registered-draft-snap.ts +12 -0
- package/src/lib/roof-duplication.ts +10 -2
- package/src/lib/room-assembly-overlay.test.ts +275 -0
- package/src/lib/room-assembly-overlay.ts +265 -0
- package/src/lib/room-built-on.test.ts +379 -0
- package/src/lib/room-built-on.ts +138 -0
- package/src/lib/room-construction-commands.ts +712 -0
- package/src/lib/room-construction.test.ts +746 -0
- package/src/lib/room-construction.ts +385 -0
- package/src/lib/room-controls-phase6.test.ts +630 -0
- package/src/lib/room-divide-session.ts +523 -0
- package/src/lib/room-handle-drag.ts +1068 -0
- package/src/lib/room-name-catalog.test.ts +90 -0
- package/src/lib/room-name-catalog.ts +168 -0
- package/src/lib/room-push-dimensions.test.ts +311 -0
- package/src/lib/room-push-dimensions.ts +251 -0
- package/src/lib/room-selection-commands.ts +62 -0
- package/src/lib/room-selection.test.ts +1017 -0
- package/src/lib/room-selection.ts +417 -0
- package/src/lib/room-structure-commands.ts +94 -0
- package/src/lib/room-structure.test.ts +921 -0
- package/src/lib/room-transform-session.ts +570 -0
- package/src/lib/room-zone-routing.test.ts +294 -0
- package/src/lib/room-zone-routing.ts +197 -0
- package/src/lib/scene-clipboard.test.ts +610 -1
- package/src/lib/scene-clipboard.ts +269 -58
- package/src/lib/scene-material-refs.test.ts +79 -0
- package/src/lib/scene-material-refs.ts +79 -0
- package/src/lib/scene.test.ts +24 -36
- package/src/lib/scene.ts +33 -57
- package/src/lib/selection-routing.test.ts +129 -45
- package/src/lib/selection-routing.ts +54 -75
- package/src/lib/session-writes.test.ts +120 -0
- package/src/lib/session-writes.ts +205 -0
- package/src/lib/sfx-bus.ts +13 -0
- package/src/lib/sfx-player.test.ts +70 -9
- package/src/lib/sfx-player.ts +6 -0
- package/src/lib/sidebar-panel.ts +62 -0
- package/src/lib/site-boundary.test.ts +4 -6
- package/src/lib/site-boundary.ts +1 -1
- package/src/lib/snapping-mode.test.ts +4 -0
- package/src/lib/snapping-mode.ts +8 -1
- package/src/lib/stair-levels.test.ts +25 -0
- package/src/lib/stair-levels.ts +22 -3
- package/src/lib/terrace-draft.test.ts +145 -0
- package/src/lib/terrace-draft.ts +214 -0
- package/src/lib/tool-cursor-icon.test.ts +16 -0
- package/src/lib/tool-cursor-icon.ts +11 -0
- package/src/lib/typing-target.ts +9 -0
- package/src/lib/units.ts +77 -39
- package/src/lib/vector-edges.test.ts +58 -0
- package/src/lib/vector-edges.ts +400 -0
- package/src/lib/viewer-selection.test.ts +577 -0
- package/src/lib/viewer-selection.ts +580 -0
- package/src/lib/wall-draw-variant.test.ts +71 -0
- package/src/lib/wall-draw-variant.ts +91 -0
- package/src/lib/wall-paint-region-session.ts +246 -0
- package/src/lib/wall-paint-region.test.ts +373 -0
- package/src/lib/wall-placement-pointer.test.ts +42 -0
- package/src/lib/wall-placement-pointer.ts +57 -0
- package/src/lib/wall-polygon-draft.test.ts +262 -0
- package/src/lib/wall-polygon-draft.ts +286 -0
- package/src/lib/wall-push.test.ts +453 -0
- package/src/lib/wall-region-face.ts +352 -0
- package/src/lib/wall-region-handles.test.ts +304 -0
- package/src/lib/wall-region-handles.ts +368 -0
- package/src/lib/wall-region-list.test.ts +119 -0
- package/src/lib/wall-region-list.ts +136 -0
- package/src/lib/wall-region-snap.ts +110 -0
- package/src/lib/window-changes.ts +35 -0
- package/src/lib/window-interaction.ts +14 -0
- package/src/lib/zone-content.ts +4 -1
- package/src/lib/zone-name.test.ts +17 -0
- package/src/lib/zone-name.ts +17 -0
- package/src/store/opens-in-select-mode.test.ts +223 -0
- package/src/store/paint-mode.test.ts +188 -0
- package/src/store/terrain-sculpt-mode.test.ts +19 -21
- package/src/store/tool-mode.test.ts +137 -6
- package/src/store/use-audio.test.ts +146 -0
- package/src/store/use-audio.tsx +127 -3
- package/src/store/use-construction-reveal.test.ts +173 -0
- package/src/store/use-construction-reveal.ts +145 -0
- package/src/store/use-corner-snap-hint.test.ts +24 -0
- package/src/store/use-corner-snap-hint.ts +47 -0
- package/src/store/use-delete-confirmation.ts +28 -1
- package/src/store/use-drawing-view.test.ts +24 -3
- package/src/store/use-drawing-view.ts +29 -1
- package/src/store/use-editor.tsx +432 -321
- package/src/store/use-floorplan-draft-preview.ts +13 -0
- package/src/store/use-floorplan-marquee.ts +12 -2
- package/src/store/use-follow-pascal.test.ts +62 -0
- package/src/store/use-follow-pascal.ts +53 -0
- package/src/store/use-point-ask.ts +247 -0
- package/src/store/use-quick-measurement-hud.ts +3 -3
- package/src/store/use-stair-build-preview.ts +33 -5
- package/src/store/use-wall-move-ghosts.ts +4 -2
- package/src/store/use-wall-region-handles-selection.test.ts +222 -0
- package/src/store/use-wall-region-handles-selection.ts +139 -0
- package/src/store/view-layout.test.ts +201 -0
- package/src/store/view-layout.ts +199 -0
- package/src/test-utils/immediate-animation-frames.ts +16 -0
- package/src/test-utils/live-gesture-round-trip.ts +224 -0
- package/src/test-utils/render-current-markup.tsx +16 -0
- package/src/test-utils/selection-harness.ts +82 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +0 -499
- package/src/components/systems/zone/zone-label-editor-system.tsx +0 -333
- package/src/components/tools/fence/fence-drafting.test.ts +0 -100
- package/src/components/tools/fence/fence-drafting.ts +0 -266
- package/src/components/tools/stair/stair-click-guard.test.ts +0 -58
- package/src/components/tools/stair/stair-click-guard.ts +0 -72
- package/src/components/tools/stair/stair-defaults.ts +0 -18
- package/src/components/tools/stair/stair-tool.tsx +0 -634
- package/src/components/ui/sidebar/panels/plugins-panel.tsx +0 -218
- package/src/components/viewer/viewer-units-panel.tsx +0 -116
- package/src/lib/floorplan/stairs.ts +0 -489
- package/src/lib/level-duplication.test.ts +0 -144
- package/src/lib/level-duplication.ts +0 -195
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import '../../../three-types'
|
|
2
2
|
|
|
3
3
|
import { Icon } from '@iconify/react'
|
|
4
|
-
import {
|
|
5
|
-
type AnyNodeId,
|
|
6
|
-
emitter,
|
|
7
|
-
type GridEvent,
|
|
8
|
-
sceneRegistry,
|
|
9
|
-
useScene,
|
|
10
|
-
type ZoneNode,
|
|
11
|
-
} from '@pascal-app/core'
|
|
4
|
+
import { type AnyNodeId, emitter, type GridEvent, sceneRegistry, useScene } from '@pascal-app/core'
|
|
12
5
|
import { useViewer } from '@pascal-app/viewer'
|
|
13
6
|
import { useThree } from '@react-three/fiber'
|
|
14
7
|
import { useCallback, useEffect, useRef } from 'react'
|
|
@@ -26,9 +19,10 @@ import {
|
|
|
26
19
|
Vector2,
|
|
27
20
|
Vector3,
|
|
28
21
|
} from 'three'
|
|
22
|
+
import { markToolCancelConsumed } from '../../../hooks/use-keyboard'
|
|
29
23
|
import { EDITOR_LAYER } from '../../../lib/constants'
|
|
30
24
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
|
31
|
-
import
|
|
25
|
+
import useInteractionScope from '../../../store/use-interaction-scope'
|
|
32
26
|
import { CursorSphere } from '../shared/cursor-sphere'
|
|
33
27
|
import { isBoxSelectPointerSuppressed, markBoxSelectHandled } from './box-select-state'
|
|
34
28
|
import { collectSelectableCandidateIds } from './select-candidates'
|
|
@@ -297,6 +291,7 @@ export const PlaneBoxSelectTool: React.FC = () => {
|
|
|
297
291
|
const currentPoint = useRef(new Vector3())
|
|
298
292
|
const pointerDown = useRef(false)
|
|
299
293
|
const isDragging = useRef(false)
|
|
294
|
+
const ownedGesture = useRef<object | null>(null)
|
|
300
295
|
const startClientX = useRef(0)
|
|
301
296
|
const startClientY = useRef(0)
|
|
302
297
|
const gridY = useRef(0)
|
|
@@ -320,11 +315,36 @@ export const PlaneBoxSelectTool: React.FC = () => {
|
|
|
320
315
|
const resetDrag = useCallback(() => {
|
|
321
316
|
pointerDown.current = false
|
|
322
317
|
isDragging.current = false
|
|
323
|
-
|
|
318
|
+
const gesture = ownedGesture.current
|
|
319
|
+
if (gesture)
|
|
320
|
+
useInteractionScope.getState().endIf(() => useInteractionScope.getState().gesture === gesture)
|
|
321
|
+
ownedGesture.current = null
|
|
322
|
+
previousGridPosition.current = null
|
|
323
|
+
if (rectFillRef.current) rectFillRef.current.visible = false
|
|
324
324
|
outlineRef.current.visible = false
|
|
325
325
|
syncPreviewSelectedIds([])
|
|
326
326
|
}, [syncPreviewSelectedIds])
|
|
327
327
|
|
|
328
|
+
useEffect(() => {
|
|
329
|
+
const onCancel = () => {
|
|
330
|
+
if (!pointerDown.current) return
|
|
331
|
+
if (
|
|
332
|
+
(!ownedGesture.current && useInteractionScope.getState().scope.kind === 'idle') ||
|
|
333
|
+
(ownedGesture.current && useInteractionScope.getState().gesture === ownedGesture.current)
|
|
334
|
+
) {
|
|
335
|
+
markToolCancelConsumed()
|
|
336
|
+
}
|
|
337
|
+
resetDrag()
|
|
338
|
+
}
|
|
339
|
+
emitter.on('tool:cancel', onCancel)
|
|
340
|
+
window.addEventListener('pointercancel', resetDrag)
|
|
341
|
+
return () => {
|
|
342
|
+
emitter.off('tool:cancel', onCancel)
|
|
343
|
+
window.removeEventListener('pointercancel', resetDrag)
|
|
344
|
+
resetDrag()
|
|
345
|
+
}
|
|
346
|
+
}, [resetDrag])
|
|
347
|
+
|
|
328
348
|
const raycastToGround = useCallback(
|
|
329
349
|
(event: PointerEvent): Vector3 | null => {
|
|
330
350
|
const rect = gl.domElement.getBoundingClientRect()
|
|
@@ -342,12 +362,11 @@ export const PlaneBoxSelectTool: React.FC = () => {
|
|
|
342
362
|
useEffect(() => {
|
|
343
363
|
const outline = outlineRef.current
|
|
344
364
|
return () => {
|
|
345
|
-
|
|
346
|
-
setPreviewSelectedIds([])
|
|
365
|
+
resetDrag()
|
|
347
366
|
outline.geometry.dispose()
|
|
348
367
|
;(outline.material as LineBasicMaterial).dispose()
|
|
349
368
|
}
|
|
350
|
-
}, [
|
|
369
|
+
}, [resetDrag])
|
|
351
370
|
|
|
352
371
|
useEffect(() => {
|
|
353
372
|
const unsubscribe = useViewer.subscribe((state) => {
|
|
@@ -440,15 +459,7 @@ export const PlaneBoxSelectTool: React.FC = () => {
|
|
|
440
459
|
|
|
441
460
|
const ids = collectNodeIdsInPlaneBounds(bounds)
|
|
442
461
|
const shouldAppend = event.metaKey || event.ctrlKey || event.shiftKey
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (phase === 'structure' && structureLayer === 'zones') {
|
|
446
|
-
if (ids.length > 0) {
|
|
447
|
-
useViewer.getState().setSelection({ zoneId: ids[0] as ZoneNode['id'] })
|
|
448
|
-
} else if (!shouldAppend) {
|
|
449
|
-
useViewer.getState().setSelection({ zoneId: null })
|
|
450
|
-
}
|
|
451
|
-
} else if (shouldAppend) {
|
|
462
|
+
if (shouldAppend) {
|
|
452
463
|
const currentIds = useViewer.getState().selection.selectedIds
|
|
453
464
|
useViewer.getState().setSelection({
|
|
454
465
|
selectedIds: Array.from(new Set([...currentIds, ...ids])),
|
|
@@ -507,6 +518,8 @@ export const PlaneBoxSelectTool: React.FC = () => {
|
|
|
507
518
|
const dy = nativeEvent.clientY - startClientY.current
|
|
508
519
|
if (!isDragging.current && Math.hypot(dx, dy) >= DRAG_THRESHOLD_PX) {
|
|
509
520
|
isDragging.current = true
|
|
521
|
+
useInteractionScope.getState().begin({ kind: 'box-select' })
|
|
522
|
+
ownedGesture.current = useInteractionScope.getState().gesture
|
|
510
523
|
}
|
|
511
524
|
|
|
512
525
|
if (isDragging.current && rectFillRef.current && outlineRef.current) {
|
|
@@ -47,10 +47,7 @@ describe('selectable candidates', () => {
|
|
|
47
47
|
},
|
|
48
48
|
previewSelectedIds: [],
|
|
49
49
|
})
|
|
50
|
-
useEditor.setState({
|
|
51
|
-
phase: 'structure',
|
|
52
|
-
structureLayer: 'elements',
|
|
53
|
-
})
|
|
50
|
+
useEditor.setState({ phase: 'building' })
|
|
54
51
|
})
|
|
55
52
|
|
|
56
53
|
test('includes building-scoped elevators for the active level building', () => {
|
|
@@ -106,4 +103,40 @@ describe('selectable candidates', () => {
|
|
|
106
103
|
|
|
107
104
|
expect(collectSelectableCandidateIds()).toContain('elevator_test')
|
|
108
105
|
})
|
|
106
|
+
|
|
107
|
+
test('Building box selection includes structural and furnishing kinds on the active level', () => {
|
|
108
|
+
for (const category of ['structure', 'furnish'] as const) {
|
|
109
|
+
const kind = `box-select-test-${category}`
|
|
110
|
+
if (!nodeRegistry.has(kind)) {
|
|
111
|
+
registerNode({
|
|
112
|
+
kind,
|
|
113
|
+
schemaVersion: 1,
|
|
114
|
+
schema: z.object({ type: z.literal(kind) }) as never,
|
|
115
|
+
category,
|
|
116
|
+
defaults: () => ({}),
|
|
117
|
+
capabilities: { selectable: {} },
|
|
118
|
+
} as AnyNodeDefinition)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
useScene.setState({
|
|
122
|
+
nodes: {
|
|
123
|
+
level_test: {
|
|
124
|
+
id: 'level_test',
|
|
125
|
+
type: 'level',
|
|
126
|
+
parentId: 'building_test',
|
|
127
|
+
children: ['wall_pick', 'item_pick', 'item_hidden'],
|
|
128
|
+
},
|
|
129
|
+
wall_pick: { id: 'wall_pick', type: 'box-select-test-structure', parentId: 'level_test' },
|
|
130
|
+
item_pick: { id: 'item_pick', type: 'box-select-test-furnish', parentId: 'level_test' },
|
|
131
|
+
item_hidden: {
|
|
132
|
+
id: 'item_hidden',
|
|
133
|
+
type: 'box-select-test-furnish',
|
|
134
|
+
parentId: 'level_test',
|
|
135
|
+
visible: false,
|
|
136
|
+
},
|
|
137
|
+
item_other: { id: 'item_other', type: 'box-select-test-furnish', parentId: 'level_other' },
|
|
138
|
+
} as unknown as Record<string, AnyNode>,
|
|
139
|
+
})
|
|
140
|
+
expect(collectSelectableCandidateIds().sort()).toEqual(['item_pick', 'wall_pick'])
|
|
141
|
+
})
|
|
109
142
|
})
|
|
@@ -19,17 +19,29 @@ function isVisibleSelectableNode(node: AnyNode): boolean {
|
|
|
19
19
|
export function collectSelectableCandidateIds(): string[] {
|
|
20
20
|
const { levelId } = useViewer.getState().selection
|
|
21
21
|
const { nodes } = useScene.getState()
|
|
22
|
-
const { phase
|
|
22
|
+
const { phase } = useEditor.getState()
|
|
23
23
|
const result: string[] = []
|
|
24
24
|
const seen = new Set<string>()
|
|
25
25
|
const addNode = (node: AnyNode | undefined) => {
|
|
26
|
-
if (
|
|
26
|
+
if (
|
|
27
|
+
!node ||
|
|
28
|
+
node.type === 'zone' ||
|
|
29
|
+
seen.has(node.id) ||
|
|
30
|
+
(node as { visible?: boolean }).visible === false
|
|
31
|
+
)
|
|
32
|
+
return
|
|
27
33
|
seen.add(node.id)
|
|
28
34
|
result.push(node.id)
|
|
29
35
|
}
|
|
30
36
|
const visitLevelDescendant = (id: AnyNodeId) => {
|
|
31
37
|
const node = nodes[id]
|
|
32
|
-
if (
|
|
38
|
+
if (
|
|
39
|
+
!node ||
|
|
40
|
+
node.type === 'zone' ||
|
|
41
|
+
seen.has(node.id) ||
|
|
42
|
+
(node as { visible?: boolean }).visible === false
|
|
43
|
+
)
|
|
44
|
+
return
|
|
33
45
|
|
|
34
46
|
if (isRegistrySelectable(node.type)) {
|
|
35
47
|
addNode(node)
|
|
@@ -52,14 +64,6 @@ export function collectSelectableCandidateIds(): string[] {
|
|
|
52
64
|
const levelNode = nodes[levelId as AnyNodeId] as LevelNode | undefined
|
|
53
65
|
if (levelNode?.type !== 'level') return []
|
|
54
66
|
|
|
55
|
-
if (phase === 'structure' && structureLayer === 'zones') {
|
|
56
|
-
for (const childId of levelNode.children) {
|
|
57
|
-
const node = nodes[childId as AnyNodeId]
|
|
58
|
-
if (node?.type === 'zone') addNode(node)
|
|
59
|
-
}
|
|
60
|
-
return result
|
|
61
|
-
}
|
|
62
|
-
|
|
63
67
|
for (const childId of levelNode.children) {
|
|
64
68
|
visitLevelDescendant(childId as AnyNodeId)
|
|
65
69
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Html } from '@react-three/drei'
|
|
2
2
|
import type { ThreeElements } from '@react-three/fiber'
|
|
3
3
|
import { forwardRef } from 'react'
|
|
4
|
-
import type
|
|
4
|
+
import { CircleGeometry, CylinderGeometry, type Group, SphereGeometry } from 'three'
|
|
5
5
|
import { furnishTools } from '../../../components/ui/action-menu/furnish-tools'
|
|
6
6
|
import { tools } from '../../../components/ui/action-menu/structure-tools'
|
|
7
7
|
import { EDITOR_LAYER } from '../../../lib/constants'
|
|
8
|
+
import { wallCursorIcon } from '../../../lib/tool-cursor-icon'
|
|
8
9
|
import useEditor from '../../../store/use-editor'
|
|
9
10
|
import useWallSnapIndicator from '../../../store/use-wall-snap-indicator'
|
|
10
11
|
|
|
@@ -25,6 +26,19 @@ interface CursorSphereProps extends Omit<ThreeElements['group'], 'ref'> {
|
|
|
25
26
|
tooltipContent?: React.ReactNode
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
// One geometry each for the life of the module: the vertical line used to
|
|
30
|
+
// be a `<cylinderGeometry args={[…, height, 8]}>` rebuilt on EVERY height
|
|
31
|
+
// change (the cursor's drop to the floor moves with the pointer), and the
|
|
32
|
+
// ring / dot / tip were built and disposed on every toggle — WebGPU then
|
|
33
|
+
// drew a frame whose vertex buffers had just been destroyed ("Vertex
|
|
34
|
+
// buffer slot 0 required by [RenderPipeline "renderPipeline_MeshBasicMaterial_…"]
|
|
35
|
+
// was not set … DrawIndexed(96 …)" — the 8-segment cylinder's 96 indices).
|
|
36
|
+
// The line is a unit cylinder scaled to `height` instead.
|
|
37
|
+
const DOT_GEOMETRY = new CircleGeometry(0.06, 32)
|
|
38
|
+
const RING_GEOMETRY = new CircleGeometry(0.2, 32)
|
|
39
|
+
const LINE_GEOMETRY = new CylinderGeometry(0.01, 0.01, 1, 8)
|
|
40
|
+
const TIP_GEOMETRY = new SphereGeometry(0.08, 20, 14)
|
|
41
|
+
|
|
28
42
|
export const CursorSphere = forwardRef<Group, CursorSphereProps>(function CursorSphere(
|
|
29
43
|
{
|
|
30
44
|
color = '#818cf8',
|
|
@@ -45,11 +59,14 @@ export const CursorSphere = forwardRef<Group, CursorSphereProps>(function Cursor
|
|
|
45
59
|
// (green square at a corner); hide the cursor's ground dot/ring so it
|
|
46
60
|
// doesn't sit on top of that glyph.
|
|
47
61
|
const isSnapping = useWallSnapIndicator((s) => s.point !== null)
|
|
62
|
+
const wallMode = useEditor((s) => s.continuationByContext.wall)
|
|
48
63
|
|
|
49
|
-
// Find the icon for the current tool
|
|
50
|
-
let activeToolConfig = null
|
|
64
|
+
// Find the icon for the current tool — the wall shows its Rooms variant.
|
|
65
|
+
let activeToolConfig: { label: string; iconSrc: string } | null | undefined = null
|
|
51
66
|
if (mode === 'build' && tool) {
|
|
52
|
-
if (tool === '
|
|
67
|
+
if (tool === 'wall') {
|
|
68
|
+
activeToolConfig = wallCursorIcon(wallMode)
|
|
69
|
+
} else if (tool === 'item' && catalogCategory) {
|
|
53
70
|
activeToolConfig = furnishTools.find((t) => t.catalogCategory === catalogCategory)
|
|
54
71
|
} else {
|
|
55
72
|
activeToolConfig = tools.find((t) => t.id === tool)
|
|
@@ -67,8 +84,7 @@ export const CursorSphere = forwardRef<Group, CursorSphereProps>(function Cursor
|
|
|
67
84
|
<group rotation={[-Math.PI / 2, 0, 0]}>
|
|
68
85
|
{/* Center dot — at the ground unless the placement point is elevated */}
|
|
69
86
|
{!dotAtTip && (
|
|
70
|
-
<mesh layers={EDITOR_LAYER} renderOrder={2}>
|
|
71
|
-
<circleGeometry args={[0.06, 32]} />
|
|
87
|
+
<mesh geometry={DOT_GEOMETRY} layers={EDITOR_LAYER} renderOrder={2}>
|
|
72
88
|
<meshBasicMaterial
|
|
73
89
|
color={color}
|
|
74
90
|
depthTest={false}
|
|
@@ -80,8 +96,7 @@ export const CursorSphere = forwardRef<Group, CursorSphereProps>(function Cursor
|
|
|
80
96
|
)}
|
|
81
97
|
|
|
82
98
|
{/* Outer ring / glow */}
|
|
83
|
-
<mesh layers={EDITOR_LAYER} renderOrder={2}>
|
|
84
|
-
<circleGeometry args={[0.2, 32]} />
|
|
99
|
+
<mesh geometry={RING_GEOMETRY} layers={EDITOR_LAYER} renderOrder={2}>
|
|
85
100
|
<meshBasicMaterial
|
|
86
101
|
color={color}
|
|
87
102
|
depthTest={false}
|
|
@@ -95,8 +110,13 @@ export const CursorSphere = forwardRef<Group, CursorSphereProps>(function Cursor
|
|
|
95
110
|
|
|
96
111
|
{/* Vertical line */}
|
|
97
112
|
{height > 0 && (
|
|
98
|
-
<mesh
|
|
99
|
-
|
|
113
|
+
<mesh
|
|
114
|
+
geometry={LINE_GEOMETRY}
|
|
115
|
+
layers={EDITOR_LAYER}
|
|
116
|
+
position={[0, height / 2, 0]}
|
|
117
|
+
renderOrder={2}
|
|
118
|
+
scale={[1, height, 1]}
|
|
119
|
+
>
|
|
100
120
|
<meshBasicMaterial
|
|
101
121
|
color={color}
|
|
102
122
|
depthTest={false}
|
|
@@ -110,8 +130,12 @@ export const CursorSphere = forwardRef<Group, CursorSphereProps>(function Cursor
|
|
|
110
130
|
{/* Bright marker dot at the tip of the line — the actual placement
|
|
111
131
|
point, riding at the cursor while the line drops to the floor. */}
|
|
112
132
|
{dotAtTip && height > 0 && (
|
|
113
|
-
<mesh
|
|
114
|
-
|
|
133
|
+
<mesh
|
|
134
|
+
geometry={TIP_GEOMETRY}
|
|
135
|
+
layers={EDITOR_LAYER}
|
|
136
|
+
position={[0, height, 0]}
|
|
137
|
+
renderOrder={2}
|
|
138
|
+
>
|
|
115
139
|
<meshBasicMaterial color={color} depthTest={false} depthWrite={false} />
|
|
116
140
|
</mesh>
|
|
117
141
|
)}
|
|
@@ -458,8 +458,7 @@ export const SiteBoundaryEditor: React.FC = () => {
|
|
|
458
458
|
|
|
459
459
|
const exitSiteEditing = useCallback(() => {
|
|
460
460
|
const editor = useEditor.getState()
|
|
461
|
-
editor.setPhase('
|
|
462
|
-
editor.setStructureLayer('elements')
|
|
461
|
+
editor.setPhase('building')
|
|
463
462
|
editor.setMode('select')
|
|
464
463
|
}, [])
|
|
465
464
|
|
|
@@ -5,17 +5,24 @@ import {
|
|
|
5
5
|
type CeilingNode,
|
|
6
6
|
createSceneApi,
|
|
7
7
|
type FenceNode,
|
|
8
|
+
isStoreyContent,
|
|
9
|
+
type LevelNode,
|
|
8
10
|
nodeRegistry,
|
|
9
11
|
type SlabNode,
|
|
12
|
+
storeyOf,
|
|
10
13
|
useScene,
|
|
11
14
|
type WallNode,
|
|
15
|
+
type ZoneNode,
|
|
12
16
|
} from '@pascal-app/core'
|
|
13
17
|
import { useViewer } from '@pascal-app/viewer'
|
|
14
|
-
import { type ComponentType, lazy, Suspense, useMemo } from 'react'
|
|
18
|
+
import { type ComponentType, lazy, Suspense, useEffect, useMemo } from 'react'
|
|
15
19
|
import { useRegisteredToolEnabled } from '../../hooks/use-registered-tool-enabled'
|
|
20
|
+
import { useCeilingEditCeilingId } from '../../lib/ceiling-edit-session'
|
|
16
21
|
import type { ReshapeKind } from '../../lib/interaction/scope'
|
|
22
|
+
import { selectPlacedNode } from '../../lib/level-selection'
|
|
23
|
+
import { paintRegionTargets, usePaintRegionMode } from '../../lib/paint-region-mode'
|
|
17
24
|
import { siteBoundaryHandlesEnabled } from '../../lib/site-boundary'
|
|
18
|
-
import useEditor, { type
|
|
25
|
+
import useEditor, { type Tool } from '../../store/use-editor'
|
|
19
26
|
import useInteractionScope, {
|
|
20
27
|
useControlPointReshape,
|
|
21
28
|
useEditingHole,
|
|
@@ -29,10 +36,14 @@ import useInteractionScope, {
|
|
|
29
36
|
} from '../../store/use-interaction-scope'
|
|
30
37
|
import { Alignment3DGuideLayer } from '../editor/alignment-3d-guide-layer'
|
|
31
38
|
import { Elevation3DGuideLayer } from '../editor/elevation-3d-guide-layer'
|
|
39
|
+
import { FloorOpeningsOverlay3D } from '../editor/floor-openings-overlay'
|
|
40
|
+
import { FloorRegionControls3D } from '../editor/floor-region-controls'
|
|
41
|
+
import { OpenWallEnds3DLayer } from '../editor/open-wall-ends-3d-layer'
|
|
32
42
|
import { OpeningGuides3DLayer } from '../editor/opening-guides-3d-layer'
|
|
33
43
|
import { WallSnapBeaconLayer } from '../editor/wall-snap-beacon-layer'
|
|
34
44
|
import { ElevatorTool } from './elevator/elevator-tool'
|
|
35
45
|
import { MoveTool } from './item/move-tool'
|
|
46
|
+
import { WallPaintRegionTool } from './paint-region/wall-paint-region-tool'
|
|
36
47
|
import { RegistryToolProvider } from './registry-tool-context'
|
|
37
48
|
import {
|
|
38
49
|
getRegistryAffordanceTool,
|
|
@@ -41,7 +52,6 @@ import {
|
|
|
41
52
|
import { FacingPoseIndicator } from './shared/facing-pose-indicator'
|
|
42
53
|
import { SiteBoundaryEditor } from './site/site-boundary-editor'
|
|
43
54
|
import { TerrainSculptTool } from './site/terrain-sculpt-tool'
|
|
44
|
-
import { StairTool } from './stair/stair-tool'
|
|
45
55
|
import { ZoneBoundaryEditor } from './zone/zone-boundary-editor'
|
|
46
56
|
import { ZoneTool } from './zone/zone-tool'
|
|
47
57
|
|
|
@@ -100,21 +110,16 @@ function getRegistryTool(tool: Tool | null): ComponentType | null {
|
|
|
100
110
|
// Legacy tool fallbacks — kinds whose placement tools haven't migrated
|
|
101
111
|
// to `def.tool` yet. Wall / fence / slab / ceiling / door / window /
|
|
102
112
|
// item / shelf / spawn now go through the registry path above.
|
|
103
|
-
const tools:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
},
|
|
107
|
-
structure: {
|
|
108
|
-
stair: StairTool,
|
|
109
|
-
zone: ZoneTool,
|
|
110
|
-
},
|
|
111
|
-
furnish: {},
|
|
113
|
+
const tools: Partial<Record<Tool, React.FC>> = {
|
|
114
|
+
'property-line': SiteBoundaryEditor,
|
|
115
|
+
zone: ZoneTool,
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
export const ToolManager: React.FC = () => {
|
|
115
119
|
const phase = useEditor((state) => state.phase)
|
|
116
120
|
const mode = useEditor((state) => state.mode)
|
|
117
121
|
const tool = useEditor((state) => state.tool)
|
|
122
|
+
const paintsWallRegions = usePaintRegionMode((state) => paintRegionTargets(state.mode).wall)
|
|
118
123
|
const registeredToolEnabled = useRegisteredToolEnabled(tool)
|
|
119
124
|
const movingNode = useMovingNode()
|
|
120
125
|
const registryToolOwnsPlacement = useInteractionScope(
|
|
@@ -157,23 +162,41 @@ export const ToolManager: React.FC = () => {
|
|
|
157
162
|
}
|
|
158
163
|
}, [reshapingNode, tangentReshape])
|
|
159
164
|
const editingHole = useEditingHole()
|
|
160
|
-
const
|
|
165
|
+
const editCeilingId = useCeilingEditCeilingId()
|
|
166
|
+
// A zone picked as a zone, from the Scene list (zoneId) or its area or pill (sole selection).
|
|
167
|
+
const zoneSelectionId = useViewer((state) => state.selection.zoneId)
|
|
168
|
+
const soleSelectedId = useViewer((state) =>
|
|
169
|
+
state.selection.selectedIds.length === 1 ? state.selection.selectedIds[0] : undefined,
|
|
170
|
+
)
|
|
171
|
+
const soleSelectedZone = useScene((state) =>
|
|
172
|
+
soleSelectedId && state.nodes[soleSelectedId as AnyNodeId]?.type === 'zone'
|
|
173
|
+
? (soleSelectedId as ZoneNode['id'])
|
|
174
|
+
: null,
|
|
175
|
+
)
|
|
176
|
+
const selectedZoneId = zoneSelectionId ?? soleSelectedZone
|
|
161
177
|
const selectedIds = useViewer((state) => state.selection.selectedIds)
|
|
162
178
|
const buildingId = useViewer((state) => state.selection.buildingId)
|
|
163
179
|
const activeLevelId = useViewer((state) => state.selection.levelId)
|
|
164
180
|
const unit = useViewer((state) => state.unit)
|
|
165
181
|
const setSelection = useViewer((state) => state.setSelection)
|
|
166
182
|
const nodes = useScene((state) => state.nodes)
|
|
183
|
+
// Placing a roof makes its level active (selectPlacedNode); a wall, room, slab, stair or item tool
|
|
184
|
+
// armed there, then or after a level change, draws on the storey the roof level covers.
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
if (!(tool && activeLevelId && isStoreyContent(tool, undefined))) return
|
|
187
|
+
const storeyId = storeyOf(useScene.getState().nodes, activeLevelId)
|
|
188
|
+
if (storeyId !== activeLevelId) setSelection({ levelId: storeyId as LevelNode['id'] })
|
|
189
|
+
}, [tool, activeLevelId, setSelection])
|
|
167
190
|
const registrySceneApi = useMemo(() => createSceneApi(useScene), [])
|
|
168
191
|
const registryToolContext = useMemo(
|
|
169
192
|
() => ({
|
|
170
193
|
activeLevelId: activeLevelId ?? null,
|
|
171
194
|
isCameraDragging: () => useViewer.getState().cameraDragging,
|
|
172
195
|
sceneApi: registrySceneApi,
|
|
173
|
-
selectNode:
|
|
196
|
+
selectNode: selectPlacedNode,
|
|
174
197
|
unit,
|
|
175
198
|
}),
|
|
176
|
-
[activeLevelId, registrySceneApi,
|
|
199
|
+
[activeLevelId, registrySceneApi, unit],
|
|
177
200
|
)
|
|
178
201
|
|
|
179
202
|
// Building transform for the local group — all building-relative tools live inside this group
|
|
@@ -213,12 +236,14 @@ export const ToolManager: React.FC = () => {
|
|
|
213
236
|
// edge handles — mounts only for a sole selection.
|
|
214
237
|
const isSoleSelection = selectedIds.length === 1
|
|
215
238
|
|
|
216
|
-
// Show slab boundary editor
|
|
239
|
+
// Show slab boundary editor in select mode with a slab selected (but not editing a hole)
|
|
217
240
|
const showSlabBoundaryEditor =
|
|
218
|
-
phase === 'structure' &&
|
|
219
241
|
mode === 'select' &&
|
|
220
242
|
isSoleSelection &&
|
|
221
243
|
selectedSlabId !== undefined &&
|
|
244
|
+
// Derived plates (a footprint's floor, a room's raised plate) follow their
|
|
245
|
+
// rooms; only user-drawn slabs get the outline editor.
|
|
246
|
+
!selectedSlab?.plateRole &&
|
|
222
247
|
!isFloorplanDrivenReshape &&
|
|
223
248
|
!editingSlabHoleIsManual
|
|
224
249
|
|
|
@@ -230,26 +255,28 @@ export const ToolManager: React.FC = () => {
|
|
|
230
255
|
!isFloorplanDrivenReshape &&
|
|
231
256
|
editingSlabHoleIsManual
|
|
232
257
|
|
|
233
|
-
// Show ceiling boundary editor
|
|
258
|
+
// Show ceiling boundary editor only inside the ceiling's Edit ceiling session (not while editing a hole)
|
|
234
259
|
const showCeilingBoundaryEditor =
|
|
235
|
-
phase === 'structure' &&
|
|
236
260
|
mode === 'select' &&
|
|
237
261
|
isSoleSelection &&
|
|
238
262
|
selectedCeilingId !== undefined &&
|
|
263
|
+
selectedCeilingId === editCeilingId &&
|
|
239
264
|
!isFloorplanDrivenReshape &&
|
|
240
265
|
(!editingHole || editingHole.nodeId !== selectedCeilingId)
|
|
241
266
|
|
|
242
|
-
// Show ceiling hole editor when editing a hole
|
|
267
|
+
// Show ceiling hole editor when editing a manual hole inside the ceiling's Edit ceiling session
|
|
243
268
|
const showCeilingHoleEditor =
|
|
244
269
|
selectedCeilingId !== undefined &&
|
|
270
|
+
selectedCeilingId === editCeilingId &&
|
|
245
271
|
editingHole !== null &&
|
|
246
272
|
editingHole.nodeId === selectedCeilingId &&
|
|
273
|
+
((nodes[selectedCeilingId as AnyNodeId] as CeilingNode).holeMetadata?.[editingHole.holeIndex]
|
|
274
|
+
?.source ?? 'manual') === 'manual' &&
|
|
247
275
|
!isFloorplanDrivenReshape
|
|
248
276
|
|
|
249
|
-
// Show zone boundary editor
|
|
277
|
+
// Show zone boundary editor in select mode with a zone selected
|
|
250
278
|
// Hide when editing a slab or ceiling to avoid overlapping handles
|
|
251
279
|
const showZoneBoundaryEditor =
|
|
252
|
-
phase === 'structure' &&
|
|
253
280
|
mode === 'select' &&
|
|
254
281
|
selectedZoneId !== null &&
|
|
255
282
|
!isFloorplanDrivenReshape &&
|
|
@@ -274,7 +301,7 @@ export const ToolManager: React.FC = () => {
|
|
|
274
301
|
const RegistryToolComponent = showBuildTool ? getRegistryTool(tool) : null
|
|
275
302
|
const useRegistryTool = RegistryToolComponent != null
|
|
276
303
|
|
|
277
|
-
const BuildToolComponent = showBuildTool && !useRegistryTool ? tools[
|
|
304
|
+
const BuildToolComponent = showBuildTool && !useRegistryTool ? tools[tool] : null
|
|
278
305
|
const handlePlacedNodeSelected = (nodeId: AnyNodeId) => {
|
|
279
306
|
setSelection({ selectedIds: [nodeId] })
|
|
280
307
|
}
|
|
@@ -298,10 +325,16 @@ export const ToolManager: React.FC = () => {
|
|
|
298
325
|
<RegistryToolProvider value={registryToolContext}>
|
|
299
326
|
{/* World-space tools: site boundary and building movement operate in world coordinates */}
|
|
300
327
|
{showSiteBoundaryEditor && <SiteBoundaryEditor />}
|
|
301
|
-
{/* Terrain sculpting is a mode rather than a `tools[
|
|
328
|
+
{/* Terrain sculpting is a mode rather than a `tools[tool]` entry —
|
|
302
329
|
it places no node — so it gets its own gate here. World-space, because
|
|
303
330
|
the ground is not building-local. */}
|
|
304
331
|
{sculpting && <TerrainSculptTool />}
|
|
332
|
+
{/* "Paint part of the floor": level-space, follows the level matrix itself. */}
|
|
333
|
+
<FloorRegionControls3D />
|
|
334
|
+
{/* Floor and ceiling openings: level-space, follows the level matrix itself. */}
|
|
335
|
+
<FloorOpeningsOverlay3D />
|
|
336
|
+
{/* "Paint part of the wall": follows the wall mesh's own matrix. */}
|
|
337
|
+
{mode === 'material-paint' && paintsWallRegions && <WallPaintRegionTool />}
|
|
305
338
|
{showMover && movingNode?.type === 'building' && (
|
|
306
339
|
<MoveTool onNodeMoved={handlePlacedNodeSelected} onSpawnMoved={handlePlacedNodeSelected} />
|
|
307
340
|
)}
|
|
@@ -454,6 +487,8 @@ export const ToolManager: React.FC = () => {
|
|
|
454
487
|
<Elevation3DGuideLayer />
|
|
455
488
|
{/* "Magnetic" beacon at the active wall-draft snap point. */}
|
|
456
489
|
<WallSnapBeaconLayer />
|
|
490
|
+
{/* Wall ends that aren't joined, with a one-click join — twin of the floor plan's. */}
|
|
491
|
+
<OpenWallEnds3DLayer />
|
|
457
492
|
</group>
|
|
458
493
|
</RegistryToolProvider>
|
|
459
494
|
)
|