@pascal-app/editor 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +16 -9
- package/src/components/editor/action-menu-button.tsx +83 -0
- package/src/components/editor/bake-thumbnail.tsx +315 -4
- package/src/components/editor/capture-level-picker.tsx +72 -0
- package/src/components/editor/custom-camera-controls.tsx +221 -128
- 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 -25
- package/src/components/editor/editor-layout-v2.tsx +131 -58
- package/src/components/editor/export-manager.tsx +34 -8
- package/src/components/editor/first-person/build-collider-world.test.ts +51 -0
- package/src/components/editor/first-person/build-collider-world.ts +6 -2
- package/src/components/editor/first-person/mechanism-targets.test.ts +75 -0
- package/src/components/editor/first-person/mechanism-targets.ts +36 -0
- package/src/components/editor/first-person-controls.tsx +205 -2
- package/src/components/editor/floating-action-menu.tsx +63 -207
- package/src/components/editor/floor-aware-camera-controls.test.ts +442 -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-panel.tsx +536 -487
- package/src/components/editor/footprint-height-handle.tsx +204 -0
- package/src/components/editor/grid.tsx +31 -7
- package/src/components/editor/group-actions.ts +59 -11
- package/src/components/editor/group-floating-action-menu.tsx +19 -9
- package/src/components/editor/group-move-3d.ts +4 -6
- package/src/components/editor/group-rotate-handle.tsx +81 -35
- package/src/components/editor/group-selection-box-3d.tsx +25 -17
- package/src/components/editor/group-transform-shared.test.ts +126 -1
- package/src/components/editor/group-transform-shared.ts +79 -73
- package/src/components/editor/handles/handle-arrow.tsx +72 -2
- 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 +272 -131
- package/src/components/editor/index.tsx +316 -176
- package/src/components/editor/join-walls-pill.tsx +44 -0
- package/src/components/editor/node-action-menu.tsx +70 -70
- package/src/components/editor/node-arrow-handles.tsx +167 -14
- 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 +132 -0
- 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 +225 -0
- package/src/components/editor/room-labels.test.ts +357 -0
- package/src/components/editor/room-labels.tsx +990 -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 +451 -317
- package/src/components/editor/slab-hole-highlights.tsx +3 -0
- package/src/components/editor/snapshot-capture-overlay.tsx +4 -0
- package/src/components/editor/snapshot-capture.test.ts +52 -4
- package/src/components/editor/snapshot-capture.ts +30 -5
- package/src/components/editor/thumbnail-generator.test.ts +308 -0
- package/src/components/editor/thumbnail-generator.tsx +1035 -229
- package/src/components/editor/use-floorplan-background-placement.ts +31 -11
- 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 +670 -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.tsx +220 -0
- package/src/components/editor/wall-justification.test.ts +140 -0
- package/src/components/editor/wall-move-side-handles.tsx +114 -163
- package/src/components/editor/wall-opening-highlights.tsx +2 -3
- 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 +3 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
- package/src/components/editor-2d/floorplan-group-move.tsx +17 -24
- 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 +33 -12
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +242 -126
- 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-measurement.tsx +166 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +87 -8
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +355 -245
- package/src/components/editor-2d/svg-paths.ts +12 -15
- package/src/components/procedural-items/inspector.tsx +40 -15
- 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/zone/zone-system.test.ts +47 -0
- package/src/components/systems/zone/zone-system.tsx +31 -66
- package/src/components/tools/__fixtures__/hosting.ts +10 -0
- package/src/components/tools/item/item-surface-placement.test.ts +3 -9
- package/src/components/tools/item/item-surface-strategy-resolver.test.ts +3 -9
- package/src/components/tools/item/placement-history.test.tsx +474 -0
- package/src/components/tools/item/placement-strategies.ts +63 -47
- package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +3 -3
- package/src/components/tools/item/use-draft-node.test.tsx +432 -1
- package/src/components/tools/item/use-draft-node.ts +207 -97
- package/src/components/tools/item/use-placement-coordinator.tsx +251 -111
- 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 +10 -2
- package/src/components/tools/registry/item-surface-move.ts +46 -24
- package/src/components/tools/registry/item-surface-resolver.test.ts +2 -2
- package/src/components/tools/registry/move-registry-node-tool.tsx +26 -10
- 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/affordance-dispatch.test.ts +24 -0
- package/src/components/tools/shared/affordance-dispatch.ts +13 -0
- package/src/components/tools/shared/cursor-sphere.tsx +36 -12
- package/src/components/tools/shared/draft-measurement-label.tsx +38 -0
- package/src/components/tools/shared/pointer-support-cap.test.ts +27 -0
- package/src/components/tools/shared/pointer-support-cap.ts +17 -5
- package/src/components/tools/shared/surface-rejection.test.tsx +5 -4
- package/src/components/tools/shared/surface-rejection.tsx +14 -0
- package/src/components/tools/site/site-boundary-editor.tsx +1 -2
- package/src/components/tools/stair/stair-defaults.ts +0 -3
- package/src/components/tools/stair/stair-tool.tsx +90 -42
- package/src/components/tools/tool-manager.tsx +82 -23
- package/src/components/tools/wall/wall-drafting.test.ts +19 -4
- package/src/components/tools/zone/zone-tool.tsx +4 -12
- package/src/components/ui/action-menu/action-button.tsx +3 -0
- package/src/components/ui/action-menu/camera-actions.tsx +2 -1
- package/src/components/ui/action-menu/control-modes.tsx +2 -35
- package/src/components/ui/action-menu/measurement-control.tsx +3 -10
- package/src/components/ui/build-panel/build-panel.tsx +235 -0
- package/src/components/ui/command-palette/editor-commands.tsx +4 -30
- package/src/components/ui/command-palette/index.tsx +5 -24
- package/src/components/ui/controls/finish-swatch.tsx +110 -0
- package/src/components/ui/controls/material-paint-panel.tsx +101 -91
- package/src/components/ui/controls/material-picker.tsx +8 -67
- package/src/components/ui/controls/scene-material-list.tsx +4 -32
- package/src/components/ui/controls/select-control.tsx +65 -0
- package/src/components/ui/controls/slider-control.tsx +76 -31
- package/src/components/ui/floating-level-selector.tsx +9 -14
- package/src/components/ui/helpers/building-helper.tsx +4 -1
- package/src/components/ui/helpers/contextual-helper-panel.tsx +154 -70
- package/src/components/ui/helpers/helper-manager.test.tsx +69 -0
- package/src/components/ui/helpers/helper-manager.tsx +348 -19
- package/src/components/ui/helpers/item-helper.tsx +8 -0
- package/src/components/ui/helpers/registered-tool-helper.tsx +10 -33
- package/src/components/ui/icon-ref.tsx +45 -1
- package/src/components/ui/item-catalog/item-catalog.tsx +5 -30
- 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 +20 -14
- 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 +2 -2
- package/src/components/ui/panels/mobile-selection-bar.tsx +2 -2
- 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-height-mode.tsx +27 -21
- package/src/components/ui/panels/multi-parametric-inspector.tsx +52 -2
- package/src/components/ui/panels/multi-selection-panel.tsx +43 -4
- 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 +78 -36
- package/src/components/ui/panels/parametric-field-control.tsx +1 -1
- package/src/components/ui/panels/parametric-inspector.tsx +7 -19
- 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 +725 -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/color-dot.tsx +36 -24
- package/src/components/ui/primitives/shortcut-token.tsx +4 -0
- package/src/components/ui/primitives/sidebar.tsx +2 -0
- package/src/components/ui/right-stack.test.ts +29 -0
- package/src/components/ui/right-stack.tsx +145 -0
- package/src/components/ui/sidebar/icon-rail.tsx +5 -0
- package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +2 -2
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +615 -372
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +7 -7
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +1 -1
- 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 +176 -211
- 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 +0 -27
- 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 +0 -2
- package/src/components/ui/sidebar/tab-bar.tsx +10 -0
- 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-visibility.test.ts +35 -0
- package/src/components/viewer/floorplan-preview-visibility.ts +19 -0
- package/src/components/viewer/floorplan-preview.tsx +6 -18
- package/src/components/viewer/viewer-hover-label.tsx +34 -0
- package/src/components/viewer/viewer-rooms.tsx +175 -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 +9 -9
- package/src/hooks/use-auto-save.integration.test.tsx +311 -0
- package/src/hooks/use-auto-save.ts +55 -14
- package/src/hooks/use-ceiling-events.test.ts +1 -0
- package/src/hooks/use-ceiling-events.ts +2 -0
- package/src/hooks/use-grid-events.test.ts +2 -0
- package/src/hooks/use-grid-events.ts +16 -11
- package/src/hooks/use-keyboard.test.ts +182 -0
- package/src/hooks/use-keyboard.ts +122 -91
- 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 +48 -0
- package/src/hooks/use-placement-notice.ts +42 -0
- package/src/hooks/use-selected-room.ts +143 -0
- package/src/index.tsx +306 -39
- package/src/lib/catalog-panel-model.ts +39 -0
- package/src/lib/catalog-selection.test.ts +27 -0
- package/src/lib/catalog-selection.ts +8 -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/contextual-help.test.ts +27 -1
- package/src/lib/contextual-help.ts +16 -9
- package/src/lib/continuation.test.ts +19 -1
- package/src/lib/continuation.ts +25 -4
- package/src/lib/door-interaction.ts +14 -0
- package/src/lib/editor-level-display.test.ts +96 -0
- package/src/lib/editor-level-display.ts +48 -0
- package/src/lib/editor-views.ts +144 -0
- package/src/lib/export-geometry-wait.test.ts +90 -0
- 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 +283 -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 +15 -9
- package/src/lib/floorplan/floorplan-export.tsx +32 -8
- package/src/lib/floorplan/floorplan-extension.ts +48 -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 +339 -18
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +138 -8
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +56 -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/schedules.test.ts +189 -0
- package/src/lib/floorplan/schedules.ts +339 -0
- package/src/lib/floorplan/sheet-export.test.ts +100 -0
- package/src/lib/floorplan/sheet-export.ts +318 -0
- package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +779 -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 +232 -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 +156 -0
- package/src/lib/floorplan/site-plan/geometry.test.ts +275 -0
- package/src/lib/floorplan/site-plan/geometry.ts +557 -0
- package/src/lib/floorplan/site-plan/index.ts +71 -0
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +366 -0
- package/src/lib/floorplan/site-plan/site-annotations.ts +1083 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +379 -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 +69 -0
- package/src/lib/floorplan/types.ts +1 -27
- package/src/lib/fresh-planar-placement.test.ts +36 -0
- package/src/lib/fresh-planar-placement.ts +191 -36
- package/src/lib/geometry-script/author.ts +152 -0
- package/src/lib/geometry-script/client.ts +152 -0
- package/src/lib/geometry-script/protocol.ts +12 -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 +544 -16
- package/src/lib/glb-export.ts +256 -125
- package/src/lib/grid-frame.test.ts +19 -0
- package/src/lib/grid-frame.ts +18 -0
- package/src/lib/history.test.ts +6 -0
- 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 +27 -1
- package/src/lib/keyboard-pan.test.ts +184 -0
- package/src/lib/keyboard-pan.ts +238 -0
- 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 +186 -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 +407 -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 +74 -0
- package/src/lib/material-paint-panel-model.ts +80 -0
- package/src/lib/material-paint.ts +4 -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 +165 -0
- package/src/lib/orbit-floor-clearance.ts +106 -0
- package/src/lib/overlay-transform.test.ts +28 -0
- package/src/lib/overlay-transform.ts +12 -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 +111 -0
- package/src/lib/paint-pass-through.ts +76 -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 +266 -25
- package/src/lib/panel-rows.ts +96 -0
- package/src/lib/panel-tool-options.test.ts +34 -0
- package/src/lib/panel-tool-options.ts +46 -0
- package/src/lib/parametric-node-update.ts +37 -0
- 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 +50 -0
- package/src/lib/plugin-panels.test.ts +25 -0
- package/src/lib/plugin-panels.ts +94 -0
- 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 +266 -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 +973 -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 +364 -0
- package/src/lib/scene-clipboard.ts +229 -48
- 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 +87 -0
- package/src/lib/scene.ts +39 -45
- package/src/lib/selection-routing.test.ts +129 -45
- package/src/lib/selection-routing.ts +53 -74
- package/src/lib/session-writes.test.ts +120 -0
- package/src/lib/session-writes.ts +205 -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 +17 -2
- package/src/lib/snapping-mode.ts +19 -7
- package/src/lib/spatial-drag-plane.test.ts +53 -0
- package/src/lib/spatial-drag-plane.ts +19 -0
- package/src/lib/spatial-pointer-input.test.ts +72 -0
- package/src/lib/spatial-pointer-input.ts +62 -0
- package/src/lib/spatial-snap.test.ts +47 -0
- package/src/lib/terrace-draft.test.ts +145 -0
- package/src/lib/terrace-draft.ts +213 -0
- package/src/lib/terrain-panel-model.ts +117 -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 +569 -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-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-interaction.ts +14 -0
- package/src/lib/world-grid-snap.test.ts +48 -0
- package/src/lib/world-grid-snap.ts +9 -5
- 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/rotation-snapping.test.ts +53 -0
- package/src/store/terrain-sculpt-mode.test.ts +19 -21
- package/src/store/tool-mode.test.ts +137 -6
- 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 +442 -323
- package/src/store/use-floorplan-draft-preview.ts +23 -1
- package/src/store/use-floorplan-marquee.ts +12 -2
- package/src/store/use-interaction-scope.ts +155 -6
- package/src/store/use-quick-measurement-hud.ts +3 -3
- package/src/store/use-stair-build-preview.ts +13 -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 +175 -0
- package/src/store/view-layout.ts +196 -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/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/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
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
MeshBasicMaterial,
|
|
20
20
|
OrthographicCamera,
|
|
21
21
|
PerspectiveCamera,
|
|
22
|
+
Ray,
|
|
22
23
|
Vector3,
|
|
23
24
|
} from 'three'
|
|
24
25
|
import { z } from 'zod'
|
|
@@ -161,6 +162,32 @@ describe('resolvePointerSupportSurface node tops', () => {
|
|
|
161
162
|
expect(support?.worldPoint).toEqual([0, 2, 0])
|
|
162
163
|
})
|
|
163
164
|
|
|
165
|
+
test('XR floor drawing uses the controller ray even when the camera sees a raised surface', () => {
|
|
166
|
+
addPluginPlatform(2, [4, 2, 2])
|
|
167
|
+
const camera = new PerspectiveCamera()
|
|
168
|
+
camera.position.set(0, 5, 5)
|
|
169
|
+
camera.updateMatrixWorld(true)
|
|
170
|
+
const controllerRay = new Ray(new Vector3(0, 1.5, 0), new Vector3(0, -1, 0))
|
|
171
|
+
const desktop = resolvePointerSupportSurface(camera, [0, 0, 0], {
|
|
172
|
+
includeNodeTopSurfaces: true,
|
|
173
|
+
})
|
|
174
|
+
expect(desktop?.sourceNodeId).toBe(PLATFORM_ID)
|
|
175
|
+
const xr = resolvePointerSupportSurface(camera, [0, 0, 0], {
|
|
176
|
+
includeNodeTopSurfaces: true,
|
|
177
|
+
pointerRay: controllerRay,
|
|
178
|
+
})
|
|
179
|
+
expect(xr?.sourceNodeId).toBeNull()
|
|
180
|
+
expect(xr?.worldPoint).toEqual([0, 0, 0])
|
|
181
|
+
camera.position.set(12, 8, -10)
|
|
182
|
+
camera.updateMatrixWorld(true)
|
|
183
|
+
expect(
|
|
184
|
+
resolvePointerSupportSurface(camera, [0, 0, 0], {
|
|
185
|
+
includeNodeTopSurfaces: true,
|
|
186
|
+
pointerRay: controllerRay,
|
|
187
|
+
})?.worldPoint,
|
|
188
|
+
).toEqual([0, 0, 0])
|
|
189
|
+
})
|
|
190
|
+
|
|
164
191
|
test('keeps an unhovered item top above a batched slab across batch transitions', () => {
|
|
165
192
|
const restoreRegistry = nodeRegistry._snapshot()
|
|
166
193
|
const item = ItemNode.parse({
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useScene,
|
|
12
12
|
} from '@pascal-app/core'
|
|
13
13
|
import { setSurfaceRaycastLayers, useViewer } from '@pascal-app/viewer'
|
|
14
|
-
import { type Camera, type Object3D, Raycaster, Vector3 } from 'three'
|
|
14
|
+
import { type Camera, type Object3D, type Ray, Raycaster, Vector3 } from 'three'
|
|
15
15
|
import { resolveTerrainGroundHit } from '../../../lib/ground-surface'
|
|
16
16
|
import { scopeNodeId } from '../../../lib/interaction/scope'
|
|
17
17
|
import useInteractionScope from '../../../store/use-interaction-scope'
|
|
@@ -79,16 +79,22 @@ export type PointerSupportSurface = {
|
|
|
79
79
|
export function resolvePointerSupportSurface(
|
|
80
80
|
camera: Camera,
|
|
81
81
|
worldHit: readonly [number, number, number],
|
|
82
|
-
options?: { includeNodeTopSurfaces?: boolean },
|
|
82
|
+
options?: { includeNodeTopSurfaces?: boolean; pointerRay?: Ray },
|
|
83
83
|
): PointerSupportSurface | null {
|
|
84
84
|
const levelId = useViewer.getState().selection.levelId
|
|
85
85
|
if (!levelId) return null
|
|
86
86
|
|
|
87
87
|
// The world ray, kept before the level conversion below: the terrain field is
|
|
88
88
|
// world-space (site geometry, not level-local), so the march needs this frame.
|
|
89
|
-
|
|
89
|
+
if (options?.pointerRay) worldRayOrigin.copy(options.pointerRay.origin)
|
|
90
|
+
else camera.getWorldPosition(worldRayOrigin)
|
|
91
|
+
const isOrthographic =
|
|
92
|
+
!options?.pointerRay &&
|
|
93
|
+
(camera as Camera & { isOrthographicCamera?: boolean }).isOrthographicCamera === true
|
|
90
94
|
const cameraToHit = hitScratch.set(worldHit[0], worldHit[1], worldHit[2]).sub(worldRayOrigin)
|
|
91
|
-
if (
|
|
95
|
+
if (options?.pointerRay) {
|
|
96
|
+
worldRayDirection.copy(options.pointerRay.direction).normalize()
|
|
97
|
+
} else if (isOrthographic) {
|
|
92
98
|
// For an orthographic camera every screen pixel has the same direction. The
|
|
93
99
|
// hit point is offset from the camera along the view plane, so using
|
|
94
100
|
// `camera.position -> hit` tilts the ray toward the screen centre and makes
|
|
@@ -102,7 +108,13 @@ export function resolvePointerSupportSurface(
|
|
|
102
108
|
}
|
|
103
109
|
|
|
104
110
|
originScratch.copy(worldRayOrigin)
|
|
105
|
-
|
|
111
|
+
// Second world point defining the ray fed to the surface solve. In the plain
|
|
112
|
+
// perspective case `worldHit` already lies on the true pointer ray, so using
|
|
113
|
+
// it keeps `t === 1` (and the returned plan point exact) whenever the pointed
|
|
114
|
+
// surface IS the event plane — the pointer-ray and orthographic branches have
|
|
115
|
+
// no such hit, so they step one unit along the resolved direction.
|
|
116
|
+
if (options?.pointerRay || isOrthographic) hitScratch.copy(worldRayOrigin).add(worldRayDirection)
|
|
117
|
+
else hitScratch.set(worldHit[0], worldHit[1], worldHit[2])
|
|
106
118
|
// Slab polygons/elevations live in the level frame; the level mesh
|
|
107
119
|
// carries the storey Y offset and any building rotation.
|
|
108
120
|
const levelMesh = sceneRegistry.nodes.get(levelId as AnyNodeId)
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
type AnyNode,
|
|
4
4
|
ItemNode,
|
|
5
5
|
nodeRegistry,
|
|
6
|
-
registerNode,
|
|
7
6
|
resolveSurfacePlacement,
|
|
8
7
|
type SceneApi,
|
|
9
8
|
type SurfaceProvider,
|
|
10
9
|
type SurfaceRejectReason,
|
|
11
10
|
} from '@pascal-app/core'
|
|
11
|
+
import { registerHostingTestNode } from '../__fixtures__/hosting'
|
|
12
12
|
import { createSurfaceRejectionFeedback } from './surface-rejection'
|
|
13
13
|
|
|
14
14
|
let restore: () => void
|
|
@@ -27,7 +27,7 @@ for (const reason of [
|
|
|
27
27
|
'no-surface',
|
|
28
28
|
'invalid-hit',
|
|
29
29
|
] as const) {
|
|
30
|
-
test(`${reason}
|
|
30
|
+
test(`${reason}: eligible-host diagnostics remain available; ineligible hosts defer silently`, () => {
|
|
31
31
|
const provider: SurfaceProvider = {
|
|
32
32
|
childFrame: 'host-local',
|
|
33
33
|
resolveHit: () =>
|
|
@@ -56,7 +56,7 @@ for (const reason of [
|
|
|
56
56
|
},
|
|
57
57
|
accepts: () => reason !== 'child-not-accepted',
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
registerHostingTestNode({
|
|
60
60
|
kind: 'test-host',
|
|
61
61
|
schemaVersion: 1,
|
|
62
62
|
schema: ItemNode,
|
|
@@ -75,6 +75,7 @@ for (const reason of [
|
|
|
75
75
|
type: reason === 'host-not-eligible' ? 'guide' : 'test-host',
|
|
76
76
|
} as unknown as AnyNode,
|
|
77
77
|
childKind: 'item',
|
|
78
|
+
childId: 'item_existing',
|
|
78
79
|
childFootprint: {
|
|
79
80
|
size: reason === 'footprint-outside-surface' ? [3, 1, 3] : [0.3, 0.3, 0.3],
|
|
80
81
|
rotationY: 0,
|
|
@@ -87,7 +88,7 @@ for (const reason of [
|
|
|
87
88
|
onReject: (r) => feedback.reject(r),
|
|
88
89
|
}),
|
|
89
90
|
).toBeNull()
|
|
90
|
-
expect(feedback.reason).toBe(reason)
|
|
91
|
+
expect(feedback.reason).toBe(reason === 'host-not-eligible' ? null : reason)
|
|
91
92
|
})
|
|
92
93
|
}
|
|
93
94
|
|
|
@@ -27,3 +27,17 @@ export function createSurfaceRejectionFeedback(
|
|
|
27
27
|
},
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
// R3F reuses one native event across enter/move dispatch and bubbled ancestor hits.
|
|
32
|
+
export function createSurfaceEventOwnership() {
|
|
33
|
+
const owners = new WeakMap<object, string>()
|
|
34
|
+
return {
|
|
35
|
+
allows(hostId: string, event: object) {
|
|
36
|
+
const owner = owners.get(event)
|
|
37
|
+
return owner === undefined || owner === hostId
|
|
38
|
+
},
|
|
39
|
+
claim(hostId: string, event: object) {
|
|
40
|
+
if (!owners.has(event)) owners.set(event, hostId)
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -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
|
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
export const DEFAULT_STAIR_TYPE = 'straight' as const
|
|
2
2
|
export const DEFAULT_STAIR_WIDTH = 1.0
|
|
3
|
-
export const DEFAULT_STAIR_LENGTH = 3.0
|
|
4
|
-
export const DEFAULT_STAIR_HEIGHT = 2.5
|
|
5
|
-
export const DEFAULT_STAIR_STEP_COUNT = 10
|
|
6
3
|
export const DEFAULT_STAIR_ATTACHMENT_SIDE = 'front' as const
|
|
7
4
|
export const DEFAULT_STAIR_FILL_TO_FLOOR = true
|
|
8
5
|
export const DEFAULT_STAIR_THICKNESS = 0.25
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AnyNode,
|
|
3
3
|
collectAlignmentAnchors,
|
|
4
|
-
|
|
4
|
+
createSizedStairFlight,
|
|
5
5
|
createSurfaceOpeningPreviewController,
|
|
6
6
|
DEFAULT_LEVEL_HEIGHT,
|
|
7
7
|
emitter,
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
type LevelNode,
|
|
12
12
|
movingAlignmentAnchors,
|
|
13
13
|
type NodeEvent,
|
|
14
|
+
planStairCreation,
|
|
15
|
+
planStairSizing,
|
|
14
16
|
resolveAlignment,
|
|
15
17
|
resolveFrozenFloorPlacementPatch,
|
|
16
18
|
resolveSupportSlabPatch,
|
|
@@ -23,6 +25,7 @@ import { useViewer } from '@pascal-app/viewer'
|
|
|
23
25
|
import { useThree } from '@react-three/fiber'
|
|
24
26
|
import { useEffect, useMemo, useRef } from 'react'
|
|
25
27
|
import * as THREE from 'three'
|
|
28
|
+
import { EDITOR_LAYER } from '../../../lib/constants'
|
|
26
29
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
|
27
30
|
import {
|
|
28
31
|
resolveStairDestinationLevel,
|
|
@@ -54,11 +57,9 @@ import {
|
|
|
54
57
|
DEFAULT_SPIRAL_TOP_LANDING_MODE,
|
|
55
58
|
DEFAULT_STAIR_ATTACHMENT_SIDE,
|
|
56
59
|
DEFAULT_STAIR_FILL_TO_FLOOR,
|
|
57
|
-
DEFAULT_STAIR_LENGTH,
|
|
58
60
|
DEFAULT_STAIR_OPENING_OFFSET,
|
|
59
61
|
DEFAULT_STAIR_RAILING_HEIGHT,
|
|
60
62
|
DEFAULT_STAIR_RAILING_MODE,
|
|
61
|
-
DEFAULT_STAIR_STEP_COUNT,
|
|
62
63
|
DEFAULT_STAIR_THICKNESS,
|
|
63
64
|
DEFAULT_STAIR_TYPE,
|
|
64
65
|
DEFAULT_STAIR_WIDTH,
|
|
@@ -75,19 +76,18 @@ type MoveTriggerEvent = GridEvent | NodeEvent<AnyNode>
|
|
|
75
76
|
* Same algorithm as StairSystem's generateStairSegmentGeometry.
|
|
76
77
|
*/
|
|
77
78
|
function createStairPreviewGeometry(rise: number): THREE.BufferGeometry {
|
|
78
|
-
const riserHeight = rise
|
|
79
|
-
const treadDepth = DEFAULT_STAIR_LENGTH / DEFAULT_STAIR_STEP_COUNT
|
|
79
|
+
const { stepCount, length, riserHeight, going: treadDepth } = planStairSizing(rise)
|
|
80
80
|
|
|
81
81
|
const shape = new THREE.Shape()
|
|
82
82
|
shape.moveTo(0, 0)
|
|
83
83
|
|
|
84
|
-
for (let i = 0; i <
|
|
84
|
+
for (let i = 0; i < stepCount; i++) {
|
|
85
85
|
shape.lineTo(i * treadDepth, (i + 1) * riserHeight)
|
|
86
86
|
shape.lineTo((i + 1) * treadDepth, (i + 1) * riserHeight)
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// Fill to floor (absoluteHeight = 0)
|
|
90
|
-
shape.lineTo(
|
|
90
|
+
shape.lineTo(length, 0)
|
|
91
91
|
shape.lineTo(0, 0)
|
|
92
92
|
|
|
93
93
|
const geometry = new THREE.ExtrudeGeometry(shape, {
|
|
@@ -110,33 +110,28 @@ function createStairPreviewGeometry(rise: number): THREE.BufferGeometry {
|
|
|
110
110
|
* dropped on, not a constant: the placed stair has no explicit `totalRise`, so
|
|
111
111
|
* this is the height `syncStairRises` immediately converges it to anyway.
|
|
112
112
|
*/
|
|
113
|
-
function
|
|
113
|
+
function resolvePlacedStairBase(
|
|
114
114
|
nodes: Record<string, AnyNode>,
|
|
115
115
|
levelId: LevelNode['id'],
|
|
116
116
|
stair: StairNode,
|
|
117
117
|
supportSurface: PointerSupportSurface | null,
|
|
118
|
+
seed: StairSegmentNode,
|
|
118
119
|
): number {
|
|
119
|
-
//
|
|
120
|
-
// scene yet: the storey height minus whatever slab lifts the drop point,
|
|
121
|
-
// capped by the surface the pointer actually aims at (a floor under an
|
|
122
|
-
// overlapping deck must not elect the deck).
|
|
120
|
+
// Keep the pointer-selected base when an overlapping deck is above the cursor.
|
|
123
121
|
const base = getFloorStackedPosition({
|
|
124
122
|
node: stair,
|
|
125
|
-
nodes,
|
|
123
|
+
nodes: { ...nodes, [seed.id]: { ...seed, parentId: stair.id } },
|
|
126
124
|
position: stair.position,
|
|
127
125
|
rotation: stair.rotation,
|
|
128
126
|
levelId,
|
|
129
127
|
maxElevation: supportSurface?.elevation ?? null,
|
|
130
128
|
})[1]
|
|
131
|
-
return
|
|
129
|
+
return base
|
|
132
130
|
}
|
|
133
131
|
|
|
134
132
|
function createSeedStairSegment(rise: number) {
|
|
135
|
-
return
|
|
133
|
+
return createSizedStairFlight(rise, {
|
|
136
134
|
width: DEFAULT_STAIR_WIDTH,
|
|
137
|
-
length: DEFAULT_STAIR_LENGTH,
|
|
138
|
-
height: rise,
|
|
139
|
-
stepCount: DEFAULT_STAIR_STEP_COUNT,
|
|
140
135
|
attachmentSide: DEFAULT_STAIR_ATTACHMENT_SIDE,
|
|
141
136
|
fillToFloor: DEFAULT_STAIR_FILL_TO_FLOOR,
|
|
142
137
|
thickness: DEFAULT_STAIR_THICKNESS,
|
|
@@ -160,15 +155,16 @@ function createDefaultStairNode({
|
|
|
160
155
|
}) {
|
|
161
156
|
return StairNode.parse({
|
|
162
157
|
name,
|
|
158
|
+
parentId: levelId,
|
|
163
159
|
position,
|
|
164
160
|
rotation,
|
|
165
161
|
stairType: DEFAULT_STAIR_TYPE,
|
|
162
|
+
uniformRisers: true,
|
|
166
163
|
fromLevelId: levelId,
|
|
167
164
|
toLevelId: nextLevelId,
|
|
168
165
|
slabOpeningMode: 'destination',
|
|
169
166
|
openingOffset: DEFAULT_STAIR_OPENING_OFFSET,
|
|
170
167
|
width: DEFAULT_STAIR_WIDTH,
|
|
171
|
-
stepCount: DEFAULT_STAIR_STEP_COUNT,
|
|
172
168
|
thickness: DEFAULT_STAIR_THICKNESS,
|
|
173
169
|
fillToFloor: DEFAULT_STAIR_FILL_TO_FLOOR,
|
|
174
170
|
innerRadius: DEFAULT_CURVED_STAIR_INNER_RADIUS,
|
|
@@ -202,7 +198,9 @@ function commitStairPlacement(
|
|
|
202
198
|
|
|
203
199
|
const stairCount = Object.values(nodes).filter((n) => n.type === 'stair').length
|
|
204
200
|
const name = `Staircase ${stairCount + 1}`
|
|
205
|
-
const
|
|
201
|
+
const storeyRise = getLevelFloorToFloorHeight(placementLevelId, nodes)
|
|
202
|
+
if (!(storeyRise > 0)) return
|
|
203
|
+
const seed = createSeedStairSegment(storeyRise)
|
|
206
204
|
|
|
207
205
|
const destinationPlan = resolveStairDestinationLevel({
|
|
208
206
|
createMissing: true,
|
|
@@ -222,10 +220,28 @@ function commitStairPlacement(
|
|
|
222
220
|
}),
|
|
223
221
|
parentId: placementLevelId,
|
|
224
222
|
})
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
const base = resolvePlacedStairBase(nodes, placementLevelId, stair, supportSurface, seed)
|
|
224
|
+
let segment: StairSegmentNode
|
|
225
|
+
try {
|
|
226
|
+
segment = {
|
|
227
|
+
...planStairCreation(
|
|
228
|
+
stair,
|
|
229
|
+
nodes,
|
|
230
|
+
{
|
|
231
|
+
width: seed.width,
|
|
232
|
+
attachmentSide: seed.attachmentSide,
|
|
233
|
+
fillToFloor: seed.fillToFloor,
|
|
234
|
+
thickness: seed.thickness,
|
|
235
|
+
},
|
|
236
|
+
base,
|
|
237
|
+
).flight,
|
|
238
|
+
id: seed.id,
|
|
239
|
+
}
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (error instanceof RangeError) return
|
|
242
|
+
throw error
|
|
228
243
|
}
|
|
244
|
+
stair.stepCount = segment.stepCount
|
|
229
245
|
const prospectiveNodes = {
|
|
230
246
|
...nodes,
|
|
231
247
|
[stair.id]: stair,
|
|
@@ -270,6 +286,7 @@ export const StairTool: React.FC = () => {
|
|
|
270
286
|
cameraRef.current = camera
|
|
271
287
|
const cursorRef = useRef<THREE.Group>(null)
|
|
272
288
|
const previewRef = useRef<THREE.Group>(null)
|
|
289
|
+
const previewMeshRef = useRef<THREE.Mesh>(null)
|
|
273
290
|
const rotationRef = useRef(0)
|
|
274
291
|
const supportSurfaceRef = useRef<PointerSupportSurface | null>(null)
|
|
275
292
|
const previousGridPosRef = useRef<[number, number] | null>(null)
|
|
@@ -281,8 +298,10 @@ export const StairTool: React.FC = () => {
|
|
|
281
298
|
)
|
|
282
299
|
const previewRiseRef = useRef(previewRise)
|
|
283
300
|
previewRiseRef.current = previewRise
|
|
284
|
-
const previewGeometry = useMemo(() => createStairPreviewGeometry(
|
|
285
|
-
|
|
301
|
+
const previewGeometry = useMemo(() => createStairPreviewGeometry(DEFAULT_LEVEL_HEIGHT), [])
|
|
302
|
+
const ownedGeometryRef = useRef(previewGeometry)
|
|
303
|
+
const geometryRiseRef = useRef(DEFAULT_LEVEL_HEIGHT)
|
|
304
|
+
useEffect(() => () => ownedGeometryRef.current.dispose(), [])
|
|
286
305
|
|
|
287
306
|
useEffect(() => {
|
|
288
307
|
if (!currentLevelId) return
|
|
@@ -321,7 +340,9 @@ export const StairTool: React.FC = () => {
|
|
|
321
340
|
nodes,
|
|
322
341
|
})
|
|
323
342
|
const nextLevelId = destinationPlan?.toLevel.id ?? placementLevelId
|
|
324
|
-
const
|
|
343
|
+
const storeyRise = getLevelFloorToFloorHeight(placementLevelId, nodes)
|
|
344
|
+
if (!(storeyRise > 0)) return null
|
|
345
|
+
const seed = createSeedStairSegment(storeyRise)
|
|
325
346
|
const stair = createDefaultStairNode({
|
|
326
347
|
name: 'Staircase Preview',
|
|
327
348
|
levelId: placementLevelId,
|
|
@@ -330,10 +351,28 @@ export const StairTool: React.FC = () => {
|
|
|
330
351
|
rotation,
|
|
331
352
|
segmentId: seed.id,
|
|
332
353
|
})
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
354
|
+
const base = resolvePlacedStairBase(nodes, placementLevelId, stair, supportSurface, seed)
|
|
355
|
+
let segment: StairSegmentNode
|
|
356
|
+
try {
|
|
357
|
+
segment = {
|
|
358
|
+
...planStairCreation(
|
|
359
|
+
stair,
|
|
360
|
+
nodes,
|
|
361
|
+
{
|
|
362
|
+
width: seed.width,
|
|
363
|
+
attachmentSide: seed.attachmentSide,
|
|
364
|
+
fillToFloor: seed.fillToFloor,
|
|
365
|
+
thickness: seed.thickness,
|
|
366
|
+
},
|
|
367
|
+
base,
|
|
368
|
+
).flight,
|
|
369
|
+
id: seed.id,
|
|
370
|
+
}
|
|
371
|
+
} catch (error) {
|
|
372
|
+
if (error instanceof RangeError) return null
|
|
373
|
+
throw error
|
|
336
374
|
}
|
|
375
|
+
stair.stepCount = segment.stepCount
|
|
337
376
|
const previewNodes = {
|
|
338
377
|
...nodes,
|
|
339
378
|
...(destinationPlan?.createdLevel
|
|
@@ -360,11 +399,13 @@ export const StairTool: React.FC = () => {
|
|
|
360
399
|
rotation: number,
|
|
361
400
|
supportSurface: PointerSupportSurface | null,
|
|
362
401
|
) => {
|
|
363
|
-
const key = `${position[0].toFixed(3)},${position[2].toFixed(3)},${rotation.toFixed(4)},${supportSurface?.elevation.toFixed(3) ?? 'none'},${supportSurface?.sourceNodeId ?? 'floor'}`
|
|
402
|
+
const key = `${position[0].toFixed(3)},${position[2].toFixed(3)},${rotation.toFixed(4)},${supportSurface?.elevation.toFixed(3) ?? 'none'},${supportSurface?.sourceNodeId ?? 'floor'},${previewRiseRef.current}`
|
|
364
403
|
if (key === lastPreviewKey) return
|
|
365
404
|
lastPreviewKey = key
|
|
366
|
-
useStairBuildPreview.getState().setPreview([position[0], position[2]], rotation)
|
|
367
405
|
const preview = buildPreviewScene(position, rotation, supportSurface)
|
|
406
|
+
useStairBuildPreview
|
|
407
|
+
.getState()
|
|
408
|
+
.setPreview(preview ? [position[0], position[2]] : null, rotation, preview?.rise ?? null)
|
|
368
409
|
const frozenPatch =
|
|
369
410
|
preview && supportSurface?.sourceNodeId
|
|
370
411
|
? resolveFrozenFloorPlacementPatch(preview.stair, preview.previewNodes, {
|
|
@@ -398,11 +439,17 @@ export const StairTool: React.FC = () => {
|
|
|
398
439
|
}
|
|
399
440
|
|
|
400
441
|
if (previewRef.current) {
|
|
442
|
+
previewRef.current.visible = Boolean(preview)
|
|
401
443
|
previewRef.current.position.set(...visualPosition)
|
|
402
444
|
previewRef.current.rotation.y = rotation
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
445
|
+
if (preview && previewMeshRef.current && preview.rise !== geometryRiseRef.current) {
|
|
446
|
+
const previous = previewMeshRef.current.geometry
|
|
447
|
+
previewMeshRef.current.geometry = createStairPreviewGeometry(preview.rise)
|
|
448
|
+
previous.dispose()
|
|
449
|
+
ownedGeometryRef.current = previewMeshRef.current.geometry
|
|
450
|
+
geometryRiseRef.current = preview.rise
|
|
451
|
+
}
|
|
452
|
+
previewRef.current.scale.y = 1
|
|
406
453
|
}
|
|
407
454
|
|
|
408
455
|
// Forward-facing triangle (editor-side overlay). The run ascends along
|
|
@@ -410,19 +457,20 @@ export const StairTool: React.FC = () => {
|
|
|
410
457
|
// so `reversed` points the triangle out of the entry (where you approach
|
|
411
458
|
// from), sitting just before it — not inside the footprint or at the
|
|
412
459
|
// elevated far end. Centre is the footprint mid-run (origin is the entry).
|
|
460
|
+
if (!preview) {
|
|
461
|
+
useFacingPose.getState().clear()
|
|
462
|
+
openingPreview.clear()
|
|
463
|
+
return
|
|
464
|
+
}
|
|
465
|
+
const sizing = planStairSizing(preview.rise)
|
|
413
466
|
useFacingPose.getState().set({
|
|
414
467
|
position: visualPosition,
|
|
415
468
|
rotationY: rotation,
|
|
416
|
-
depth:
|
|
417
|
-
center: [0,
|
|
469
|
+
depth: sizing.length,
|
|
470
|
+
center: [0, sizing.length / 2],
|
|
418
471
|
reversed: true,
|
|
419
472
|
})
|
|
420
473
|
|
|
421
|
-
if (!preview) {
|
|
422
|
-
openingPreview.clear()
|
|
423
|
-
return
|
|
424
|
-
}
|
|
425
|
-
|
|
426
474
|
openingPreview.apply(syncAutoStairOpenings(preview.previewNodes))
|
|
427
475
|
}
|
|
428
476
|
|
|
@@ -625,7 +673,7 @@ export const StairTool: React.FC = () => {
|
|
|
625
673
|
forward-facing triangle is drawn by the editor-side overlay from the
|
|
626
674
|
pose published in `applyDraftPreview`. */}
|
|
627
675
|
<group ref={previewRef}>
|
|
628
|
-
<mesh
|
|
676
|
+
<mesh dispose={null} geometry={previewGeometry} layers={EDITOR_LAYER} ref={previewMeshRef}>
|
|
629
677
|
<meshStandardMaterial color="#818cf8" depthWrite={false} opacity={0.35} transparent />
|
|
630
678
|
</mesh>
|
|
631
679
|
</group>
|