@pascal-app/editor 1.0.6 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +18 -15
- package/src/components/editor/action-menu-button.tsx +4 -1
- package/src/components/editor/alignment-3d-guide-layer.tsx +102 -11
- package/src/components/editor/bake-thumbnail.tsx +3 -2
- package/src/components/editor/custom-camera-controls.tsx +110 -42
- package/src/components/editor/duplicate-node.ts +2 -1
- package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
- package/src/components/editor/first-person-controls.tsx +77 -68
- package/src/components/editor/fit-scene-framing.test.ts +318 -0
- package/src/components/editor/fit-scene-framing.ts +142 -0
- package/src/components/editor/floating-action-menu.tsx +31 -11
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floor-openings-overlay.tsx +1 -1
- package/src/components/editor/floor-region-controls.tsx +2 -2
- package/src/components/editor/floorplan-panel.test.ts +220 -0
- package/src/components/editor/floorplan-panel.tsx +458 -140
- package/src/components/editor/floorplan-wall-typing-keys.test.tsx +152 -0
- package/src/components/editor/floorplan-wall-typing-keys.ts +79 -0
- package/src/components/editor/footprint-height-handle.tsx +2 -2
- package/src/components/editor/grid.tsx +24 -15
- package/src/components/editor/group-actions.ts +19 -1
- package/src/components/editor/group-floating-action-menu.tsx +1 -1
- package/src/components/editor/group-move-3d.ts +27 -2
- package/src/components/editor/group-selection-box-3d.tsx +4 -1
- package/src/components/editor/group-transform-shared.ts +23 -1
- package/src/components/editor/index.tsx +150 -20
- package/src/components/editor/inspector-extension-actions.tsx +36 -0
- package/src/components/editor/localized-html.tsx +22 -0
- package/src/components/editor/measurement-pill.tsx +1 -1
- package/src/components/editor/node-action-menu.tsx +46 -12
- package/src/components/editor/node-arrow-handles.tsx +2 -3
- package/src/components/editor/open-wall-ends-3d-layer.tsx +2 -3
- package/src/components/editor/open-wall-ends-hint.tsx +1 -1
- package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
- package/src/components/editor/quick-measurement-card.tsx +1 -1
- package/src/components/editor/registry-action-contributions.test.tsx +67 -6
- package/src/components/editor/registry-action-contributions.tsx +115 -15
- package/src/components/editor/riser-diagram-panel.tsx +59 -9
- package/src/components/editor/room-controls.tsx +4 -3
- package/src/components/editor/room-handles-2d.tsx +158 -7
- package/src/components/editor/room-handles.tsx +244 -54
- package/src/components/editor/room-highlight.tsx +2 -0
- package/src/components/editor/room-labels.tsx +33 -17
- package/src/components/editor/selection-manager.tsx +15 -102
- package/src/components/editor/separator-end-grip.tsx +74 -0
- package/src/components/editor/site-edge-labels.tsx +7 -3
- package/src/components/editor/thumbnail-generator.test.ts +15 -6
- package/src/components/editor/thumbnail-generator.tsx +26 -1
- package/src/components/editor/use-floorplan-background-placement.ts +55 -52
- package/src/components/editor/views/view-bar.tsx +111 -26
- package/src/components/editor/wall-measurement-label.tsx +31 -15
- package/src/components/editor/wall-move-side-handles.tsx +15 -7
- package/src/components/editor/wall-pull-grip.tsx +193 -0
- package/src/components/editor/wall-region-handles.tsx +2 -2
- package/src/components/editor/wall-snap-beacon-layer.tsx +134 -33
- package/src/components/editor-2d/floorplan-adjacent-walls-layer.test.ts +76 -0
- package/src/components/editor-2d/floorplan-adjacent-walls-layer.tsx +186 -0
- package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +63 -3
- package/src/components/editor-2d/floorplan-group-move.tsx +32 -4
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +6 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +33 -24
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +12 -5
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +2 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +29 -5
- package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +96 -4
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +52 -12
- package/src/components/editor-2d/renderers/floorplan-draft-measurement.tsx +7 -1
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +13 -1
- package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.test.tsx +35 -0
- package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.tsx +65 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +34 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +211 -39
- package/src/components/procedural-items/inspector.tsx +59 -12
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +2 -0
- package/src/components/systems/ceiling/ceiling-system.test.ts +141 -0
- package/src/components/systems/ceiling/ceiling-system.tsx +53 -18
- package/src/components/systems/roof/roof-edit-system.test.tsx +15 -1
- package/src/components/systems/roof/roof-edit-system.tsx +30 -19
- package/src/components/tools/distribution/distribution-run-tool.test.tsx +371 -3
- package/src/components/tools/distribution/distribution-run-tool.tsx +200 -58
- package/src/components/tools/item/move-tool.tsx +1 -6
- package/src/components/tools/item/placement-history.test.tsx +1 -1
- package/src/components/tools/item/placement-strategies.test.ts +64 -1
- package/src/components/tools/item/placement-strategies.ts +5 -9
- package/src/components/tools/item/use-placement-coordinator.tsx +31 -35
- package/src/components/tools/paint-region/wall-paint-region-tool.tsx +1 -1
- package/src/components/tools/registry/item-surface-move.test.ts +11 -0
- package/src/components/tools/registry/move-registry-node-tool.test.ts +42 -1
- package/src/components/tools/registry/move-registry-node-tool.tsx +38 -14
- package/src/components/tools/select/select-candidates.test.ts +45 -0
- package/src/components/tools/select/select-candidates.ts +13 -2
- package/src/components/tools/shared/cursor-sphere.tsx +10 -3
- package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
- package/src/components/tools/shared/placement-box.tsx +2 -6
- package/src/components/tools/shared/placement-coordinate-input.tsx +94 -0
- package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
- package/src/components/tools/shared/segment-angle.ts +0 -4
- package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
- package/src/components/tools/tool-manager.tsx +1 -24
- package/src/components/tools/wall/wall-drafting.test.ts +299 -23
- package/src/components/tools/wall/wall-drafting.ts +132 -24
- package/src/components/tools/wall/wall-pull-snap.test.ts +274 -0
- package/src/components/tools/wall/wall-pull-snap.ts +563 -0
- package/src/components/tools/wall/wall-snap-geometry.test.ts +113 -1
- package/src/components/tools/wall/wall-snap-geometry.ts +137 -0
- package/src/components/tools/wall/wall-typed-entry.test.ts +99 -0
- package/src/components/tools/wall/wall-typed-entry.ts +70 -0
- package/src/components/tools/zone/zone-tool.tsx +3 -3
- package/src/components/ui/action-menu/action-button.tsx +2 -0
- package/src/components/ui/action-menu/camera-actions.tsx +8 -7
- package/src/components/ui/action-menu/control-modes.tsx +9 -5
- package/src/components/ui/action-menu/follow-pascal-pill.tsx +3 -1
- package/src/components/ui/action-menu/follow-pascal-toggle.tsx +7 -1
- package/src/components/ui/action-menu/furnish-tools.tsx +7 -6
- package/src/components/ui/action-menu/index.tsx +1 -0
- package/src/components/ui/action-menu/measurement-control.tsx +35 -29
- package/src/components/ui/action-menu/point-ask-button.tsx +4 -3
- package/src/components/ui/action-menu/structure-tools.tsx +25 -23
- package/src/components/ui/action-menu/view-toggles.test.ts +18 -0
- package/src/components/ui/action-menu/view-toggles.tsx +256 -51
- package/src/components/ui/build-panel/build-panel.tsx +163 -25
- package/src/components/ui/command-palette/editor-commands.tsx +38 -11
- package/src/components/ui/controls/finish-swatch.tsx +45 -19
- package/src/components/ui/controls/material-picker.tsx +24 -17
- package/src/components/ui/controls/metric-control.test.ts +52 -0
- package/src/components/ui/controls/metric-control.tsx +62 -24
- package/src/components/ui/controls/segmented-control.tsx +5 -2
- package/src/components/ui/controls/slider-control.test.tsx +23 -0
- package/src/components/ui/controls/slider-control.tsx +47 -19
- package/src/components/ui/controls/tool-options-panel.tsx +74 -44
- package/src/components/ui/floating-level-selector.tsx +100 -48
- package/src/components/ui/helpers/building-helper.tsx +5 -3
- package/src/components/ui/helpers/contextual-helper-panel.tsx +97 -37
- package/src/components/ui/helpers/helper-manager.test.tsx +106 -3
- package/src/components/ui/helpers/helper-manager.tsx +136 -57
- package/src/components/ui/helpers/item-helper.tsx +6 -4
- package/src/components/ui/helpers/registered-tool-helper.tsx +3 -1
- package/src/components/ui/level-duplicate-dialog.tsx +32 -23
- package/src/components/ui/level-wall-copy-prompt.tsx +124 -0
- package/src/components/ui/panels/collections/collections-popover.tsx +11 -11
- package/src/components/ui/panels/deck-boards-fields.tsx +136 -0
- package/src/components/ui/panels/floor-foundation-panel.tsx +114 -39
- package/src/components/ui/panels/floor-opening-panel.tsx +74 -26
- package/src/components/ui/panels/inspector-extensions.tsx +66 -0
- package/src/components/ui/panels/mobile-panel-sheet.tsx +3 -1
- package/src/components/ui/panels/mobile-selection-bar.tsx +34 -28
- package/src/components/ui/panels/multi-field-value.test.ts +27 -0
- package/src/components/ui/panels/multi-field-value.ts +20 -1
- package/src/components/ui/panels/multi-height-mode.tsx +11 -10
- package/src/components/ui/panels/multi-parametric-inspector.tsx +122 -34
- package/src/components/ui/panels/multi-selection-panel.tsx +76 -36
- package/src/components/ui/panels/node-display.ts +35 -24
- package/src/components/ui/panels/panel-manager.tsx +9 -5
- package/src/components/ui/panels/panel-wrapper.tsx +63 -127
- package/src/components/ui/panels/parametric-field-control.tsx +13 -10
- package/src/components/ui/panels/parametric-inspector.tsx +18 -12
- package/src/components/ui/panels/reference-panel.tsx +51 -35
- package/src/components/ui/panels/room-construction-rows.tsx +465 -66
- package/src/components/ui/panels/room-name-options.test.ts +36 -3
- package/src/components/ui/panels/room-name-options.ts +110 -11
- package/src/components/ui/panels/room-openings-field.tsx +22 -13
- package/src/components/ui/panels/room-panel.tsx +207 -100
- package/src/components/ui/panels/shape-choice.tsx +7 -5
- package/src/components/ui/panels/wall-region-list.tsx +7 -7
- package/src/components/ui/primitives/shortcut-token.tsx +10 -6
- package/src/components/ui/right-stack.test.ts +13 -6
- package/src/components/ui/right-stack.tsx +21 -10
- package/src/components/ui/sidebar/icon-rail.tsx +11 -9
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +10 -10
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +10 -9
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +3 -0
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +102 -32
- package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +4 -3
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +5 -3
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +7 -5
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/default-names.ts +75 -0
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +118 -72
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +36 -3
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +7 -4
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +8 -2
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +8 -5
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +9 -4
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +8 -7
- package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +7 -6
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +16 -1
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +15 -8
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-membership-checkbox.tsx +3 -1
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +19 -9
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +25 -14
- package/src/components/ui/sidebar/tab-bar.tsx +4 -1
- package/src/components/ui/sidebar/use-plugin-panels.tsx +19 -10
- package/src/components/ui/snap-target-badge.tsx +7 -6
- package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +721 -0
- package/src/components/ui/wall-type/wall-type-menu.tsx +167 -0
- package/src/components/viewer/floorplan-compass-button.tsx +12 -3
- package/src/components/viewer/floorplan-preview.tsx +27 -19
- package/src/components/viewer/render-settings-menu.tsx +294 -0
- package/src/components/viewer/sky-control.tsx +710 -0
- package/src/components/viewer/use-viewer-ui-sounds.test.ts +15 -0
- package/src/components/viewer/use-viewer-ui-sounds.ts +46 -0
- package/src/components/viewer/viewer-controls-bar.tsx +278 -260
- package/src/components/viewer/viewer-hover-label.tsx +12 -4
- package/src/components/viewer/viewer-rooms.tsx +62 -5
- package/src/components/viewer/viewer-scene-header.tsx +28 -17
- package/src/components/viewer/viewer-stage-switcher.tsx +4 -2
- package/src/components/viewer-overlay.tsx +8 -0
- package/src/components/walkthrough-hud.tsx +75 -2
- package/src/hooks/use-keyboard.test.ts +85 -4
- package/src/hooks/use-keyboard.ts +65 -7
- package/src/hooks/use-selected-room.ts +12 -1
- package/src/i18n/floor-footprint-name.ts +30 -0
- package/src/i18n/level-name.ts +39 -0
- package/src/i18n/locales/en/messages.po +15849 -0
- package/src/i18n/locales/en/messages.ts +1 -0
- package/src/i18n/locales/en-XA/messages.ts +1 -0
- package/src/i18n/locales/es/messages.po +39331 -0
- package/src/i18n/locales/es/messages.ts +1 -0
- package/src/i18n/locales/fr/messages.po +39331 -0
- package/src/i18n/locales/fr/messages.ts +1 -0
- package/src/i18n/locales/ja/messages.po +39331 -0
- package/src/i18n/locales/ja/messages.ts +1 -0
- package/src/i18n/locales/zh-CN/messages.po +39331 -0
- package/src/i18n/locales/zh-CN/messages.ts +1 -0
- package/src/i18n/locales.test.ts +63 -0
- package/src/i18n/locales.ts +32 -0
- package/src/i18n/message-slot.tsx +25 -0
- package/src/i18n/node-messages.ts +169 -0
- package/src/i18n/pseudo-locale.ts +17 -0
- package/src/i18n/published-entry.test.ts +17 -0
- package/src/i18n/scene-theme-name.ts +16 -0
- package/src/index.tsx +224 -45
- package/src/lib/add-storey.test.ts +3 -3
- package/src/lib/contextual-help.test.ts +20 -1
- package/src/lib/contextual-help.ts +101 -19
- package/src/lib/continuation.ts +38 -11
- package/src/lib/elevation-guides.ts +21 -6
- package/src/lib/elevator-support.ts +1 -72
- package/src/lib/floor-footprints.test.ts +3 -2
- package/src/lib/floor-footprints.ts +1 -9
- package/src/lib/floor-foundation-support.test.ts +15 -6
- package/src/lib/floor-opening-draft.ts +1 -1
- package/src/lib/floor-region-session.ts +1 -1
- package/src/lib/floorplan/dimension-drive.test.ts +36 -2
- package/src/lib/floorplan/dimension-drive.ts +68 -3
- package/src/lib/floorplan/floorplan-export.test.ts +16 -5
- package/src/lib/floorplan/floorplan-export.tsx +13 -16
- package/src/lib/floorplan/floorplan-extension.test.ts +11 -12
- package/src/lib/floorplan/floorplan-extension.ts +37 -18
- package/src/lib/floorplan/floorplan-mode.test.ts +13 -6
- package/src/lib/floorplan/floorplan-mode.ts +14 -12
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +1 -1
- package/src/lib/floorplan/floorplan-readonly.ts +3 -3
- package/src/lib/floorplan/open-wall-ends.ts +2 -1
- package/src/lib/floorplan/schedules.ts +10 -6
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +46 -1
- package/src/lib/floorplan/site-plan/site-annotations.ts +3 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +6 -3
- package/src/lib/geometry-script/author.test.ts +114 -0
- package/src/lib/geometry-script/author.ts +55 -23
- package/src/lib/geometry-script/client.ts +3 -0
- package/src/lib/geometry-script/finish-scripts.ts +7 -7
- package/src/lib/gesture-lifecycle.ts +3 -1
- package/src/lib/glb-export.test.ts +327 -0
- package/src/lib/glb-export.ts +70 -2
- package/src/lib/grid-frame.test.ts +56 -3
- package/src/lib/grid-frame.ts +28 -1
- package/src/lib/history.test.ts +2 -1
- package/src/lib/hud-title.ts +48 -24
- package/src/lib/ifc-export.test.ts +41 -1
- package/src/lib/inspector-card-mode.test.ts +3 -31
- package/src/lib/inspector-card-mode.ts +5 -10
- package/src/lib/inspector-expanded.test.ts +13 -0
- package/src/lib/inspector-expanded.ts +13 -1
- package/src/lib/interaction/scope.ts +1 -0
- package/src/lib/keyboard-pan.test.ts +7 -0
- package/src/lib/keyboard-pan.ts +9 -2
- package/src/lib/layer-thickness.test.ts +22 -0
- package/src/lib/layer-thickness.ts +79 -0
- package/src/lib/level-selection.test.ts +18 -2
- package/src/lib/level-selection.ts +31 -8
- package/src/lib/linear-display.ts +1 -1
- package/src/lib/live-gesture-round-trip.test.ts +1 -1
- package/src/lib/lot/drop-in.test.ts +59 -1
- package/src/lib/lot/drop-in.ts +42 -50
- package/src/lib/material-swatch-style.ts +24 -0
- package/src/lib/measurement-label.test.ts +1 -1
- package/src/lib/measurement-parser.ts +7 -3
- package/src/lib/measurements.test.ts +24 -6
- package/src/lib/measurements.ts +1 -203
- package/src/lib/mezzanine-selection.test.ts +3 -1
- package/src/lib/node-action-movement.test.tsx +115 -0
- package/src/lib/node-action-movement.ts +2 -0
- package/src/lib/orbit-near-plane.test.ts +12 -0
- package/src/lib/orbit-near-plane.ts +13 -0
- package/src/lib/paint-regions.test.ts +8 -5
- package/src/lib/paint-scope.test.ts +53 -0
- package/src/lib/paint-scope.ts +18 -4
- package/src/lib/panel-rows.ts +17 -8
- package/src/lib/placement-metadata.ts +4 -0
- package/src/lib/point-ask/describe-target.ts +6 -8
- package/src/lib/point-ask/region.ts +1 -1
- package/src/lib/print-shell-compiler.ts +12 -1
- package/src/lib/print-wall-solids.test.ts +11 -0
- package/src/lib/print-wall-solids.ts +9 -0
- package/src/lib/reference-scale.ts +83 -0
- package/src/lib/room-assembly-overlay.ts +1 -1
- package/src/lib/room-built-on.test.ts +28 -17
- package/src/lib/room-built-on.ts +21 -17
- package/src/lib/room-construction-commands.ts +3 -2
- package/src/lib/room-construction.test.ts +28 -1
- package/src/lib/room-construction.ts +14 -0
- package/src/lib/room-controls-phase6.test.ts +141 -1
- package/src/lib/room-handle-drag.ts +554 -58
- package/src/lib/room-name-catalog.test.ts +8 -0
- package/src/lib/room-name-catalog.ts +8 -1
- package/src/lib/room-name-messages.test.ts +132 -0
- package/src/lib/room-name-messages.ts +147 -0
- package/src/lib/room-name-storage.test.ts +334 -0
- package/src/lib/room-push-dimensions.test.ts +38 -1
- package/src/lib/room-push-dimensions.ts +226 -2
- package/src/lib/room-selection.test.ts +23 -6
- package/src/lib/scene-bounds.test.ts +144 -0
- package/src/lib/scene-bounds.ts +79 -15
- package/src/lib/scene-clipboard.test.ts +70 -49
- package/src/lib/scene-clipboard.ts +11 -5
- package/src/lib/selection-cycle.ts +63 -0
- package/src/lib/separator-edit.test.ts +431 -0
- package/src/lib/separator-edit.ts +179 -0
- package/src/lib/separator-lines.ts +38 -0
- package/src/lib/sfx-bus.ts +27 -1
- package/src/lib/sfx-player.ts +16 -0
- package/src/lib/snapping-mode.test.ts +23 -1
- package/src/lib/tool-cursor-icon.ts +5 -1
- package/src/lib/tool-shortcuts.ts +130 -0
- package/src/lib/viewer-selection.test.ts +28 -24
- package/src/lib/viewer-selection.ts +21 -6
- package/src/lib/wall-draw-variant.test.ts +7 -6
- package/src/lib/wall-draw-variant.ts +107 -6
- package/src/lib/wall-nudge.test.ts +44 -0
- package/src/lib/wall-nudge.ts +131 -0
- package/src/lib/wall-parallel-snap.test.ts +83 -0
- package/src/lib/wall-parallel-snap.ts +176 -0
- package/src/lib/wall-placement-pointer.test.ts +23 -0
- package/src/lib/wall-placement-pointer.ts +18 -0
- package/src/lib/wall-pull.test.ts +245 -0
- package/src/lib/wall-pull.ts +209 -0
- package/src/lib/wall-push-line-up.test.ts +132 -0
- package/src/lib/wall-region-list.ts +17 -6
- package/src/lib/wall-run-snap.test.ts +188 -0
- package/src/lib/wall-run-snap.ts +83 -0
- package/src/lib/wall-type.test.ts +125 -0
- package/src/lib/wall-type.ts +119 -0
- package/src/lib/window-interaction.test.ts +44 -0
- package/src/lib/world-grid-snap.test.ts +139 -2
- package/src/lib/world-grid-snap.ts +102 -105
- package/src/store/use-alignment-guides.ts +27 -1
- package/src/store/use-editor.tsx +53 -0
- package/src/store/use-floorplan-annotation-visibility.test.ts +64 -0
- package/src/store/use-floorplan-annotation-visibility.ts +47 -22
- package/src/store/use-placement-typing.test.ts +165 -0
- package/src/store/use-placement-typing.ts +186 -0
- package/src/store/use-wall-snap-indicator.ts +15 -2
- package/src/components/tools/elevator/elevator-defaults.ts +0 -8
- package/src/components/tools/elevator/elevator-tool.tsx +0 -372
- package/src/components/tools/elevator/move-elevator-tool.tsx +0 -262
- package/src/lib/door-interaction.ts +0 -102
- package/src/lib/measurement-label.ts +0 -68
- package/src/lib/window-interaction.ts +0 -100
|
@@ -7,15 +7,22 @@ import {
|
|
|
7
7
|
containsPoint,
|
|
8
8
|
DEFAULT_LEVEL_HEIGHT,
|
|
9
9
|
emitter,
|
|
10
|
+
formatNumber,
|
|
11
|
+
getAreaUnitLabel,
|
|
12
|
+
type LinearUnit,
|
|
13
|
+
type PascalI18n,
|
|
10
14
|
type Polygon,
|
|
11
15
|
polygonInteriorPoint,
|
|
12
16
|
type Ring,
|
|
17
|
+
roomFloorElevation,
|
|
13
18
|
sceneRegistry,
|
|
19
|
+
squareMetersToAreaUnit,
|
|
20
|
+
useLocale,
|
|
21
|
+
usePascalI18n,
|
|
14
22
|
useScene,
|
|
15
23
|
type ZoneNode,
|
|
16
24
|
} from '@pascal-app/core'
|
|
17
25
|
import { timeSpan, useViewer } from '@pascal-app/viewer'
|
|
18
|
-
import { Html } from '@react-three/drei'
|
|
19
26
|
import { useFrame, useThree } from '@react-three/fiber'
|
|
20
27
|
import {
|
|
21
28
|
createContext,
|
|
@@ -45,19 +52,14 @@ import {
|
|
|
45
52
|
} from 'three'
|
|
46
53
|
import { useShallow } from 'zustand/react/shallow'
|
|
47
54
|
import { useRoomRecords } from '../../hooks/use-selected-room'
|
|
48
|
-
import {
|
|
49
|
-
import { roomFloorElevation } from '../../lib/room-handle-drag'
|
|
55
|
+
import { displayRoomName } from '../../lib/room-name-messages'
|
|
50
56
|
import type { RoomSelectionRecord } from '../../lib/room-selection'
|
|
51
|
-
import {
|
|
52
|
-
clickZoneArea,
|
|
53
|
-
hoverZoneArea,
|
|
54
|
-
leaveZoneArea,
|
|
55
|
-
zoneKindLabel,
|
|
56
|
-
} from '../../lib/room-zone-routing'
|
|
57
|
+
import { clickZoneArea, hoverZoneArea, leaveZoneArea } from '../../lib/room-zone-routing'
|
|
57
58
|
import { selectionModifiersFromEvent } from '../../lib/selection-routing'
|
|
58
59
|
import useEditor from '../../store/use-editor'
|
|
59
60
|
import useInteractionScope from '../../store/use-interaction-scope'
|
|
60
61
|
import { isViewVisible, VIEW_2D, VIEW_3D } from '../../store/view-layout'
|
|
62
|
+
import { Html } from './localized-html'
|
|
61
63
|
|
|
62
64
|
const CONTEXT_TYPES = new Set(['site', 'building', 'level'])
|
|
63
65
|
|
|
@@ -92,8 +94,12 @@ export function roomLabelsVisible(state: RoomLabelVisibilityState): boolean {
|
|
|
92
94
|
)
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
export function formatRoomArea(squareMeters: number, unit: LinearUnit): string {
|
|
96
|
-
return `${squareMetersToAreaUnit(squareMeters, unit)
|
|
97
|
+
export function formatRoomArea(squareMeters: number, unit: LinearUnit, locale?: string): string {
|
|
98
|
+
return `${formatNumber(squareMetersToAreaUnit(squareMeters, unit), {
|
|
99
|
+
min: 1,
|
|
100
|
+
max: 1,
|
|
101
|
+
locale,
|
|
102
|
+
})} ${getAreaUnitLabel(unit)}`
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
function useRoomLabelsVisible() {
|
|
@@ -1169,11 +1175,12 @@ export function RoomPillChip({
|
|
|
1169
1175
|
onLeave: () => void
|
|
1170
1176
|
}) {
|
|
1171
1177
|
const unit = useViewer((s) => s.unit)
|
|
1172
|
-
const
|
|
1178
|
+
const locale = useLocale()
|
|
1179
|
+
const areaLabel = formatRoomArea(areaM2, unit, locale)
|
|
1173
1180
|
// biome-ignore lint/correctness/useExhaustiveDependencies: A dot's border box does not resize when its hidden text changes.
|
|
1174
1181
|
useLayoutEffect(() => {
|
|
1175
1182
|
if (elements instanceof PillElements) elements.measure(zoneId)
|
|
1176
|
-
}, [areaM2, elements, name, unit, zoneId])
|
|
1183
|
+
}, [areaM2, elements, locale, name, unit, zoneId])
|
|
1177
1184
|
const register = useCallback(
|
|
1178
1185
|
(element: HTMLElement | null) => {
|
|
1179
1186
|
if (element) elements.set(zoneId, element)
|
|
@@ -1227,6 +1234,7 @@ export function RoomPillDetail({
|
|
|
1227
1234
|
areaM2: number
|
|
1228
1235
|
}) {
|
|
1229
1236
|
const unit = useViewer((s) => s.unit)
|
|
1237
|
+
const locale = useLocale()
|
|
1230
1238
|
return (
|
|
1231
1239
|
<div
|
|
1232
1240
|
aria-hidden
|
|
@@ -1234,7 +1242,7 @@ export function RoomPillDetail({
|
|
|
1234
1242
|
data-room-label-detail={zoneId}
|
|
1235
1243
|
data-room-label-view={view}
|
|
1236
1244
|
>
|
|
1237
|
-
<LabelParts areaLabel={formatRoomArea(areaM2, unit)} color={color} full name={name} />
|
|
1245
|
+
<LabelParts areaLabel={formatRoomArea(areaM2, unit, locale)} color={color} full name={name} />
|
|
1238
1246
|
</div>
|
|
1239
1247
|
)
|
|
1240
1248
|
}
|
|
@@ -1244,7 +1252,13 @@ function useLabelZone(zoneId: string) {
|
|
|
1244
1252
|
return zone && zone.visible !== false ? zone : null
|
|
1245
1253
|
}
|
|
1246
1254
|
|
|
1247
|
-
const labelName = (zone: ZoneNode
|
|
1255
|
+
const labelName = (zone: ZoneNode, i18n: PascalI18n) => {
|
|
1256
|
+
const name = zone.name?.trim()
|
|
1257
|
+
if (name) return displayRoomName(name, i18n)
|
|
1258
|
+
return zone.spaceRole === 'room'
|
|
1259
|
+
? i18n._({ id: 'editor.roomLabel.room', message: 'Room' })
|
|
1260
|
+
: i18n._({ id: 'editor.roomLabel.zone', message: 'Zone' })
|
|
1261
|
+
}
|
|
1248
1262
|
|
|
1249
1263
|
/**
|
|
1250
1264
|
* One room's pill in the editor. Hover and click take the same rule as the
|
|
@@ -1260,6 +1274,7 @@ function RoomPill({
|
|
|
1260
1274
|
view: '2d' | '3d'
|
|
1261
1275
|
elements: Map<string, HTMLElement>
|
|
1262
1276
|
}) {
|
|
1277
|
+
const i18n = usePascalI18n()
|
|
1263
1278
|
const zone = useLabelZone(entry.zoneId)
|
|
1264
1279
|
// A pill that disappears under the cursor (its click selected something)
|
|
1265
1280
|
// fires no leave: end the hover it set.
|
|
@@ -1270,7 +1285,7 @@ function RoomPill({
|
|
|
1270
1285
|
areaM2={entry.area}
|
|
1271
1286
|
color={zone.color}
|
|
1272
1287
|
elements={elements}
|
|
1273
|
-
name={labelName(zone)}
|
|
1288
|
+
name={labelName(zone, i18n)}
|
|
1274
1289
|
onClick={(event) => clickZoneArea(entry.zoneId, selectionModifiersFromEvent(event))}
|
|
1275
1290
|
onHover={(event) => hoverZoneArea(entry.zoneId, selectionModifiersFromEvent(event))}
|
|
1276
1291
|
onLeave={() => leaveZoneArea(entry.zoneId)}
|
|
@@ -1281,13 +1296,14 @@ function RoomPill({
|
|
|
1281
1296
|
}
|
|
1282
1297
|
|
|
1283
1298
|
function RoomDetail({ entry, view }: { entry: LabelEntry; view: '2d' | '3d' }) {
|
|
1299
|
+
const i18n = usePascalI18n()
|
|
1284
1300
|
const zone = useLabelZone(entry.zoneId)
|
|
1285
1301
|
if (!zone) return null
|
|
1286
1302
|
return (
|
|
1287
1303
|
<RoomPillDetail
|
|
1288
1304
|
areaM2={entry.area}
|
|
1289
1305
|
color={zone.color}
|
|
1290
|
-
name={labelName(zone)}
|
|
1306
|
+
name={labelName(zone, i18n)}
|
|
1291
1307
|
view={view}
|
|
1292
1308
|
zoneId={entry.zoneId}
|
|
1293
1309
|
/>
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
type NodeEvent,
|
|
16
16
|
nodeRegistry,
|
|
17
17
|
type RoofEvent,
|
|
18
|
-
type RoofNode,
|
|
19
18
|
type RoofSegmentEvent,
|
|
20
19
|
type RoofSegmentNode,
|
|
21
20
|
resolveLevelId,
|
|
@@ -29,7 +28,13 @@ import {
|
|
|
29
28
|
useScene,
|
|
30
29
|
wallFaceScriptPaintRefusal,
|
|
31
30
|
} from '@pascal-app/core'
|
|
32
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
markHighlight,
|
|
33
|
+
ROOF_HEEL_MATERIAL_INDEX,
|
|
34
|
+
registerMaterialCacheCleanup,
|
|
35
|
+
tintEmissive,
|
|
36
|
+
useViewer,
|
|
37
|
+
} from '@pascal-app/viewer'
|
|
33
38
|
import { useThree } from '@react-three/fiber'
|
|
34
39
|
import { useCallback, useEffect, useRef } from 'react'
|
|
35
40
|
import { type BufferGeometry, Color, type Material, type Mesh, type Object3D, Vector3 } from 'three'
|
|
@@ -57,7 +62,6 @@ import {
|
|
|
57
62
|
import { selectionEnabled } from '../../lib/interaction/scope'
|
|
58
63
|
import {
|
|
59
64
|
type ActivePaintMaterial,
|
|
60
|
-
buildRoofSurfaceMaterialPatch,
|
|
61
65
|
hasActivePaintMaterial,
|
|
62
66
|
resolveActivePaintMaterialFromSelection,
|
|
63
67
|
} from '../../lib/material-paint'
|
|
@@ -90,7 +94,6 @@ import {
|
|
|
90
94
|
paintScopeRole,
|
|
91
95
|
paintSurfaceLabel,
|
|
92
96
|
resolvePaintScopeTargets,
|
|
93
|
-
slotDisplayLabel,
|
|
94
97
|
type WallPaintHit,
|
|
95
98
|
} from '../../lib/paint-scope'
|
|
96
99
|
import {
|
|
@@ -248,9 +251,10 @@ function resolveStairMaterialTarget(
|
|
|
248
251
|
|
|
249
252
|
function resolveRoofMaterialTarget(
|
|
250
253
|
event: RoofEvent | RoofSegmentEvent,
|
|
251
|
-
): 'top' | 'edge' | 'wall' | null {
|
|
254
|
+
): 'top' | 'edge' | 'wall' | 'heel' | null {
|
|
252
255
|
const materialIndex = getIntersectionMaterialIndex(getEventObject(event), event.faceIndex)
|
|
253
256
|
if (materialIndex === 3) return 'top'
|
|
257
|
+
if (materialIndex === ROOF_HEEL_MATERIAL_INDEX) return 'heel'
|
|
254
258
|
if (materialIndex === 0) return 'edge'
|
|
255
259
|
if (materialIndex === 1 || materialIndex === 2) return 'wall'
|
|
256
260
|
|
|
@@ -451,14 +455,6 @@ function resolveSelectModeNodeTarget(event: NodeEvent, drill = false): AnyNode {
|
|
|
451
455
|
return event.node
|
|
452
456
|
}
|
|
453
457
|
|
|
454
|
-
function previewMeshMaterial(mesh: Mesh, material: Material | Material[]): PaintPreviewCleanup {
|
|
455
|
-
const previousMaterial = mesh.material
|
|
456
|
-
mesh.material = material
|
|
457
|
-
return () => {
|
|
458
|
-
mesh.material = previousMaterial
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
458
|
function previewCursor(cursor: string): PaintPreviewCleanup {
|
|
463
459
|
const previousCursor = document.body.style.cursor
|
|
464
460
|
document.body.style.cursor = cursor
|
|
@@ -467,33 +463,6 @@ function previewCursor(cursor: string): PaintPreviewCleanup {
|
|
|
467
463
|
}
|
|
468
464
|
}
|
|
469
465
|
|
|
470
|
-
function applyRoofPaintPreview(
|
|
471
|
-
node: RoofNode,
|
|
472
|
-
role: 'top' | 'edge' | 'wall',
|
|
473
|
-
material: ActivePaintMaterial,
|
|
474
|
-
): PaintPreviewCleanup | null {
|
|
475
|
-
const root = getRegisteredNodeObject(node.id)
|
|
476
|
-
const mesh = root?.getObjectByName('merged-roof') as Mesh | undefined
|
|
477
|
-
if (!mesh) return null
|
|
478
|
-
|
|
479
|
-
const previewNode = {
|
|
480
|
-
...node,
|
|
481
|
-
...buildRoofSurfaceMaterialPatch(node, role, material.material, material.materialPreset),
|
|
482
|
-
}
|
|
483
|
-
const previewMaterial = getRoofMaterialArray(
|
|
484
|
-
previewNode,
|
|
485
|
-
useViewer.getState().shading,
|
|
486
|
-
useViewer.getState().textures,
|
|
487
|
-
useViewer.getState().colorPreset,
|
|
488
|
-
useViewer.getState().sceneTheme,
|
|
489
|
-
null,
|
|
490
|
-
useScene.getState().materials,
|
|
491
|
-
)
|
|
492
|
-
if (!previewMaterial) return null
|
|
493
|
-
|
|
494
|
-
return previewMeshMaterial(mesh, previewMaterial)
|
|
495
|
-
}
|
|
496
|
-
|
|
497
466
|
// Chimney + dormer paint dispatch lives on their NodeDefinition's
|
|
498
467
|
// `capabilities.paint` (see packages/nodes/src/{chimney,dormer}/
|
|
499
468
|
// paint.ts). The generic registry-driven arm in this file consults
|
|
@@ -518,16 +487,14 @@ const HIGHLIGHT_PROFILES = {
|
|
|
518
487
|
delete: {
|
|
519
488
|
color: new Color('#dc2626'),
|
|
520
489
|
blend: 0.76,
|
|
521
|
-
|
|
522
|
-
emissiveIntensity: 0.46,
|
|
490
|
+
emissive: 0.92,
|
|
523
491
|
},
|
|
524
492
|
selection: {
|
|
525
493
|
// Keep the real material/texture readable: no albedo tint, just a gentle
|
|
526
494
|
// indigo emissive glow so it reads as selected.
|
|
527
495
|
color: new Color('#818cf8'),
|
|
528
496
|
blend: 0,
|
|
529
|
-
|
|
530
|
-
emissiveIntensity: 0.12,
|
|
497
|
+
emissive: 0.12,
|
|
531
498
|
},
|
|
532
499
|
} as const
|
|
533
500
|
|
|
@@ -582,15 +549,7 @@ function createHighlightedMaterial(material: Material, kind: HighlightKind): Mat
|
|
|
582
549
|
highlightedMaterial.color = highlightedMaterial.color.clone().lerp(profile.color, profile.blend)
|
|
583
550
|
}
|
|
584
551
|
|
|
585
|
-
|
|
586
|
-
highlightedMaterial.emissive = highlightedMaterial.emissive
|
|
587
|
-
.clone()
|
|
588
|
-
.lerp(profile.color, profile.emissiveBlend)
|
|
589
|
-
highlightedMaterial.emissiveIntensity = Math.max(
|
|
590
|
-
highlightedMaterial.emissiveIntensity ?? 0,
|
|
591
|
-
profile.emissiveIntensity,
|
|
592
|
-
)
|
|
593
|
-
}
|
|
552
|
+
tintEmissive(highlightedMaterial, profile.color, profile.emissive)
|
|
594
553
|
|
|
595
554
|
if (typeof highlightedMaterial.opacity === 'number' && highlightedMaterial.opacity < 1) {
|
|
596
555
|
highlightedMaterial.transparent = true
|
|
@@ -598,7 +557,7 @@ function createHighlightedMaterial(material: Material, kind: HighlightKind): Mat
|
|
|
598
557
|
}
|
|
599
558
|
|
|
600
559
|
highlightedMaterial.needsUpdate = true
|
|
601
|
-
return highlightedMaterial
|
|
560
|
+
return markHighlight(highlightedMaterial)
|
|
602
561
|
}
|
|
603
562
|
|
|
604
563
|
function createHighlightedMaterials(
|
|
@@ -892,9 +851,7 @@ export const SelectionManager = () => {
|
|
|
892
851
|
|
|
893
852
|
// Registry-driven paint dispatch — kinds that declare
|
|
894
853
|
// `capabilities.paint` route hover / click / preview through
|
|
895
|
-
// their definition
|
|
896
|
-
// roof / stair / single-surface arms below stay until they
|
|
897
|
-
// migrate too.
|
|
854
|
+
// their definition (wall, chimney, dormer, roof, …).
|
|
898
855
|
const paintCap = nodeRegistry.get(node.type)?.capabilities?.paint
|
|
899
856
|
if (paintCap) {
|
|
900
857
|
const materialIndex = getIntersectionMaterialIndex(getEventObject(event), event.faceIndex)
|
|
@@ -1063,51 +1020,7 @@ export const SelectionManager = () => {
|
|
|
1063
1020
|
}
|
|
1064
1021
|
}
|
|
1065
1022
|
|
|
1066
|
-
//
|
|
1067
|
-
// the roof's, and fans to it.
|
|
1068
|
-
if (node.type === 'roof') {
|
|
1069
|
-
const role = resolveRoofMaterialTarget(event as RoofEvent)
|
|
1070
|
-
if (isPaintPicking()) return pickInteraction(node, role, event, null)
|
|
1071
|
-
const compatible = role !== null && paintEnabled
|
|
1072
|
-
return {
|
|
1073
|
-
key: `roof:${node.id}:${role ?? 'unsupported'}:${eraser ? 'erase' : 'paint'}`,
|
|
1074
|
-
hoveredId: node.id as AnyNodeId,
|
|
1075
|
-
hoverMode: compatible ? (eraser ? 'erase-ready' : 'paint-ready') : 'paint-disabled',
|
|
1076
|
-
// Roof isn't on the slot model (role-specific fields, custom commit),
|
|
1077
|
-
// so it offers only the single surface — but still labels it.
|
|
1078
|
-
paintHover:
|
|
1079
|
-
compatible && role
|
|
1080
|
-
? {
|
|
1081
|
-
scopes: ['single'],
|
|
1082
|
-
slotLabel: slotDisplayLabel(node, role),
|
|
1083
|
-
nodeNoun: 'roof',
|
|
1084
|
-
}
|
|
1085
|
-
: null,
|
|
1086
|
-
apply:
|
|
1087
|
-
compatible && role
|
|
1088
|
-
? () => {
|
|
1089
|
-
useScene
|
|
1090
|
-
.getState()
|
|
1091
|
-
.updateNode(
|
|
1092
|
-
node.id as AnyNodeId,
|
|
1093
|
-
buildRoofSurfaceMaterialPatch(
|
|
1094
|
-
node,
|
|
1095
|
-
role,
|
|
1096
|
-
paintSpec.material,
|
|
1097
|
-
paintSpec.materialPreset,
|
|
1098
|
-
),
|
|
1099
|
-
)
|
|
1100
|
-
}
|
|
1101
|
-
: null,
|
|
1102
|
-
preview:
|
|
1103
|
-
compatible && role
|
|
1104
|
-
? () => applyRoofPaintPreview(node, role, paintSpec)
|
|
1105
|
-
: () => previewCursor('not-allowed'),
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
// Only `roof` reaches a legacy paint arm (above) — every
|
|
1110
|
-
// other paintable kind declares `capabilities.paint` and returns from the
|
|
1023
|
+
// Every paintable kind declares `capabilities.paint` and returns from the
|
|
1111
1024
|
// registry-driven dispatch at the top of this function.
|
|
1112
1025
|
|
|
1113
1026
|
const disabledNodeTypes = ['zone']
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { type SeparatorNode, sceneRegistry } from '@pascal-app/core'
|
|
4
|
+
import { type ThreeEvent, useThree } from '@react-three/fiber'
|
|
5
|
+
import { useState } from 'react'
|
|
6
|
+
import { OrthographicCamera, Plane, Vector2, Vector3 } from 'three'
|
|
7
|
+
import { runSeparatorEndDrag } from '../../lib/separator-edit'
|
|
8
|
+
import { suppressBoxSelectForPointer } from '../tools/select/box-select-state'
|
|
9
|
+
import { HandleArrow } from './handles/handle-arrow'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* An invisible wall's 3D end grip, the twin of its end handle on the plan: a
|
|
13
|
+
* cube at the end, on the floor at `height`. Dragging it moves the end across
|
|
14
|
+
* that floor (`runSeparatorEndDrag`), the invisible walls joined there
|
|
15
|
+
* following. Rendered in the level frame.
|
|
16
|
+
*/
|
|
17
|
+
export function SeparatorEndGrip({
|
|
18
|
+
separator,
|
|
19
|
+
endpoint,
|
|
20
|
+
height,
|
|
21
|
+
levelId,
|
|
22
|
+
}: {
|
|
23
|
+
separator: SeparatorNode
|
|
24
|
+
endpoint: 'start' | 'end'
|
|
25
|
+
height: number
|
|
26
|
+
levelId: string
|
|
27
|
+
}) {
|
|
28
|
+
const [hover, setHover] = useState(false)
|
|
29
|
+
const { camera, raycaster, gl } = useThree()
|
|
30
|
+
const zoom = camera instanceof OrthographicCamera ? 1 / camera.zoom : 1
|
|
31
|
+
const [x, z] = separator[endpoint]
|
|
32
|
+
const onPointerDown = (event: ThreeEvent<PointerEvent>) => {
|
|
33
|
+
if (event.button !== 0) return
|
|
34
|
+
event.stopPropagation()
|
|
35
|
+
suppressBoxSelectForPointer(event)
|
|
36
|
+
const level = sceneRegistry.nodes.get(levelId)
|
|
37
|
+
if (!level) return
|
|
38
|
+
level.updateWorldMatrix(true, false)
|
|
39
|
+
const toLocal = level.matrixWorld.clone().invert()
|
|
40
|
+
const floor = new Plane().setFromNormalAndCoplanarPoint(
|
|
41
|
+
new Vector3(0, 1, 0).transformDirection(level.matrixWorld),
|
|
42
|
+
new Vector3(x, height, z).applyMatrix4(level.matrixWorld),
|
|
43
|
+
)
|
|
44
|
+
runSeparatorEndDrag({
|
|
45
|
+
separatorId: separator.id,
|
|
46
|
+
endpoint,
|
|
47
|
+
levelId,
|
|
48
|
+
toPlan: (clientX, clientY) => {
|
|
49
|
+
const rect = gl.domElement.getBoundingClientRect()
|
|
50
|
+
raycaster.setFromCamera(
|
|
51
|
+
new Vector2(
|
|
52
|
+
((clientX - rect.left) / rect.width) * 2 - 1,
|
|
53
|
+
-((clientY - rect.top) / rect.height) * 2 + 1,
|
|
54
|
+
),
|
|
55
|
+
camera,
|
|
56
|
+
)
|
|
57
|
+
const hit = raycaster.ray.intersectPlane(floor, new Vector3())
|
|
58
|
+
if (!hit) return null
|
|
59
|
+
const local = hit.applyMatrix4(toLocal)
|
|
60
|
+
return [local.x, local.z]
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
return (
|
|
65
|
+
<HandleArrow
|
|
66
|
+
cursor="grab"
|
|
67
|
+
hover={hover}
|
|
68
|
+
onHoverChange={setHover}
|
|
69
|
+
onPointerDown={onPointerDown}
|
|
70
|
+
placement={{ position: [x, height, z], baseScale: zoom }}
|
|
71
|
+
shape="tracker"
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import type { SiteNode } from '@pascal-app/core'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
formatLinearMeasurement,
|
|
6
|
+
sceneRegistry,
|
|
7
|
+
useLiveNodeOverrides,
|
|
8
|
+
useScene,
|
|
9
|
+
} from '@pascal-app/core'
|
|
5
10
|
import { getSceneTheme, useViewer } from '@pascal-app/viewer'
|
|
6
|
-
import { Html } from '@react-three/drei'
|
|
7
11
|
import { createPortal, useFrame, useThree } from '@react-three/fiber'
|
|
8
12
|
import { useCallback, useMemo, useRef, useState } from 'react'
|
|
9
13
|
import { type Camera, type Object3D, Vector3 } from 'three'
|
|
10
14
|
import { groundHeightAt } from '../../lib/ground-surface'
|
|
11
|
-
import { formatLinearMeasurement } from '../../lib/measurements'
|
|
12
15
|
import { SITE_BOUNDARY_DRAG_LABEL } from '../../lib/site-boundary'
|
|
13
16
|
import { useActiveHandleDrag } from '../../store/use-interaction-scope'
|
|
17
|
+
import { Html } from './localized-html'
|
|
14
18
|
|
|
15
19
|
type ViewportSize = {
|
|
16
20
|
width: number
|
|
@@ -205,31 +205,38 @@ describe('a sheet capture session', () => {
|
|
|
205
205
|
const viewer = useViewer.getState()
|
|
206
206
|
viewer.setWallMode('down')
|
|
207
207
|
viewer.setLevelMode('exploded')
|
|
208
|
-
viewer.
|
|
208
|
+
viewer.setRenderQuality('low')
|
|
209
209
|
viewer.setTextures(false)
|
|
210
210
|
viewer.setColorPreset('white')
|
|
211
|
+
viewer.setTimeOfDay('night')
|
|
212
|
+
viewer.setWeather('overcast')
|
|
211
213
|
const view = () => {
|
|
212
|
-
const { wallMode, levelMode,
|
|
213
|
-
|
|
214
|
+
const { wallMode, levelMode, renderQuality, textures, colorPreset, timeOfDay, weather } =
|
|
215
|
+
useViewer.getState()
|
|
216
|
+
return { wallMode, levelMode, renderQuality, textures, colorPreset, timeOfDay, weather }
|
|
214
217
|
}
|
|
215
|
-
// the
|
|
218
|
+
// the quality stays the user's: the capture brings its own AO
|
|
216
219
|
|
|
217
220
|
const restore = presentFinished()
|
|
218
221
|
expect(view()).toEqual({
|
|
219
222
|
wallMode: 'up',
|
|
220
223
|
levelMode: 'stacked',
|
|
221
|
-
|
|
224
|
+
renderQuality: 'low',
|
|
222
225
|
textures: true,
|
|
223
226
|
colorPreset: 'clay',
|
|
227
|
+
timeOfDay: 'midday',
|
|
228
|
+
weather: 'fair',
|
|
224
229
|
})
|
|
225
230
|
|
|
226
231
|
restore()
|
|
227
232
|
expect(view()).toEqual({
|
|
228
233
|
wallMode: 'down',
|
|
229
234
|
levelMode: 'exploded',
|
|
230
|
-
|
|
235
|
+
renderQuality: 'low',
|
|
231
236
|
textures: false,
|
|
232
237
|
colorPreset: 'white',
|
|
238
|
+
timeOfDay: 'night',
|
|
239
|
+
weather: 'overcast',
|
|
233
240
|
})
|
|
234
241
|
})
|
|
235
242
|
|
|
@@ -239,6 +246,8 @@ describe('a sheet capture session', () => {
|
|
|
239
246
|
viewer.setLevelMode('stacked')
|
|
240
247
|
viewer.setTextures(true)
|
|
241
248
|
viewer.setColorPreset('clay')
|
|
249
|
+
viewer.setTimeOfDay('midday')
|
|
250
|
+
viewer.setWeather('fair')
|
|
242
251
|
let writes = 0
|
|
243
252
|
const unsubscribe = useViewer.subscribe(() => {
|
|
244
253
|
writes++
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
GRID_LAYER,
|
|
17
17
|
heroCameraPose,
|
|
18
18
|
holdLiveFrame,
|
|
19
|
+
itemLightsForCapture,
|
|
19
20
|
pendingSceneBuildCount,
|
|
20
21
|
presentLevelsForCapture,
|
|
21
22
|
refreshIsolation,
|
|
@@ -437,13 +438,15 @@ const HOLD_MAX_MS = 300_000
|
|
|
437
438
|
* What the sheets' pictures are drawn in, whatever the user's view: full-
|
|
438
439
|
* height walls, the storeys stacked at their true heights, the materials'
|
|
439
440
|
* own textures and colours (a white or blueprint palette is a view, not the
|
|
440
|
-
* house). The shading stays the user's: the capture brings its own AO.
|
|
441
|
+
* house), lit by a fair midday. The shading stays the user's: the capture brings its own AO.
|
|
441
442
|
*/
|
|
442
443
|
const FINISHED_PRESENTATION = {
|
|
443
444
|
wallMode: 'up',
|
|
444
445
|
levelMode: 'stacked',
|
|
445
446
|
textures: true,
|
|
446
447
|
colorPreset: 'clay',
|
|
448
|
+
timeOfDay: 'midday',
|
|
449
|
+
weather: 'fair',
|
|
447
450
|
} as const
|
|
448
451
|
|
|
449
452
|
/** The viewer switched to FINISHED_PRESENTATION; the returned function puts the user's view back. */
|
|
@@ -470,6 +473,25 @@ export function presentFinished(): () => void {
|
|
|
470
473
|
viewer.setColorPreset(FINISHED_PRESENTATION.colorPreset)
|
|
471
474
|
restores.push(() => useViewer.getState().setColorPreset(colorPreset))
|
|
472
475
|
}
|
|
476
|
+
if (
|
|
477
|
+
viewer.timeOfDay !== FINISHED_PRESENTATION.timeOfDay ||
|
|
478
|
+
viewer.timeOfDayMinutes !== null ||
|
|
479
|
+
viewer.skyLive
|
|
480
|
+
) {
|
|
481
|
+
const { timeOfDay, timeOfDayMinutes, skyLive } = viewer
|
|
482
|
+
viewer.setTimeOfDay(FINISHED_PRESENTATION.timeOfDay)
|
|
483
|
+
restores.push(() => {
|
|
484
|
+
const next = useViewer.getState()
|
|
485
|
+
next.setTimeOfDay(timeOfDay)
|
|
486
|
+
if (timeOfDayMinutes !== null) next.setTimeOfDayMinutes(timeOfDayMinutes)
|
|
487
|
+
if (skyLive) next.setSkyLive(true)
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
if (viewer.weather !== FINISHED_PRESENTATION.weather) {
|
|
491
|
+
const weather = viewer.weather
|
|
492
|
+
viewer.setWeather(FINISHED_PRESENTATION.weather)
|
|
493
|
+
restores.push(() => useViewer.getState().setWeather(weather))
|
|
494
|
+
}
|
|
473
495
|
return () => {
|
|
474
496
|
for (const restore of restores.reverse()) restore()
|
|
475
497
|
}
|
|
@@ -995,6 +1017,9 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
|
|
|
995
1017
|
} else if (snapLevels || pose || cameraPose) {
|
|
996
1018
|
exteriorGroundForCapture(restore, Boolean(cameraPose || pose), thumbnailCamera)
|
|
997
1019
|
}
|
|
1020
|
+
// Last, with the camera where the picture is taken from: the item lights that
|
|
1021
|
+
// matter to it, at full strength.
|
|
1022
|
+
restore(itemLightsForCapture(scene, thumbnailCamera))
|
|
998
1023
|
}
|
|
999
1024
|
// The scene pass is a FRAME-updated node: it renders once per node
|
|
1000
1025
|
// frame, and only the animation loop advances that frame. With the
|