@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
|
@@ -3,14 +3,24 @@ import {
|
|
|
3
3
|
type AnyNode,
|
|
4
4
|
type AnyNodeDefinition,
|
|
5
5
|
type AnyNodeId,
|
|
6
|
+
BuildingNode,
|
|
7
|
+
beginSceneHistoryPauseSession,
|
|
6
8
|
CabinetModuleNode,
|
|
7
9
|
CabinetNode,
|
|
8
10
|
findLevelAncestorId,
|
|
11
|
+
getSceneHistoryPauseDepth,
|
|
12
|
+
type LevelNode,
|
|
9
13
|
nodeRegistry,
|
|
14
|
+
pauseSceneHistory,
|
|
15
|
+
RoofNode,
|
|
16
|
+
RoofSegmentNode,
|
|
10
17
|
registerNode,
|
|
18
|
+
resumeSceneHistory,
|
|
11
19
|
type SceneCommit,
|
|
12
20
|
subscribeSceneCommits,
|
|
13
21
|
useScene,
|
|
22
|
+
WallNode,
|
|
23
|
+
WindowNode,
|
|
14
24
|
} from '@pascal-app/core'
|
|
15
25
|
import { z } from 'zod'
|
|
16
26
|
import {
|
|
@@ -187,6 +197,8 @@ describe('commitFreshPlacementSubtree', () => {
|
|
|
187
197
|
} as Partial<AnyNode>)
|
|
188
198
|
unsubscribe()
|
|
189
199
|
|
|
200
|
+
// A legacy caller's raw pause is kept after the one recorded step.
|
|
201
|
+
expect(useScene.temporal.getState().isTracking).toBe(false)
|
|
190
202
|
expect(committedId).toBeTruthy()
|
|
191
203
|
expect(committedId).not.toBe(SHELF_ID)
|
|
192
204
|
const finalId = committedId as AnyNodeId
|
|
@@ -217,6 +229,132 @@ describe('commitFreshPlacementSubtree', () => {
|
|
|
217
229
|
expect((useScene.getState().nodes[LEVEL_ID] as { children: AnyNodeId[] }).children).toEqual([])
|
|
218
230
|
})
|
|
219
231
|
|
|
232
|
+
// A roof lives on the level above the walls that carry it: one dropped onto them (a duplicate
|
|
233
|
+
// in hand) goes there in the placement's one step.
|
|
234
|
+
test('a roof dropped onto the walls that carry it lands on a new Roof level, in the one step', () => {
|
|
235
|
+
const corners: [number, number][] = [
|
|
236
|
+
[0, 0],
|
|
237
|
+
[8, 0],
|
|
238
|
+
[8, 6],
|
|
239
|
+
[0, 6],
|
|
240
|
+
]
|
|
241
|
+
const walls = corners.map((start, i) =>
|
|
242
|
+
WallNode.parse({ parentId: LEVEL_ID, start, end: corners[(i + 1) % 4], height: 2.5 }),
|
|
243
|
+
)
|
|
244
|
+
const segment = RoofSegmentNode.parse({ width: 8, depth: 6, wallHeight: 0 })
|
|
245
|
+
const draft = RoofNode.parse({
|
|
246
|
+
parentId: LEVEL_ID,
|
|
247
|
+
visible: false,
|
|
248
|
+
metadata: { isNew: true },
|
|
249
|
+
children: [segment.id],
|
|
250
|
+
})
|
|
251
|
+
const building = BuildingNode.parse({ children: [LEVEL_ID] })
|
|
252
|
+
useScene.setState({
|
|
253
|
+
nodes: {
|
|
254
|
+
[building.id]: building,
|
|
255
|
+
[LEVEL_ID]: {
|
|
256
|
+
...level([...walls.map((wall) => wall.id), draft.id]),
|
|
257
|
+
parentId: building.id,
|
|
258
|
+
height: 2.5,
|
|
259
|
+
},
|
|
260
|
+
...Object.fromEntries(walls.map((wall) => [wall.id, wall])),
|
|
261
|
+
[draft.id]: draft,
|
|
262
|
+
[segment.id]: { ...segment, parentId: draft.id },
|
|
263
|
+
},
|
|
264
|
+
rootNodeIds: [building.id],
|
|
265
|
+
collections: {},
|
|
266
|
+
dirtyNodes: new Set(),
|
|
267
|
+
} as never)
|
|
268
|
+
useScene.temporal.getState().clear()
|
|
269
|
+
|
|
270
|
+
const committedId = commitFreshPlacementSubtree(draft.id, {
|
|
271
|
+
position: [4, 2.5, 3],
|
|
272
|
+
visible: true,
|
|
273
|
+
} as Partial<AnyNode>)!
|
|
274
|
+
const nodes = useScene.getState().nodes
|
|
275
|
+
const roofLevel = nodes[nodes[committedId]!.parentId as AnyNodeId] as LevelNode
|
|
276
|
+
expect(roofLevel).toMatchObject({ type: 'level', metadata: { role: 'roof' } })
|
|
277
|
+
expect(roofLevel.children as string[]).toEqual([committedId])
|
|
278
|
+
expect((nodes[LEVEL_ID] as LevelNode).children as string[]).not.toContain(committedId)
|
|
279
|
+
expect(useScene.getState().rootNodeIds).toEqual([building.id])
|
|
280
|
+
|
|
281
|
+
useScene.temporal.getState().undo()
|
|
282
|
+
expect(useScene.getState().nodes[committedId]).toBeUndefined()
|
|
283
|
+
expect(useScene.getState().nodes[roofLevel.id]).toBeUndefined()
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
test('a window joined at a corner while in hand stays joined: its partner names the committed window', () => {
|
|
287
|
+
const wall = WallNode.parse({ id: 'wall_a', parentId: LEVEL_ID, start: [4, 0], end: [0, 0] })
|
|
288
|
+
const other = WallNode.parse({ id: 'wall_b', parentId: LEVEL_ID, start: [0, 0], end: [0, 3] })
|
|
289
|
+
const draft = WindowNode.parse({
|
|
290
|
+
id: 'window_draft',
|
|
291
|
+
parentId: wall.id,
|
|
292
|
+
wallId: wall.id,
|
|
293
|
+
position: [3.5, 1.5, 0],
|
|
294
|
+
width: 1,
|
|
295
|
+
visible: false,
|
|
296
|
+
metadata: { isNew: true },
|
|
297
|
+
corner: { end: 'end', partnerId: 'window_partner', post: 'none' },
|
|
298
|
+
})
|
|
299
|
+
const partner = WindowNode.parse({
|
|
300
|
+
id: 'window_partner',
|
|
301
|
+
parentId: other.id,
|
|
302
|
+
wallId: other.id,
|
|
303
|
+
position: [0.5, 1.5, 0],
|
|
304
|
+
width: 1,
|
|
305
|
+
corner: { end: 'start', partnerId: draft.id, post: 'none' },
|
|
306
|
+
})
|
|
307
|
+
useScene.setState({
|
|
308
|
+
nodes: {
|
|
309
|
+
[LEVEL_ID]: level([wall.id as AnyNodeId, other.id as AnyNodeId]),
|
|
310
|
+
[wall.id]: { ...wall, children: [draft.id] } as AnyNode,
|
|
311
|
+
[other.id]: { ...other, children: [partner.id] } as AnyNode,
|
|
312
|
+
[draft.id]: draft as AnyNode,
|
|
313
|
+
[partner.id]: partner as AnyNode,
|
|
314
|
+
},
|
|
315
|
+
rootNodeIds: [LEVEL_ID],
|
|
316
|
+
collections: {},
|
|
317
|
+
dirtyNodes: new Set(),
|
|
318
|
+
} as never)
|
|
319
|
+
|
|
320
|
+
const committedId = commitFreshPlacementSubtree(draft.id as AnyNodeId, { visible: true })
|
|
321
|
+
|
|
322
|
+
const nodes = useScene.getState().nodes as Record<string, AnyNode>
|
|
323
|
+
expect(committedId).toBeTruthy()
|
|
324
|
+
expect((nodes[committedId!] as WindowNode).corner?.partnerId).toBe(partner.id)
|
|
325
|
+
expect((nodes[partner.id] as WindowNode).corner?.partnerId).toBe(committedId!)
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
test("never records inside another owner's pause", () => {
|
|
329
|
+
pauseSceneHistory(useScene)
|
|
330
|
+
try {
|
|
331
|
+
const committedId = commitFreshPlacementSubtree(SHELF_ID, {
|
|
332
|
+
position: [2, 0, 3],
|
|
333
|
+
visible: true,
|
|
334
|
+
} as Partial<AnyNode>)
|
|
335
|
+
expect(committedId).toBeTruthy()
|
|
336
|
+
expect(useScene.temporal.getState().pastStates).toHaveLength(0)
|
|
337
|
+
expect(useScene.temporal.getState().isTracking).toBe(false)
|
|
338
|
+
expect(getSceneHistoryPauseDepth()).toBe(1)
|
|
339
|
+
} finally {
|
|
340
|
+
resumeSceneHistory(useScene)
|
|
341
|
+
}
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
test("lifts the moving node's gesture session for its one step and keeps it after", () => {
|
|
345
|
+
const overlay = beginSceneHistoryPauseSession(useScene, { gesture: SHELF_ID })
|
|
346
|
+
const committedId = commitFreshPlacementSubtree(SHELF_ID, {
|
|
347
|
+
position: [2, 0, 3],
|
|
348
|
+
visible: true,
|
|
349
|
+
} as Partial<AnyNode>)
|
|
350
|
+
expect(committedId).toBeTruthy()
|
|
351
|
+
expect(useScene.temporal.getState().pastStates).toHaveLength(1)
|
|
352
|
+
expect(useScene.temporal.getState().isTracking).toBe(false)
|
|
353
|
+
overlay.end()
|
|
354
|
+
expect(getSceneHistoryPauseDepth()).toBe(0)
|
|
355
|
+
expect(useScene.temporal.getState().isTracking).toBe(true)
|
|
356
|
+
})
|
|
357
|
+
|
|
220
358
|
test('uses the subtree contract for childless variants and never aliases root-only children', () => {
|
|
221
359
|
registerCabinetClonePrepTestKind()
|
|
222
360
|
const childlessCabinet = {
|
|
@@ -244,6 +382,16 @@ describe('commitFreshPlacementSubtree', () => {
|
|
|
244
382
|
expect((source as AnyNode & { children: AnyNodeId[] }).children).toEqual(['item_original'])
|
|
245
383
|
})
|
|
246
384
|
|
|
385
|
+
// A copy of one side of a corner window kept naming the original's partner, which never named it
|
|
386
|
+
// back: a link that made the copy refuse to join another window.
|
|
387
|
+
test('a copy of one side of a corner window is a plain window', () => {
|
|
388
|
+
const side = WindowNode.parse({
|
|
389
|
+
corner: { end: 'end', partnerId: 'window_partner', post: 'none' },
|
|
390
|
+
})
|
|
391
|
+
expect((prepareFreshPlacementRootDuplicate(side) as WindowNode).corner).toBeUndefined()
|
|
392
|
+
expect(side.corner?.partnerId).toBe('window_partner')
|
|
393
|
+
})
|
|
394
|
+
|
|
247
395
|
test('commits a duplicated cabinet draft without deleting the original modules', () => {
|
|
248
396
|
seedCabinetRun()
|
|
249
397
|
useScene.temporal.getState().clear()
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AnyNode,
|
|
3
3
|
type AnyNodeId,
|
|
4
|
+
beginSceneHistoryPauseSession,
|
|
5
|
+
type CloneNodesIntoResult,
|
|
4
6
|
cloneNodesInto,
|
|
7
|
+
collectionIdsOf,
|
|
5
8
|
collectSubtree,
|
|
6
9
|
createSceneApi,
|
|
7
10
|
type DuplicableConfig,
|
|
11
|
+
getEffectiveNode,
|
|
12
|
+
getSceneHistoryPauseDepth,
|
|
8
13
|
getSurfaceProvider,
|
|
9
14
|
nodeRegistry,
|
|
10
15
|
resolveSurfacePlacement,
|
|
16
|
+
runSceneHistoryDraftWrite,
|
|
11
17
|
type SurfaceRejectReason,
|
|
18
|
+
seatRoofs,
|
|
12
19
|
useScene,
|
|
13
20
|
} from '@pascal-app/core'
|
|
21
|
+
import { remapNodeReferences } from '@pascal-app/core/clone-scene-graph'
|
|
14
22
|
import { evaluateRecipe, isProceduralItem } from '@pascal-app/core/procedural-items'
|
|
15
23
|
import useInteractionScope from '../store/use-interaction-scope'
|
|
16
24
|
import usePlacementPreview from '../store/use-placement-preview'
|
|
@@ -34,6 +42,61 @@ function parentIdOf(node: AnyNode): AnyNodeId | undefined {
|
|
|
34
42
|
return parentId ?? undefined
|
|
35
43
|
}
|
|
36
44
|
|
|
45
|
+
/**
|
|
46
|
+
* The nodes the draft root names, outside its subtree and other than its parent, that name it
|
|
47
|
+
* back (a corner window's partner, joined while the draft was in hand). The commit gives the
|
|
48
|
+
* root a new id, so they are rewritten to name the committed node, never the deleted draft.
|
|
49
|
+
*/
|
|
50
|
+
function peerReferenceUpdates(
|
|
51
|
+
nodes: Readonly<Record<string, AnyNode>>,
|
|
52
|
+
root: AnyNode,
|
|
53
|
+
subtreeIds: ReadonlySet<string>,
|
|
54
|
+
committedId: AnyNodeId,
|
|
55
|
+
): { id: AnyNodeId; data: Partial<AnyNode> }[] {
|
|
56
|
+
const named = new Set<string>()
|
|
57
|
+
const visit = (value: unknown) => {
|
|
58
|
+
if (typeof value === 'string') {
|
|
59
|
+
if (nodes[value] && !subtreeIds.has(value) && value !== root.parentId) named.add(value)
|
|
60
|
+
} else if (Array.isArray(value)) value.forEach(visit)
|
|
61
|
+
else if (value && typeof value === 'object') Object.values(value).forEach(visit)
|
|
62
|
+
}
|
|
63
|
+
const {
|
|
64
|
+
id: _id,
|
|
65
|
+
parentId: _parentId,
|
|
66
|
+
children: _children,
|
|
67
|
+
...fields
|
|
68
|
+
} = root as AnyNode & {
|
|
69
|
+
children?: unknown
|
|
70
|
+
}
|
|
71
|
+
visit(fields)
|
|
72
|
+
const ids = new Map([[root.id, committedId as string]])
|
|
73
|
+
return [...named].flatMap((id) => {
|
|
74
|
+
const peer = nodes[id]!
|
|
75
|
+
const remapped = remapNodeReferences(peer, ids) as Record<string, unknown>
|
|
76
|
+
const data = Object.fromEntries(
|
|
77
|
+
Object.entries(remapped).filter(
|
|
78
|
+
([key, value]) =>
|
|
79
|
+
JSON.stringify(value) !== JSON.stringify((peer as Record<string, unknown>)[key]),
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
return Object.keys(data).length ? [{ id: id as AnyNodeId, data: data as Partial<AnyNode> }] : []
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create ops for a clone of scene nodes: each copy joins the collections its
|
|
88
|
+
* source is in, whatever its kind.
|
|
89
|
+
*/
|
|
90
|
+
export function copyCreateOps(cloned: CloneNodesIntoResult, parentId: AnyNodeId | undefined) {
|
|
91
|
+
const collections = useScene.getState().collections
|
|
92
|
+
const sourceIds = new Map([...cloned.idMap].map(([source, copy]) => [copy, source]))
|
|
93
|
+
return cloned.nodes.map((node, index) => ({
|
|
94
|
+
node,
|
|
95
|
+
...(index === 0 && parentId ? { parentId } : {}),
|
|
96
|
+
collectionIds: collectionIdsOf(collections, sourceIds.get(node.id as AnyNodeId)!),
|
|
97
|
+
}))
|
|
98
|
+
}
|
|
99
|
+
|
|
37
100
|
function duplicableConfigFor(node: AnyNode): DuplicableConfig | null {
|
|
38
101
|
const duplicable = nodeRegistry.get(node.type)?.capabilities?.duplicable
|
|
39
102
|
return duplicable && typeof duplicable === 'object' ? duplicable : null
|
|
@@ -61,6 +124,8 @@ export function prepareFreshPlacementRootDuplicate(node: AnyNode): AnyNode {
|
|
|
61
124
|
metadata?: unknown
|
|
62
125
|
}
|
|
63
126
|
delete duplicate.id
|
|
127
|
+
// A copy of one side of a corner window is a plain window: its partner stays with the original.
|
|
128
|
+
delete duplicate.corner
|
|
64
129
|
if (Array.isArray(duplicate.children)) duplicate.children = []
|
|
65
130
|
duplicate.metadata = {
|
|
66
131
|
...getPlacementMetadataRecord(stripPlacementMetadataFlags(duplicate.metadata)),
|
|
@@ -114,9 +179,7 @@ export function createFreshPlacementSubtree(
|
|
|
114
179
|
})
|
|
115
180
|
|
|
116
181
|
scene.applyNodeChanges({
|
|
117
|
-
create: cloned
|
|
118
|
-
index === 0 && parentId ? { node, parentId } : { node },
|
|
119
|
-
),
|
|
182
|
+
create: copyCreateOps(cloned, parentId),
|
|
120
183
|
update: surfaceAttachmentUpdates(cloned.rootId, parentId, surfaceAttachmentId(subtree.root)),
|
|
121
184
|
})
|
|
122
185
|
|
|
@@ -202,6 +265,21 @@ function namedSurfaceRejection(
|
|
|
202
265
|
return null
|
|
203
266
|
}
|
|
204
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Runs a placement's one committing write as a history step. The moving node's gesture
|
|
270
|
+
* sessions (the 2D move overlay's) are lifted for it; any other owner's pause still holds, so
|
|
271
|
+
* the write then records nothing. A legacy caller's raw `temporal.pause()` is kept afterwards.
|
|
272
|
+
*/
|
|
273
|
+
function recordPlacementStep(rootId: AnyNodeId, wasTracking: boolean, write: () => void): void {
|
|
274
|
+
const step = beginSceneHistoryPauseSession(useScene, { gesture: rootId })
|
|
275
|
+
try {
|
|
276
|
+
step.commitStep(write)
|
|
277
|
+
} finally {
|
|
278
|
+
step.end()
|
|
279
|
+
if (!wasTracking && getSceneHistoryPauseDepth() === 0) useScene.temporal.getState().pause()
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
205
283
|
/**
|
|
206
284
|
* Replace the draft in one validated write. History already excludes fresh
|
|
207
285
|
* subtrees, so this records one creation without first deleting the preview.
|
|
@@ -223,9 +301,13 @@ export function commitFreshPlacementSubtree(
|
|
|
223
301
|
onReject?.(rejection)
|
|
224
302
|
return null
|
|
225
303
|
}
|
|
226
|
-
const descendants = subtree.descendants.map((node) =>
|
|
304
|
+
const descendants = subtree.descendants.map((node) =>
|
|
305
|
+
cleanPlacementMetadata(getEffectiveNode(node)),
|
|
306
|
+
)
|
|
227
307
|
const parentId = parentIdOf(root)
|
|
228
308
|
const cloned = cloneNodesInto([root, ...descendants], { rootId, parentId })
|
|
309
|
+
// The drafts' memberships, read before they are deleted.
|
|
310
|
+
const create = copyCreateOps(cloned, parentId)
|
|
229
311
|
const updates = surfaceAttachmentUpdates(rootId, null, null)
|
|
230
312
|
for (const update of surfaceAttachmentUpdates(cloned.rootId, parentId, surfaceId)) {
|
|
231
313
|
const attachments = { ...(update.data as { attachments: Record<string, string> }).attachments }
|
|
@@ -235,6 +317,12 @@ export function commitFreshPlacementSubtree(
|
|
|
235
317
|
if (previous === -1) updates.push(merged)
|
|
236
318
|
else updates[previous] = merged
|
|
237
319
|
}
|
|
320
|
+
const subtreeIds = new Set([rootId, ...descendants.map((node) => node.id)])
|
|
321
|
+
for (const update of peerReferenceUpdates(scene.nodes, root, subtreeIds, cloned.rootId))
|
|
322
|
+
if (!updates.some((entry) => entry.id === update.id)) updates.push(update)
|
|
323
|
+
|
|
324
|
+
// A roof dropped onto the walls that carry it goes to the level above them, in the same step.
|
|
325
|
+
const seated = seatRoofs(scene.nodes, { create, update: updates, delete: [rootId] }).changes
|
|
238
326
|
|
|
239
327
|
const scope = useInteractionScope.getState()
|
|
240
328
|
const factoryCreated =
|
|
@@ -243,43 +331,32 @@ export function commitFreshPlacementSubtree(
|
|
|
243
331
|
const wasTracking = temporal.isTracking
|
|
244
332
|
if (!factoryCreated && descendants.length === 0 && surfaceId === null) {
|
|
245
333
|
// Preserve the established root-only lifecycle for placements outside the subtree factory.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
temporal.resume()
|
|
250
|
-
useScene.getState().applyNodeChanges({
|
|
251
|
-
create: cloned.nodes.map((node, index) =>
|
|
252
|
-
index === 0 && parentId ? { node, parentId } : { node },
|
|
253
|
-
),
|
|
254
|
-
update: updates,
|
|
334
|
+
runSceneHistoryDraftWrite(() => {
|
|
335
|
+
updateSurfaceNode(rootId, {}, null)
|
|
336
|
+
useScene.getState().deleteNode(rootId)
|
|
255
337
|
})
|
|
256
|
-
|
|
338
|
+
recordPlacementStep(rootId, wasTracking, () =>
|
|
339
|
+
useScene.getState().applyNodeChanges({ create: seated.create, update: seated.update }),
|
|
340
|
+
)
|
|
257
341
|
} else {
|
|
258
|
-
|
|
259
|
-
try {
|
|
260
|
-
// applyNodeChanges validates the complete proposed graph before publishing any part of it.
|
|
261
|
-
scene.applyNodeChanges({
|
|
262
|
-
delete: [rootId],
|
|
263
|
-
create: cloned.nodes.map((node, index) =>
|
|
264
|
-
index === 0 && parentId ? { node, parentId } : { node },
|
|
265
|
-
),
|
|
266
|
-
update: updates,
|
|
267
|
-
})
|
|
268
|
-
for (const node of [subtree.root, ...subtree.descendants]) scene.clearDirty(node.id)
|
|
269
|
-
} catch (error) {
|
|
270
|
-
// Zustand publishes before notifying subscribers. A subscriber error must restore
|
|
271
|
-
// the draft and its ownership/history, never masquerade as a placement refusal.
|
|
272
|
-
temporal.pause()
|
|
342
|
+
recordPlacementStep(rootId, wasTracking, () => {
|
|
273
343
|
try {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
344
|
+
// applyNodeChanges validates the complete proposed graph before publishing any part of it.
|
|
345
|
+
scene.applyNodeChanges(seated)
|
|
346
|
+
for (const node of [subtree.root, ...subtree.descendants]) scene.clearDirty(node.id)
|
|
347
|
+
} catch (error) {
|
|
348
|
+
// Zustand publishes before notifying subscribers. A subscriber error must restore
|
|
349
|
+
// the draft and its ownership/history, never masquerade as a placement refusal.
|
|
350
|
+
useScene.temporal.getState().pause()
|
|
351
|
+
try {
|
|
352
|
+
if (useScene.getState() !== scene) useScene.setState(scene, true)
|
|
353
|
+
} finally {
|
|
354
|
+
useInteractionScope.setState(scope)
|
|
355
|
+
useScene.temporal.setState(temporal)
|
|
356
|
+
}
|
|
357
|
+
throw error
|
|
278
358
|
}
|
|
279
|
-
|
|
280
|
-
} finally {
|
|
281
|
-
if (!wasTracking) temporal.pause()
|
|
282
|
-
}
|
|
359
|
+
})
|
|
283
360
|
}
|
|
284
361
|
useInteractionScope.getState().finishSubtree(rootId)
|
|
285
362
|
if (usePlacementPreview.getState().node?.id === rootId) usePlacementPreview.getState().clear()
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AnyNodeId,
|
|
3
|
+
type CompiledGeometryScript,
|
|
4
|
+
GEOMETRY_SCRIPT_MIME_TYPES,
|
|
5
|
+
GeometryArtifactMetadata,
|
|
6
|
+
GeometryReuseFields,
|
|
7
|
+
type GeometryScriptLanguage,
|
|
8
|
+
type GeometryScriptParamValue,
|
|
9
|
+
generateId,
|
|
10
|
+
getArtifactStore,
|
|
11
|
+
isScriptedNode,
|
|
12
|
+
runAsSingleSceneHistoryStep,
|
|
13
|
+
scriptedObjectMeta,
|
|
14
|
+
useScene,
|
|
15
|
+
} from '@pascal-app/core'
|
|
16
|
+
import {
|
|
17
|
+
addColumn,
|
|
18
|
+
addObject,
|
|
19
|
+
authoredObject,
|
|
20
|
+
editedScriptLanguage,
|
|
21
|
+
editedScriptParams,
|
|
22
|
+
rescriptOpening,
|
|
23
|
+
} from '@pascal-app/core/agent-operations'
|
|
24
|
+
import { compileGeometryScriptInWorker } from './client'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Builds a bpy module and stores its GLB and text, returning their hashes.
|
|
28
|
+
* Blender runs outside the browser, so the host provides this (the hosted app
|
|
29
|
+
* calls its Blender service); without one, bpy scripts are refused.
|
|
30
|
+
*/
|
|
31
|
+
export type BlenderScriptBuilder = (input: {
|
|
32
|
+
code: string
|
|
33
|
+
params?: Record<string, GeometryScriptParamValue>
|
|
34
|
+
nodeId: string
|
|
35
|
+
metadata: GeometryArtifactMetadata
|
|
36
|
+
}) => Promise<CompiledGeometryScript>
|
|
37
|
+
|
|
38
|
+
let blenderBuilder: BlenderScriptBuilder | null = null
|
|
39
|
+
|
|
40
|
+
export function configureBlenderScriptBuilder(next: BlenderScriptBuilder | null): void {
|
|
41
|
+
blenderBuilder = next
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The editor's compile step for `add_object`: runs the module (a three.js one
|
|
46
|
+
* in the worker, a bpy one through the host's Blender builder) and stores the
|
|
47
|
+
* GLB and the module text, so the core operation can reference both by hash.
|
|
48
|
+
* Without `code`, the node's stored script is rebuilt; either way an edit keeps
|
|
49
|
+
* the node's param values it does not override.
|
|
50
|
+
*/
|
|
51
|
+
export async function compileAndStoreGeometryScript(input: {
|
|
52
|
+
code?: string
|
|
53
|
+
language?: GeometryScriptLanguage
|
|
54
|
+
nodeId?: string
|
|
55
|
+
params?: Record<string, GeometryScriptParamValue>
|
|
56
|
+
kind?: 'item' | 'door' | 'window' | 'column'
|
|
57
|
+
inputs?: Record<string, unknown>
|
|
58
|
+
name?: string
|
|
59
|
+
description?: string
|
|
60
|
+
category?: string
|
|
61
|
+
tags?: string[]
|
|
62
|
+
provenance?: GeometryArtifactMetadata
|
|
63
|
+
}): Promise<CompiledGeometryScript> {
|
|
64
|
+
const code = input.code ?? (await storedScript(input.nodeId))
|
|
65
|
+
const node = input.nodeId ? useScene.getState().nodes[input.nodeId as AnyNodeId] : undefined
|
|
66
|
+
const language = editedScriptLanguage(node, input)
|
|
67
|
+
const params = editedScriptParams(node, input.params)
|
|
68
|
+
const kind =
|
|
69
|
+
input.kind ??
|
|
70
|
+
(node?.type === 'door' || node?.type === 'window' || node?.type === 'column'
|
|
71
|
+
? node.type
|
|
72
|
+
: 'item')
|
|
73
|
+
const nodeId = input.nodeId ?? generateId(kind)
|
|
74
|
+
const metadata = GeometryArtifactMetadata.parse({
|
|
75
|
+
...(isScriptedNode(node) ? scriptedObjectMeta(node) : {}),
|
|
76
|
+
...input.provenance,
|
|
77
|
+
...GeometryReuseFields.parse(input),
|
|
78
|
+
kind,
|
|
79
|
+
})
|
|
80
|
+
if (language === 'bpy') {
|
|
81
|
+
if (!blenderBuilder) throw new Error('This editor cannot run Blender (bpy) scripts')
|
|
82
|
+
return { ...(await blenderBuilder({ code, params, nodeId, metadata })), nodeId }
|
|
83
|
+
}
|
|
84
|
+
const { glb, ...compiled } = await compileGeometryScriptInWorker({
|
|
85
|
+
code,
|
|
86
|
+
params,
|
|
87
|
+
inputs: input.inputs,
|
|
88
|
+
})
|
|
89
|
+
const context = { nodeId, metadata: { ...metadata, mount: compiled.mount } }
|
|
90
|
+
const store = getArtifactStore()
|
|
91
|
+
const [sha256] = await Promise.all([
|
|
92
|
+
store.put(compiled.sha256, glb, 'model/gltf-binary', context),
|
|
93
|
+
store.put(
|
|
94
|
+
compiled.script,
|
|
95
|
+
new TextEncoder().encode(code),
|
|
96
|
+
GEOMETRY_SCRIPT_MIME_TYPES.three,
|
|
97
|
+
context,
|
|
98
|
+
),
|
|
99
|
+
])
|
|
100
|
+
return { ...compiled, sha256, nodeId }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The module text of an authored object, read back from the artifact store. */
|
|
104
|
+
export async function storedScript(nodeId: string | undefined): Promise<string> {
|
|
105
|
+
if (!nodeId) throw new Error('Pass the code to build a new object')
|
|
106
|
+
const node = authoredObject(useScene.getState().nodes, nodeId)
|
|
107
|
+
const code = await getArtifactStore().text(node.source.script)
|
|
108
|
+
if (code === null) throw new Error(`The script of ${nodeId} could not be read`)
|
|
109
|
+
return code
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const rebuildGeneration = new Map<string, number>()
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Re-runs a scripted node's script with new param values, the inspector's
|
|
116
|
+
* and resize handles' path: same code, new artifact, one undo step. A slower earlier rebuild of
|
|
117
|
+
* the same node never overwrites a newer one.
|
|
118
|
+
*/
|
|
119
|
+
export async function rebuildAuthoredObject(
|
|
120
|
+
nodeId: string,
|
|
121
|
+
params: Record<string, GeometryScriptParamValue>,
|
|
122
|
+
/** Where it ends up, when a resize also moves it (a side arrow keeps the opposite edge). */
|
|
123
|
+
position?: [number, number, number],
|
|
124
|
+
): Promise<void> {
|
|
125
|
+
const generation = (rebuildGeneration.get(nodeId) ?? 0) + 1
|
|
126
|
+
rebuildGeneration.set(nodeId, generation)
|
|
127
|
+
const compiled = await compileAndStoreGeometryScript({ nodeId, params })
|
|
128
|
+
if (rebuildGeneration.get(nodeId) !== generation) return
|
|
129
|
+
const changes = rebuildChanges(nodeId, compiled, params, position)
|
|
130
|
+
runAsSingleSceneHistoryStep(useScene, () => {
|
|
131
|
+
for (const { id, data } of changes?.update ?? []) {
|
|
132
|
+
useScene.getState().updateNode(id as AnyNodeId, data)
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** What a rebuild changes, through the operation of the node's kind. */
|
|
138
|
+
function rebuildChanges(
|
|
139
|
+
nodeId: string,
|
|
140
|
+
compiled: CompiledGeometryScript,
|
|
141
|
+
params: Record<string, GeometryScriptParamValue>,
|
|
142
|
+
position: [number, number, number] | undefined,
|
|
143
|
+
) {
|
|
144
|
+
const nodes = useScene.getState().nodes
|
|
145
|
+
const context = { activeLevelId: null }
|
|
146
|
+
switch (nodes[nodeId as AnyNodeId]?.type) {
|
|
147
|
+
case 'column': {
|
|
148
|
+
const at = position && { x: position[0], y: position[1], z: position[2] }
|
|
149
|
+
return addColumn(nodes, { nodeId, compiled, ...at }, context).changes
|
|
150
|
+
}
|
|
151
|
+
case 'window':
|
|
152
|
+
case 'door':
|
|
153
|
+
return rescriptOpening(nodes, { nodeId, compiled, position }, context).changes
|
|
154
|
+
default:
|
|
155
|
+
return addObject(nodes, { params, nodeId, compiled, position }, context).changes
|
|
156
|
+
}
|
|
157
|
+
}
|