@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
|
@@ -59,7 +59,11 @@ import {
|
|
|
59
59
|
useRoomRecords,
|
|
60
60
|
} from '../../../hooks/use-selected-room'
|
|
61
61
|
import { useCeilingEditSession } from '../../../lib/ceiling-edit-session'
|
|
62
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
AXIS_MOVE_DRAG_LABEL,
|
|
64
|
+
PATH_POINT_DRAG_LABEL,
|
|
65
|
+
ROTATE_HANDLE_DRAG_LABEL,
|
|
66
|
+
} from '../../../lib/contextual-help'
|
|
63
67
|
import {
|
|
64
68
|
canDirectRotateNode,
|
|
65
69
|
resolveDirectManipulationNode,
|
|
@@ -78,7 +82,7 @@ import { resolveNodeForDrawingType } from '../../../lib/floorplan/drawing-coordi
|
|
|
78
82
|
import {
|
|
79
83
|
createFloorplanContextExtensions,
|
|
80
84
|
type FloorplanAnnotationRole,
|
|
81
|
-
type
|
|
85
|
+
type FloorplanWallDimensionFaces,
|
|
82
86
|
getFloorplanNodeExtension,
|
|
83
87
|
readFloorplanGeometryMetadata,
|
|
84
88
|
withFloorplanGeometryMetadata,
|
|
@@ -86,7 +90,6 @@ import {
|
|
|
86
90
|
import {
|
|
87
91
|
type FloorplanMode,
|
|
88
92
|
resolveFloorplanAnnotationVisibility,
|
|
89
|
-
resolveFloorplanWallDimensionReference,
|
|
90
93
|
} from '../../../lib/floorplan/floorplan-mode'
|
|
91
94
|
import {
|
|
92
95
|
buildFloorplanContext,
|
|
@@ -115,6 +118,11 @@ import { createSessionWrites, type SessionWrites } from '../../../lib/session-wr
|
|
|
115
118
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
|
116
119
|
import { clearSurfacePlanSnapFeedback } from '../../../lib/surface-plan-snap'
|
|
117
120
|
import { paintZoneMembership } from '../../../lib/units'
|
|
121
|
+
import {
|
|
122
|
+
startWallPull,
|
|
123
|
+
WALL_PULL_AFFORDANCE,
|
|
124
|
+
type WallPullGripPayload,
|
|
125
|
+
} from '../../../lib/wall-pull'
|
|
118
126
|
import useDirectManipulationFeedback from '../../../store/use-direct-manipulation-feedback'
|
|
119
127
|
import useDrawingView from '../../../store/use-drawing-view'
|
|
120
128
|
import useEditor, { isAngleSnapActive, type Mode } from '../../../store/use-editor'
|
|
@@ -130,7 +138,12 @@ import { isViewVisible, VIEW_2D } from '../../../store/view-layout'
|
|
|
130
138
|
import { FloorRegionControls2D } from '../../editor/floor-region-controls'
|
|
131
139
|
import { startGroupPickUp } from '../../editor/group-actions'
|
|
132
140
|
import { classifyParticipant } from '../../editor/group-transform-shared'
|
|
133
|
-
import {
|
|
141
|
+
import {
|
|
142
|
+
RoomHandleDragPreview2D,
|
|
143
|
+
RoomHandles2D,
|
|
144
|
+
SelectedWallDimensions2D,
|
|
145
|
+
useSelectionDimensionNodes,
|
|
146
|
+
} from '../../editor/room-handles-2d'
|
|
134
147
|
import { RoomHighlight2D } from '../../editor/room-highlight'
|
|
135
148
|
import { RoomLabels2D } from '../../editor/room-labels'
|
|
136
149
|
import { suppressBoxSelectForPointer } from '../../tools/select/box-select-state'
|
|
@@ -150,7 +163,10 @@ import {
|
|
|
150
163
|
resolveSvgAnnotationCollisions,
|
|
151
164
|
svgAnnotationLabelId,
|
|
152
165
|
} from './floorplan-annotation-layout'
|
|
153
|
-
import {
|
|
166
|
+
import {
|
|
167
|
+
FloorplanDimensionEditOverlay,
|
|
168
|
+
typeableDimensionAt,
|
|
169
|
+
} from './floorplan-dimension-edit-overlay'
|
|
154
170
|
import { FloorplanDimensionRenderer } from './floorplan-dimension-renderer'
|
|
155
171
|
import { FloorplanGeometryRenderer } from './floorplan-geometry-renderer'
|
|
156
172
|
import {
|
|
@@ -199,6 +215,15 @@ const DIRECT_DRAG_THRESHOLD_PX = 4
|
|
|
199
215
|
const DIRECT_ROTATE_EPSILON = 1e-6
|
|
200
216
|
const DIRECT_ROTATE_RADIANS_PER_PIXEL = Math.PI / 180
|
|
201
217
|
const MAX_HANDLE_UNITS_PER_PIXEL = 0.015
|
|
218
|
+
// A wall end's pull grip, in screen pixels out from the end along the wall:
|
|
219
|
+
// clear of the end's move grip (its hit ring reaches ~17 px), a short dashed
|
|
220
|
+
// leader, then a round button with an outward arrow.
|
|
221
|
+
const PULL_GRIP_LEADER_START_PX = 9
|
|
222
|
+
const PULL_GRIP_CENTER_PX = 30
|
|
223
|
+
const PULL_GRIP_RADIUS_PX = 10
|
|
224
|
+
const PULL_GRIP_HIT_RADIUS_PX = 13
|
|
225
|
+
const PULL_GRIP_FILL = '#10b981' // emerald-500
|
|
226
|
+
const PULL_GRIP_HOVER_FILL = '#059669' // emerald-600
|
|
202
227
|
|
|
203
228
|
export function resolveFloorplanHandleUnitsPerPixel(unitsPerPixel: number): number {
|
|
204
229
|
return Math.min(unitsPerPixel, MAX_HANDLE_UNITS_PER_PIXEL)
|
|
@@ -300,7 +325,7 @@ export function subscribeFloorplanAffordanceToolCancel(
|
|
|
300
325
|
// edits that set a direction/shape map to a scope; resize / rotate / body-move
|
|
301
326
|
// affordances return `null` (no polygon/wall snapping chip). Keyed off the
|
|
302
327
|
// affordance name the kinds register (`move-vertex` / `move-edge` / `add-vertex`
|
|
303
|
-
// / `curve` / `move-endpoint`).
|
|
328
|
+
// / `curve` / `move-endpoint` / `move-path-point` / `move-segment`).
|
|
304
329
|
export function floorplanAffordanceReshapeScope(
|
|
305
330
|
affordance: string,
|
|
306
331
|
nodeId: string,
|
|
@@ -341,6 +366,14 @@ export function floorplanAffordanceReshapeScope(
|
|
|
341
366
|
if (affordance.includes('rotate')) {
|
|
342
367
|
return { kind: 'handle-drag', nodeId, handle: ROTATE_HANDLE_DRAG_LABEL }
|
|
343
368
|
}
|
|
369
|
+
// MEP run point and run segment drags: the same handle-drag scopes as their
|
|
370
|
+
// 3D arrows, so they snap in the kind's own context (polygon for runs).
|
|
371
|
+
if (affordance === 'move-path-point') {
|
|
372
|
+
return { kind: 'handle-drag', nodeId, handle: PATH_POINT_DRAG_LABEL }
|
|
373
|
+
}
|
|
374
|
+
if (affordance === 'move-segment') {
|
|
375
|
+
return { kind: 'handle-drag', nodeId, handle: AXIS_MOVE_DRAG_LABEL }
|
|
376
|
+
}
|
|
344
377
|
return null
|
|
345
378
|
}
|
|
346
379
|
|
|
@@ -387,7 +420,7 @@ type NodeDeps = {
|
|
|
387
420
|
live: LiveTransform | undefined
|
|
388
421
|
unit: 'metric' | 'imperial'
|
|
389
422
|
metricNotation: 'meters' | 'millimeters'
|
|
390
|
-
|
|
423
|
+
wallDimensionFaces?: FloorplanWallDimensionFaces
|
|
391
424
|
selected: boolean
|
|
392
425
|
highlighted: boolean
|
|
393
426
|
hovered: boolean
|
|
@@ -497,6 +530,20 @@ export function floorplanEntryYieldsToTool(state: {
|
|
|
497
530
|
return state.mode === 'build' || state.openingPlacement
|
|
498
531
|
}
|
|
499
532
|
|
|
533
|
+
/**
|
|
534
|
+
* Whether an entry draws its node without edit handles or dimension chrome: a member of a
|
|
535
|
+
* multi-selection, a ceiling outside its edit session, or anything while a build tool is armed (a
|
|
536
|
+
* selected roof keeps its selection for a new plane to join it, but a press near its eave must
|
|
537
|
+
* draw, not resize it).
|
|
538
|
+
*/
|
|
539
|
+
export function floorplanEntryStripsHandles(state: {
|
|
540
|
+
mode: Mode
|
|
541
|
+
multiSelected: boolean
|
|
542
|
+
ceilingWithoutSession: boolean
|
|
543
|
+
}): boolean {
|
|
544
|
+
return state.multiSelected || state.ceilingWithoutSession || state.mode === 'build'
|
|
545
|
+
}
|
|
546
|
+
|
|
500
547
|
function floorplanEntryYieldsToToolNow(): boolean {
|
|
501
548
|
const { mode, tool } = useEditor.getState()
|
|
502
549
|
const movingNode = getMovingNode()
|
|
@@ -596,6 +643,11 @@ export const FloorplanRegistryLayer = memo(function FloorplanRegistryLayer() {
|
|
|
596
643
|
// `handleClickStop` / `handleSelect`, so the tool never sees it. Pass
|
|
597
644
|
// clicks through in that case (`floorplanEntryYieldsToToolNow`).
|
|
598
645
|
const editorMode = useEditor((s) => s.mode)
|
|
646
|
+
const selectionDimensionNodes = useSelectionDimensionNodes()
|
|
647
|
+
const selectionDimensionIds = useMemo(
|
|
648
|
+
() => new Set(selectionDimensionNodes.map((node) => node.id)),
|
|
649
|
+
[selectionDimensionNodes],
|
|
650
|
+
)
|
|
599
651
|
const floorplanSelectionTool = useEditor((s) => s.floorplanSelectionTool)
|
|
600
652
|
const endpointReshape = useEndpointReshape()
|
|
601
653
|
const isMarqueeSelectionActive =
|
|
@@ -609,12 +661,9 @@ export const FloorplanRegistryLayer = memo(function FloorplanRegistryLayer() {
|
|
|
609
661
|
const floorplanVisible = useEditor((s) => isViewVisible(s, VIEW_2D))
|
|
610
662
|
const drawingType = useDrawingView((s) => s.drawingType)
|
|
611
663
|
const annotationVisibility = useFloorplanAnnotationVisibility((s) => s.visibility)
|
|
612
|
-
const
|
|
664
|
+
const showDimensions = useFloorplanAnnotationVisibility((s) => s.showDimensions)
|
|
665
|
+
const wallDimensionFaces = useFloorplanAnnotationVisibility((s) => s.wallDimensionFaces)
|
|
613
666
|
const floorplanMode = useFloorplanMode((s) => s.mode)
|
|
614
|
-
const effectiveWallDimensionReference = resolveFloorplanWallDimensionReference(
|
|
615
|
-
floorplanMode,
|
|
616
|
-
wallDimensionReference,
|
|
617
|
-
)
|
|
618
667
|
// Elevator builders read runtime state imperatively, so entries include this
|
|
619
668
|
// rare-changing ref in their cache deps.
|
|
620
669
|
const interactiveElevators = useInteractive((s) => s.elevators)
|
|
@@ -981,6 +1030,12 @@ export const FloorplanRegistryLayer = memo(function FloorplanRegistryLayer() {
|
|
|
981
1030
|
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return false
|
|
982
1031
|
if (movingNode) return false
|
|
983
1032
|
if (useEditor.getState().mode === 'delete') return false
|
|
1033
|
+
// A press on a member's dimension value is the type-a-value box's: its click opens it.
|
|
1034
|
+
if (!immediate && typeableDimensionAt(event.target, event.clientX, event.clientY)) {
|
|
1035
|
+
event.stopPropagation()
|
|
1036
|
+
suppressBoxSelectForPointer(event)
|
|
1037
|
+
return true
|
|
1038
|
+
}
|
|
984
1039
|
const started = startFloorplanGroupMove(id, event, {
|
|
985
1040
|
immediate,
|
|
986
1041
|
onClickFallthrough: immediate ? undefined : () => startGroupPickUp(),
|
|
@@ -1299,6 +1354,16 @@ export const FloorplanRegistryLayer = memo(function FloorplanRegistryLayer() {
|
|
|
1299
1354
|
runFloorplanWallPush(payload, event.clientX, event.clientY)
|
|
1300
1355
|
return
|
|
1301
1356
|
}
|
|
1357
|
+
// A wall end's pull grip opens the Walls run at that end; the plan's
|
|
1358
|
+
// wall draft then follows the pointer and the release places the wall.
|
|
1359
|
+
if (affordance === WALL_PULL_AFFORDANCE) {
|
|
1360
|
+
event.preventDefault()
|
|
1361
|
+
event.stopPropagation()
|
|
1362
|
+
suppressBoxSelectForPointer(event)
|
|
1363
|
+
const grip = payload as WallPullGripPayload
|
|
1364
|
+
startWallPull({ wallId: grip.wallId, endpoint: grip.endpoint, origin: '2d' })
|
|
1365
|
+
return
|
|
1366
|
+
}
|
|
1302
1367
|
|
|
1303
1368
|
const sceneNodes = useScene.getState().nodes
|
|
1304
1369
|
const node = sceneNodes[nodeId]
|
|
@@ -1590,6 +1655,7 @@ export const FloorplanRegistryLayer = memo(function FloorplanRegistryLayer() {
|
|
|
1590
1655
|
<FloorplanRegistryEntry
|
|
1591
1656
|
activeDragId={handleIdForNode(activeDragId, entry.id)}
|
|
1592
1657
|
annotationVisibility={annotationVisibility}
|
|
1658
|
+
showDimensions={showDimensions}
|
|
1593
1659
|
floorplanMode={floorplanMode}
|
|
1594
1660
|
floorplanVisible={floorplanVisible}
|
|
1595
1661
|
geometryCacheRef={geometryCacheRef}
|
|
@@ -1597,6 +1663,7 @@ export const FloorplanRegistryLayer = memo(function FloorplanRegistryLayer() {
|
|
|
1597
1663
|
hoveredHandleId={handleIdForNode(hoveredHandleId, entry.id)}
|
|
1598
1664
|
interactiveElevators={interactiveElevators}
|
|
1599
1665
|
isMarqueeSelectionActive={isMarqueeSelectionActive}
|
|
1666
|
+
keepsSelectionDimensions={selectionDimensionIds.has(entry.id)}
|
|
1600
1667
|
key={`${pass}-${entry.id}`}
|
|
1601
1668
|
levelDataCacheRef={levelDataCacheRef}
|
|
1602
1669
|
levelNodeIdsByType={floorplanData.levelNodeIdsByType}
|
|
@@ -1619,7 +1686,7 @@ export const FloorplanRegistryLayer = memo(function FloorplanRegistryLayer() {
|
|
|
1619
1686
|
siblingEpoch={entry.dependsOnSiblingInputs ? (siblingEpochs.get(entry.id) ?? 0) : 0}
|
|
1620
1687
|
unit={unit}
|
|
1621
1688
|
metricNotation={metricNotation}
|
|
1622
|
-
|
|
1689
|
+
wallDimensionFaces={wallDimensionFaces}
|
|
1623
1690
|
visibilityRootId={
|
|
1624
1691
|
entry.visibilityRootId ?? (entry.ctxOverrides ? undefined : (levelId as AnyNodeId))
|
|
1625
1692
|
}
|
|
@@ -1669,6 +1736,7 @@ export const FloorplanRegistryLayer = memo(function FloorplanRegistryLayer() {
|
|
|
1669
1736
|
<g className="floorplan-registry-handles">{renderEntries('handles')}</g>
|
|
1670
1737
|
<RoomHandles2D levelId={levelId} />
|
|
1671
1738
|
<RoomHandleDragPreview2D levelId={levelId} />
|
|
1739
|
+
<SelectedWallDimensions2D levelId={levelId} />
|
|
1672
1740
|
<FloorplanAnnotationLayoutResolver active={floorplanVisible} />
|
|
1673
1741
|
{/* Click-to-type dimension labels. Portals its HTML input to
|
|
1674
1742
|
document.body, so mounting it inside the SVG tree is safe. */}
|
|
@@ -1707,9 +1775,8 @@ function FloorplanAnnotationLayoutResolver({ active }: { active: boolean }) {
|
|
|
1707
1775
|
const drawingType = useDrawingView((state) => state.drawingType)
|
|
1708
1776
|
const annotationLayoutOverrides = useDrawingView((state) => state.annotationLayoutOverrides)
|
|
1709
1777
|
const annotationVisibility = useFloorplanAnnotationVisibility((state) => state.visibility)
|
|
1710
|
-
const
|
|
1711
|
-
|
|
1712
|
-
)
|
|
1778
|
+
const showDimensions = useFloorplanAnnotationVisibility((state) => state.showDimensions)
|
|
1779
|
+
const wallDimensionFaces = useFloorplanAnnotationVisibility((state) => state.wallDimensionFaces)
|
|
1713
1780
|
const floorplanMode = useFloorplanMode((state) => state.mode)
|
|
1714
1781
|
const layoutInputs = useMemo(
|
|
1715
1782
|
() => ({
|
|
@@ -1718,7 +1785,8 @@ function FloorplanAnnotationLayoutResolver({ active }: { active: boolean }) {
|
|
|
1718
1785
|
drawingType,
|
|
1719
1786
|
interactionIdle,
|
|
1720
1787
|
settledLayoutEpoch,
|
|
1721
|
-
|
|
1788
|
+
showDimensions,
|
|
1789
|
+
wallDimensionFaces,
|
|
1722
1790
|
floorplanMode,
|
|
1723
1791
|
}),
|
|
1724
1792
|
[
|
|
@@ -1727,7 +1795,8 @@ function FloorplanAnnotationLayoutResolver({ active }: { active: boolean }) {
|
|
|
1727
1795
|
drawingType,
|
|
1728
1796
|
interactionIdle,
|
|
1729
1797
|
settledLayoutEpoch,
|
|
1730
|
-
|
|
1798
|
+
showDimensions,
|
|
1799
|
+
wallDimensionFaces,
|
|
1731
1800
|
floorplanMode,
|
|
1732
1801
|
],
|
|
1733
1802
|
)
|
|
@@ -1955,6 +2024,7 @@ function readFloorplanAnnotationLayoutOffset(label: SVGGElement) {
|
|
|
1955
2024
|
type FloorplanRegistryEntryProps = {
|
|
1956
2025
|
activeDragId: string | null
|
|
1957
2026
|
annotationVisibility: FloorplanAnnotationVisibility
|
|
2027
|
+
showDimensions: boolean
|
|
1958
2028
|
floorplanMode: FloorplanMode
|
|
1959
2029
|
ctxOverrides: FloorplanContextOverrides | undefined
|
|
1960
2030
|
floorplanVisible: boolean
|
|
@@ -1963,6 +2033,8 @@ type FloorplanRegistryEntryProps = {
|
|
|
1963
2033
|
hoveredHandleId: string | null
|
|
1964
2034
|
interactiveElevators: unknown
|
|
1965
2035
|
isMarqueeSelectionActive: boolean
|
|
2036
|
+
/** A wall in a multi-selection still shows its own length (`useSelectionDimensionNodes`). */
|
|
2037
|
+
keepsSelectionDimensions: boolean
|
|
1966
2038
|
levelDataCacheRef: { current: Map<string, LevelDataCacheEntry> }
|
|
1967
2039
|
levelNodeIdsByType: ReadonlyMap<string, readonly AnyNodeId[]>
|
|
1968
2040
|
moving: boolean
|
|
@@ -1996,13 +2068,14 @@ type FloorplanRegistryEntryProps = {
|
|
|
1996
2068
|
siblingEpoch: number
|
|
1997
2069
|
unit: 'metric' | 'imperial'
|
|
1998
2070
|
metricNotation: 'meters' | 'millimeters'
|
|
1999
|
-
|
|
2071
|
+
wallDimensionFaces: FloorplanWallDimensionFaces
|
|
2000
2072
|
visibilityRootId: AnyNodeId | undefined
|
|
2001
2073
|
}
|
|
2002
2074
|
|
|
2003
2075
|
const FloorplanRegistryEntry = memo(function FloorplanRegistryEntry({
|
|
2004
2076
|
activeDragId,
|
|
2005
2077
|
annotationVisibility,
|
|
2078
|
+
showDimensions,
|
|
2006
2079
|
floorplanMode,
|
|
2007
2080
|
ctxOverrides,
|
|
2008
2081
|
floorplanVisible,
|
|
@@ -2011,6 +2084,7 @@ const FloorplanRegistryEntry = memo(function FloorplanRegistryEntry({
|
|
|
2011
2084
|
hoveredHandleId,
|
|
2012
2085
|
interactiveElevators,
|
|
2013
2086
|
isMarqueeSelectionActive,
|
|
2087
|
+
keepsSelectionDimensions,
|
|
2014
2088
|
levelDataCacheRef,
|
|
2015
2089
|
levelNodeIdsByType,
|
|
2016
2090
|
moving,
|
|
@@ -2032,7 +2106,7 @@ const FloorplanRegistryEntry = memo(function FloorplanRegistryEntry({
|
|
|
2032
2106
|
siblingEpoch,
|
|
2033
2107
|
unit,
|
|
2034
2108
|
metricNotation,
|
|
2035
|
-
|
|
2109
|
+
wallDimensionFaces,
|
|
2036
2110
|
visibilityRootId,
|
|
2037
2111
|
}: FloorplanRegistryEntryProps): React.ReactElement | null {
|
|
2038
2112
|
// A node an agent just built is drawn on its construction-reveal turn, as in 3D.
|
|
@@ -2049,7 +2123,12 @@ const FloorplanRegistryEntry = memo(function FloorplanRegistryEntry({
|
|
|
2049
2123
|
const ceilingWithoutSession = useCeilingEditSession(
|
|
2050
2124
|
(state) => node.type === 'ceiling' && state.session?.ceilingId !== nodeId,
|
|
2051
2125
|
)
|
|
2052
|
-
const
|
|
2126
|
+
const mode = useEditor((state) => state.mode)
|
|
2127
|
+
const suppressHandles = floorplanEntryStripsHandles({
|
|
2128
|
+
mode,
|
|
2129
|
+
multiSelected,
|
|
2130
|
+
ceilingWithoutSession,
|
|
2131
|
+
})
|
|
2053
2132
|
const selectedLevelId = useViewer((state) => state.selection.levelId)
|
|
2054
2133
|
const selectionProxyId = resolveSelectionProxyId(
|
|
2055
2134
|
node,
|
|
@@ -2089,6 +2168,7 @@ const FloorplanRegistryEntry = memo(function FloorplanRegistryEntry({
|
|
|
2089
2168
|
referencedAnnotationRole,
|
|
2090
2169
|
selected,
|
|
2091
2170
|
target: 'editor',
|
|
2171
|
+
dimensions: showDimensions,
|
|
2092
2172
|
},
|
|
2093
2173
|
)
|
|
2094
2174
|
|
|
@@ -2220,7 +2300,7 @@ const FloorplanRegistryEntry = memo(function FloorplanRegistryEntry({
|
|
|
2220
2300
|
siblingEpoch,
|
|
2221
2301
|
unit,
|
|
2222
2302
|
metricNotation,
|
|
2223
|
-
|
|
2303
|
+
wallDimensionFaces,
|
|
2224
2304
|
visibilityRootId,
|
|
2225
2305
|
})
|
|
2226
2306
|
const rawGeometry = cacheEntry ? cacheEntry[pass] : null
|
|
@@ -2229,10 +2309,10 @@ const FloorplanRegistryEntry = memo(function FloorplanRegistryEntry({
|
|
|
2229
2309
|
: null
|
|
2230
2310
|
// Multi-selection shows highlight only: strip this member's edit handles /
|
|
2231
2311
|
// dimension chrome (all of which live in the overlay pass) while keeping
|
|
2232
|
-
// its highlighted body geometry.
|
|
2312
|
+
// its highlighted body geometry and, for a wall, its own length.
|
|
2233
2313
|
const geometry =
|
|
2234
2314
|
visibleGeometry && suppressHandles && pass !== 'base'
|
|
2235
|
-
? stripHandleChrome(visibleGeometry)
|
|
2315
|
+
? stripHandleChrome(visibleGeometry, keepsSelectionDimensions)
|
|
2236
2316
|
: visibleGeometry
|
|
2237
2317
|
useFloorplanReveal(nodeId, entryRef, cacheEntry, revealWaiting, revealActive)
|
|
2238
2318
|
if (revealWaiting || !geometry) return null
|
|
@@ -2293,7 +2373,7 @@ type BuildFloorplanEntryGeometryArgs = {
|
|
|
2293
2373
|
siblingEpoch: number
|
|
2294
2374
|
unit: 'metric' | 'imperial'
|
|
2295
2375
|
metricNotation: 'meters' | 'millimeters'
|
|
2296
|
-
|
|
2376
|
+
wallDimensionFaces?: FloorplanWallDimensionFaces
|
|
2297
2377
|
visibilityRootId: AnyNodeId | undefined
|
|
2298
2378
|
}
|
|
2299
2379
|
|
|
@@ -2353,7 +2433,7 @@ export function buildFloorplanEntryGeometry({
|
|
|
2353
2433
|
siblingEpoch,
|
|
2354
2434
|
unit,
|
|
2355
2435
|
metricNotation,
|
|
2356
|
-
|
|
2436
|
+
wallDimensionFaces,
|
|
2357
2437
|
visibilityRootId,
|
|
2358
2438
|
}: BuildFloorplanEntryGeometryArgs): CacheEntry | null {
|
|
2359
2439
|
const def = nodeRegistry.get(node.type)
|
|
@@ -2381,7 +2461,7 @@ export function buildFloorplanEntryGeometry({
|
|
|
2381
2461
|
live,
|
|
2382
2462
|
unit,
|
|
2383
2463
|
metricNotation,
|
|
2384
|
-
|
|
2464
|
+
wallDimensionFaces,
|
|
2385
2465
|
selected,
|
|
2386
2466
|
highlighted,
|
|
2387
2467
|
hovered,
|
|
@@ -2468,7 +2548,7 @@ export function buildFloorplanEntryGeometry({
|
|
|
2468
2548
|
unit,
|
|
2469
2549
|
metricNotation,
|
|
2470
2550
|
roomLabelOverlay: true,
|
|
2471
|
-
|
|
2551
|
+
wallDimensionFaces,
|
|
2472
2552
|
highlighted,
|
|
2473
2553
|
hovered,
|
|
2474
2554
|
moving,
|
|
@@ -2494,7 +2574,7 @@ export function buildFloorplanEntryGeometry({
|
|
|
2494
2574
|
metricNotation,
|
|
2495
2575
|
purpose: 'edit',
|
|
2496
2576
|
roomLabelOverlay: true,
|
|
2497
|
-
|
|
2577
|
+
wallDimensionFaces,
|
|
2498
2578
|
}),
|
|
2499
2579
|
viewState: palette
|
|
2500
2580
|
? {
|
|
@@ -2642,6 +2722,87 @@ export const InteractiveGeometry = memo(function InteractiveGeometry({
|
|
|
2642
2722
|
|
|
2643
2723
|
return renderInteractive(geometry, 0)
|
|
2644
2724
|
|
|
2725
|
+
/**
|
|
2726
|
+
* A wall end's "pull to continue" grip: screen-sized, just past the end
|
|
2727
|
+
* along the wall, emerald so it reads apart from the end's move grip.
|
|
2728
|
+
*/
|
|
2729
|
+
function renderPullGrip(
|
|
2730
|
+
g: Extract<FloorplanGeometry, { kind: 'move-arrow' }>,
|
|
2731
|
+
keyHint: number,
|
|
2732
|
+
): React.ReactElement {
|
|
2733
|
+
const handleId = `${makeHandleId(nodeId, g.payload)}:pull`
|
|
2734
|
+
const isHovered = hoveredHandleId === handleId
|
|
2735
|
+
const k = handleUnitsPerPixel
|
|
2736
|
+
const c = PULL_GRIP_CENTER_PX * k
|
|
2737
|
+
const r = PULL_GRIP_RADIUS_PX * (isHovered ? 1.12 : 1) * k
|
|
2738
|
+
// Outward arrow inside the button: shaft then head, tip along +x.
|
|
2739
|
+
const arrowD = [
|
|
2740
|
+
`M ${c - 5.5 * k},${-1.4 * k}`,
|
|
2741
|
+
`L ${c + 0.5 * k},${-1.4 * k}`,
|
|
2742
|
+
`L ${c + 0.5 * k},${-4.6 * k}`,
|
|
2743
|
+
`L ${c + 6 * k},0`,
|
|
2744
|
+
`L ${c + 0.5 * k},${4.6 * k}`,
|
|
2745
|
+
`L ${c + 0.5 * k},${1.4 * k}`,
|
|
2746
|
+
`L ${c - 5.5 * k},${1.4 * k}`,
|
|
2747
|
+
'Z',
|
|
2748
|
+
].join(' ')
|
|
2749
|
+
const affordance = g.affordance
|
|
2750
|
+
const payload = g.payload
|
|
2751
|
+
return (
|
|
2752
|
+
<g
|
|
2753
|
+
data-wall-pull-grip={
|
|
2754
|
+
typeof payload === 'object' && payload && 'endpoint' in payload
|
|
2755
|
+
? String((payload as { endpoint: unknown }).endpoint)
|
|
2756
|
+
: ''
|
|
2757
|
+
}
|
|
2758
|
+
key={keyHint}
|
|
2759
|
+
onClick={(e) => e.stopPropagation()}
|
|
2760
|
+
transform={`translate(${g.point[0]} ${g.point[1]}) rotate(${(g.angle * 180) / Math.PI})`}
|
|
2761
|
+
>
|
|
2762
|
+
<line
|
|
2763
|
+
pointerEvents="none"
|
|
2764
|
+
stroke={PULL_GRIP_FILL}
|
|
2765
|
+
strokeDasharray={`${3 * k} ${2.5 * k}`}
|
|
2766
|
+
strokeLinecap="round"
|
|
2767
|
+
strokeWidth={1.5 * k}
|
|
2768
|
+
x1={PULL_GRIP_LEADER_START_PX * k}
|
|
2769
|
+
x2={c - r}
|
|
2770
|
+
y1={0}
|
|
2771
|
+
y2={0}
|
|
2772
|
+
/>
|
|
2773
|
+
<circle
|
|
2774
|
+
cx={c}
|
|
2775
|
+
cy={0}
|
|
2776
|
+
fill={isHovered ? PULL_GRIP_HOVER_FILL : PULL_GRIP_FILL}
|
|
2777
|
+
pointerEvents="none"
|
|
2778
|
+
r={r}
|
|
2779
|
+
stroke="#ffffff"
|
|
2780
|
+
strokeWidth={1.5 * k}
|
|
2781
|
+
/>
|
|
2782
|
+
<path d={arrowD} fill="#ffffff" pointerEvents="none" />
|
|
2783
|
+
<circle
|
|
2784
|
+
cx={c}
|
|
2785
|
+
cy={0}
|
|
2786
|
+
fill="transparent"
|
|
2787
|
+
onPointerDown={(e) => {
|
|
2788
|
+
if (affordance) {
|
|
2789
|
+
onHandlePointerDown(affordance, payload, e as ReactPointerEvent<SVGGElement>)
|
|
2790
|
+
}
|
|
2791
|
+
}}
|
|
2792
|
+
onPointerEnter={() => onHandleHoverChange(handleId)}
|
|
2793
|
+
onPointerLeave={() => onHandleHoverChange(null)}
|
|
2794
|
+
// Fill only: the default stroke is a plan metre wide, and would take
|
|
2795
|
+
// the press meant for the end's move grip.
|
|
2796
|
+
pointerEvents="fill"
|
|
2797
|
+
r={PULL_GRIP_HIT_RADIUS_PX * k}
|
|
2798
|
+
style={{ cursor: 'grab' }}
|
|
2799
|
+
>
|
|
2800
|
+
{g.title ? <title>{g.title}</title> : null}
|
|
2801
|
+
</circle>
|
|
2802
|
+
</g>
|
|
2803
|
+
)
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2645
2806
|
function renderInteractive(g: FloorplanGeometry, keyHint: number): React.ReactElement {
|
|
2646
2807
|
switch (g.kind) {
|
|
2647
2808
|
case 'group': {
|
|
@@ -2914,7 +3075,8 @@ export const InteractiveGeometry = memo(function InteractiveGeometry({
|
|
|
2914
3075
|
const head1 = `M ${t1x} ${t1y} L ${b1ax} ${b1ay} L ${b1bx} ${b1by} Z`
|
|
2915
3076
|
const head2 = `M ${t2x} ${t2y} L ${b2ax} ${b2ay} L ${b2bx} ${b2by} Z`
|
|
2916
3077
|
const fill = isHovered ? '#a5b4fc' : '#8381ed'
|
|
2917
|
-
const
|
|
3078
|
+
const scale = g.scale ?? 1
|
|
3079
|
+
const strokeWidthPx = (isHovered ? 2.4 : 1.8) * Math.sqrt(scale)
|
|
2918
3080
|
const angleDeg = (g.angle * 180) / Math.PI
|
|
2919
3081
|
const affordance = g.affordance
|
|
2920
3082
|
const payload = g.payload
|
|
@@ -2923,7 +3085,7 @@ export const InteractiveGeometry = memo(function InteractiveGeometry({
|
|
|
2923
3085
|
<g
|
|
2924
3086
|
key={keyHint}
|
|
2925
3087
|
onClick={(e) => e.stopPropagation()}
|
|
2926
|
-
transform={`translate(${g.point[0]} ${g.point[1]}) rotate(${angleDeg})`}
|
|
3088
|
+
transform={`translate(${g.point[0]} ${g.point[1]}) rotate(${angleDeg}) scale(${scale})`}
|
|
2927
3089
|
>
|
|
2928
3090
|
<path
|
|
2929
3091
|
d={arcPath}
|
|
@@ -2978,6 +3140,7 @@ export const InteractiveGeometry = memo(function InteractiveGeometry({
|
|
|
2978
3140
|
}
|
|
2979
3141
|
case 'move-arrow': {
|
|
2980
3142
|
if (!palette) return <></>
|
|
3143
|
+
if (g.variant === 'pull') return renderPullGrip(g, keyHint)
|
|
2981
3144
|
// Affordance-routed arrows (door width-resize) get a per-payload
|
|
2982
3145
|
// handle id so each side can hover independently; default
|
|
2983
3146
|
// (move-flow) arrows share the node's :move id like the dot.
|
|
@@ -2987,13 +3150,14 @@ export const InteractiveGeometry = memo(function InteractiveGeometry({
|
|
|
2987
3150
|
// so it shrinks on zoom-out and grows on zoom-in, matching the
|
|
2988
3151
|
// wall it accompanies. Composed of a rectangular shaft + triangular
|
|
2989
3152
|
// head, drawn as a single path for a clean fill + stroke outline.
|
|
2990
|
-
const
|
|
2991
|
-
const
|
|
2992
|
-
const
|
|
2993
|
-
const
|
|
3153
|
+
const k = g.scale ?? 1
|
|
3154
|
+
const sl = 0.1 * k // shaft length (shortened body)
|
|
3155
|
+
const hl = 0.12 * k // head length
|
|
3156
|
+
const sh = 0.04 * k // shaft half-height
|
|
3157
|
+
const hh = 0.1 * k // head half-height
|
|
2994
3158
|
// Inset the shaft start so the arrow sits a little off the wall
|
|
2995
3159
|
// body (matches the 3D `HANDLE_OFFSET`).
|
|
2996
|
-
const bi = 0.03 // base inset
|
|
3160
|
+
const bi = 0.03 * k // base inset
|
|
2997
3161
|
const arrowD = `M ${bi},${-sh} L ${bi + sl},${-sh} L ${bi + sl},${-hh} L ${bi + sl + hl},0 L ${bi + sl},${hh} L ${bi + sl},${sh} L ${bi},${sh} Z`
|
|
2998
3162
|
// Indigo palette to match the 3D wall push arrows
|
|
2999
3163
|
// (`ARROW_COLOR` / `ARROW_HOVER_COLOR`) and the corner-sphere
|
|
@@ -3642,11 +3806,19 @@ const HANDLE_CHROME_KINDS = new Set<FloorplanGeometry['kind']>([
|
|
|
3642
3806
|
'equal-spacing-badge',
|
|
3643
3807
|
])
|
|
3644
3808
|
|
|
3645
|
-
function stripHandleChrome(
|
|
3809
|
+
function stripHandleChrome(
|
|
3810
|
+
g: FloorplanGeometry,
|
|
3811
|
+
keepContextualDimensions = false,
|
|
3812
|
+
): FloorplanGeometry | null {
|
|
3813
|
+
if (
|
|
3814
|
+
keepContextualDimensions &&
|
|
3815
|
+
readFloorplanGeometryMetadata(g).annotationRole === 'contextual-dimension'
|
|
3816
|
+
)
|
|
3817
|
+
return g
|
|
3646
3818
|
if (HANDLE_CHROME_KINDS.has(g.kind)) return null
|
|
3647
3819
|
if (g.kind === 'group') {
|
|
3648
3820
|
const children = g.children
|
|
3649
|
-
.map(stripHandleChrome)
|
|
3821
|
+
.map((child) => stripHandleChrome(child, keepContextualDimensions))
|
|
3650
3822
|
.filter((c): c is FloorplanGeometry => c !== null)
|
|
3651
3823
|
if (children.length === 0) return null
|
|
3652
3824
|
return { kind: 'group', children, transform: g.transform }
|
|
@@ -3763,7 +3935,7 @@ function nodeDepsEqual(a: NodeDeps, b: NodeDeps): boolean {
|
|
|
3763
3935
|
'live',
|
|
3764
3936
|
'unit',
|
|
3765
3937
|
'metricNotation',
|
|
3766
|
-
'
|
|
3938
|
+
'wallDimensionFaces',
|
|
3767
3939
|
'selected',
|
|
3768
3940
|
'highlighted',
|
|
3769
3941
|
'hovered',
|