@pascal-app/editor 1.0.6 → 1.0.7
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 +20 -8
- package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
- package/src/components/editor/first-person-controls.tsx +72 -61
- package/src/components/editor/floating-action-menu.tsx +27 -9
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floorplan-panel.test.ts +220 -0
- package/src/components/editor/floorplan-panel.tsx +285 -132
- package/src/components/editor/footprint-height-handle.tsx +1 -1
- 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 +20 -0
- package/src/components/editor/index.tsx +130 -20
- package/src/components/editor/inspector-extension-actions.tsx +36 -0
- package/src/components/editor/localized-html.tsx +22 -0
- package/src/components/editor/node-action-menu.tsx +46 -12
- package/src/components/editor/node-arrow-handles.tsx +1 -2
- package/src/components/editor/open-wall-ends-3d-layer.tsx +1 -1
- package/src/components/editor/opening-guides-3d-layer.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 +1 -1
- package/src/components/editor/room-handles-2d.tsx +156 -6
- package/src/components/editor/room-handles.tsx +241 -51
- package/src/components/editor/room-highlight.tsx +2 -0
- package/src/components/editor/room-labels.tsx +29 -15
- 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 +1 -1
- 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 +1 -1
- package/src/components/editor/wall-move-side-handles.tsx +5 -1
- package/src/components/editor/wall-pull-grip.tsx +193 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +81 -1
- 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 +31 -3
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +6 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +22 -13
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +11 -3
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +7 -1
- 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-registry-layer.test.ts +12 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +197 -37
- 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/tools/distribution/distribution-run-tool.tsx +50 -26
- 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 +30 -34
- package/src/components/tools/registry/item-surface-move.test.ts +11 -0
- 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 +1 -1
- package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
- package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
- 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/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 +30 -14
- 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 +53 -3
- package/src/components/ui/helpers/helper-manager.tsx +82 -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 +113 -38
- package/src/components/ui/panels/floor-opening-panel.tsx +73 -25
- 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 +8 -6
- 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 +8 -7
- package/src/components/ui/panels/multi-parametric-inspector.tsx +122 -34
- package/src/components/ui/panels/multi-selection-panel.tsx +75 -35
- package/src/components/ui/panels/node-display.ts +35 -24
- package/src/components/ui/panels/panel-manager.tsx +5 -3
- package/src/components/ui/panels/panel-wrapper.tsx +22 -109
- 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 +464 -65
- 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 +21 -12
- package/src/components/ui/panels/room-panel.tsx +206 -99
- 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/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/site-panel/building-tree-node.tsx +4 -3
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -2
- 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 +117 -64
- 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 +8 -3
- 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 +18 -8
- 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 +61 -4
- 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 +15117 -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 +37501 -0
- package/src/i18n/locales/es/messages.ts +1 -0
- package/src/i18n/locales/fr/messages.po +37501 -0
- package/src/i18n/locales/fr/messages.ts +1 -0
- package/src/i18n/locales/ja/messages.po +37501 -0
- package/src/i18n/locales/ja/messages.ts +1 -0
- package/src/i18n/locales/zh-CN/messages.po +37501 -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 +156 -4
- package/src/lib/add-storey.test.ts +3 -3
- package/src/lib/contextual-help.test.ts +20 -1
- package/src/lib/contextual-help.ts +95 -19
- package/src/lib/continuation.ts +38 -11
- package/src/lib/floor-footprints.test.ts +2 -1
- package/src/lib/floor-foundation-support.test.ts +7 -3
- 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/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/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/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/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-parser.ts +7 -3
- package/src/lib/measurements.test.ts +17 -0
- package/src/lib/measurements.ts +20 -5
- package/src/lib/mezzanine-selection.test.ts +3 -1
- 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/reference-scale.ts +83 -0
- package/src/lib/room-built-on.test.ts +26 -12
- package/src/lib/room-built-on.ts +20 -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 +140 -0
- package/src/lib/room-handle-drag.ts +557 -42
- 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 +37 -0
- package/src/lib/room-push-dimensions.ts +226 -2
- package/src/lib/room-selection.test.ts +23 -6
- package/src/lib/scene-clipboard.test.ts +70 -49
- package/src/lib/scene-clipboard.ts +9 -4
- 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/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 +20 -5
- 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 +14 -5
- 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 +42 -0
- package/src/lib/window-interaction.ts +47 -5
- 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 +48 -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-wall-snap-indicator.ts +15 -2
|
@@ -12,12 +12,14 @@ import {
|
|
|
12
12
|
calculateLevelMiters,
|
|
13
13
|
createSizedStairFlight,
|
|
14
14
|
DEFAULT_ANGLE_STEP,
|
|
15
|
+
DEFAULT_WALL_THICKNESS,
|
|
15
16
|
type DoorNode,
|
|
16
17
|
DoorNode as DoorNodeSchema,
|
|
17
18
|
type ElevatorNode,
|
|
18
19
|
emitter,
|
|
19
20
|
type FenceNode,
|
|
20
21
|
type FloorplanGeometry,
|
|
22
|
+
formatNumber as formatLocalizedNumber,
|
|
21
23
|
type GeometryContext,
|
|
22
24
|
type GuideNode,
|
|
23
25
|
getRenderableSlabPolygon,
|
|
@@ -37,6 +39,7 @@ import {
|
|
|
37
39
|
resolveSlabPlacementElevation,
|
|
38
40
|
resolveTerrainWallConstructionOptions,
|
|
39
41
|
runAsSingleSceneHistoryStep,
|
|
42
|
+
type SeparatorNode,
|
|
40
43
|
type SiteNode,
|
|
41
44
|
type SlabNode,
|
|
42
45
|
SlabNode as SlabNodeSchema,
|
|
@@ -50,6 +53,7 @@ import {
|
|
|
50
53
|
useInteractive,
|
|
51
54
|
useLiveNodeOverrides,
|
|
52
55
|
useLiveTransforms,
|
|
56
|
+
useLocale,
|
|
53
57
|
useScene,
|
|
54
58
|
type WallNode,
|
|
55
59
|
WallNode as WallNodeSchema,
|
|
@@ -121,6 +125,13 @@ import {
|
|
|
121
125
|
} from '../../lib/keyboard-pan'
|
|
122
126
|
import { measurementHint, parseMeasurement } from '../../lib/measurement-parser'
|
|
123
127
|
import { formatLinearMeasurement, linearUnitToMeters } from '../../lib/measurements'
|
|
128
|
+
import {
|
|
129
|
+
createGuideScaleReference,
|
|
130
|
+
formatReferenceScaleLabel,
|
|
131
|
+
isValidReferenceScaleDisplayLength,
|
|
132
|
+
MAX_REFERENCE_SCALE_DISPLAY_LENGTH,
|
|
133
|
+
type ReferenceScaleUnit,
|
|
134
|
+
} from '../../lib/reference-scale'
|
|
124
135
|
import { snapRegisteredDraftPoint } from '../../lib/registered-draft-snap'
|
|
125
136
|
import { selectRoomFromHit } from '../../lib/room-selection-commands'
|
|
126
137
|
import { enterBuildingFromPlanHit } from '../../lib/selection-routing'
|
|
@@ -135,7 +146,13 @@ import {
|
|
|
135
146
|
} from '../../lib/units'
|
|
136
147
|
import { cn } from '../../lib/utils'
|
|
137
148
|
import { framingAnimates } from '../../lib/viewer-selection'
|
|
138
|
-
import {
|
|
149
|
+
import {
|
|
150
|
+
getWallDrawVariant,
|
|
151
|
+
getWallLineKind,
|
|
152
|
+
invisibleWallDraftSnapLines,
|
|
153
|
+
useWallDraftThickness,
|
|
154
|
+
} from '../../lib/wall-draw-variant'
|
|
155
|
+
import { wallSideOfPlanPoint } from '../../lib/wall-placement-pointer'
|
|
139
156
|
import {
|
|
140
157
|
addWallPolygonDraftCorner,
|
|
141
158
|
commitWallPolygonDraft,
|
|
@@ -143,6 +160,18 @@ import {
|
|
|
143
160
|
startWallPolygonDraft,
|
|
144
161
|
wallPolygonDraftWalls,
|
|
145
162
|
} from '../../lib/wall-polygon-draft'
|
|
163
|
+
import {
|
|
164
|
+
endWallPullRun,
|
|
165
|
+
releaseWallPullDrag,
|
|
166
|
+
useWallPullRun,
|
|
167
|
+
wallDraftIsRun,
|
|
168
|
+
} from '../../lib/wall-pull'
|
|
169
|
+
import {
|
|
170
|
+
isWallRunFree,
|
|
171
|
+
publishWallRunFeedback,
|
|
172
|
+
snapWallRunDraftPoint,
|
|
173
|
+
wallRunDirection,
|
|
174
|
+
} from '../../lib/wall-run-snap'
|
|
146
175
|
import { snapBuildingLocalToWorldGrid } from '../../lib/world-grid-snap'
|
|
147
176
|
import { nextZoneName } from '../../lib/zone-name'
|
|
148
177
|
import { subscribeNavigationSyncPose } from '../../store/navigation-sync-pose-store'
|
|
@@ -173,6 +202,7 @@ import {
|
|
|
173
202
|
VIEW_3D,
|
|
174
203
|
visibleScene,
|
|
175
204
|
} from '../../store/view-layout'
|
|
205
|
+
import { FloorplanAdjacentWallsLayer } from '../editor-2d/floorplan-adjacent-walls-layer'
|
|
176
206
|
import { FloorplanAlignmentGuideLayer } from '../editor-2d/floorplan-alignment-guide-layer'
|
|
177
207
|
import { FloorplanCursorIndicatorOverlay as Editor2dFloorplanCursorIndicatorOverlay } from '../editor-2d/floorplan-cursor-indicator-overlay'
|
|
178
208
|
import { FloorplanGroupActionMenu } from '../editor-2d/floorplan-group-action-menu'
|
|
@@ -230,6 +260,7 @@ import {
|
|
|
230
260
|
} from '../tools/shared/segment-angle'
|
|
231
261
|
import {
|
|
232
262
|
chainEndJoinsExistingWall,
|
|
263
|
+
createInvisibleWallOnCurrentLevel,
|
|
233
264
|
createWallOnCurrentLevel,
|
|
234
265
|
isSegmentLongEnough,
|
|
235
266
|
snapWallDraftPoint,
|
|
@@ -258,6 +289,7 @@ import {
|
|
|
258
289
|
resolveFloorplanPresentationViewBox,
|
|
259
290
|
setFloorplanCompassRotation,
|
|
260
291
|
} from './floorplan-navigation-presentation'
|
|
292
|
+
import { swallowNextClick } from './handles/use-handle-drag'
|
|
261
293
|
import { useFloorplanBackgroundPlacement } from './use-floorplan-background-placement'
|
|
262
294
|
import { useFloorplanHitTesting } from './use-floorplan-hit-testing'
|
|
263
295
|
import { useFloorplanSceneData } from './use-floorplan-scene-data'
|
|
@@ -266,6 +298,9 @@ import { sceneSpan } from './views/scene-region'
|
|
|
266
298
|
const FALLBACK_VIEW_SIZE = 12
|
|
267
299
|
const FLOORPLAN_PADDING = 2
|
|
268
300
|
const MIN_VIEWPORT_WIDTH_RATIO = 0.08
|
|
301
|
+
// However large the fitted plan (a whole lot), zooming in goes down to a view this wide (m): a
|
|
302
|
+
// casing leg or a stud's edge fills the screen.
|
|
303
|
+
const MIN_VIEWPORT_WIDTH = 0.6
|
|
269
304
|
const MAX_VIEWPORT_WIDTH_RATIO = 40
|
|
270
305
|
const PANEL_MIN_WIDTH = 420
|
|
271
306
|
const PANEL_MIN_HEIGHT = 320
|
|
@@ -526,8 +561,6 @@ type GuideTransformDraft = {
|
|
|
526
561
|
rotation: number
|
|
527
562
|
}
|
|
528
563
|
|
|
529
|
-
type ReferenceScaleUnit = 'meters' | 'centimeters' | 'feet' | 'inches'
|
|
530
|
-
|
|
531
564
|
// The in-flight reference-scale measurement. Only the per-CLICK fields live
|
|
532
565
|
// here (guide + start anchor); the rubber-band's moving END is the shared
|
|
533
566
|
// `useFloorplanDraftPreview.cursorPoint` (set on every move anyway), so it
|
|
@@ -835,7 +868,7 @@ function resolveFloorplanViewWidth(
|
|
|
835
868
|
return Math.max(0.001, requestedWidth)
|
|
836
869
|
}
|
|
837
870
|
|
|
838
|
-
const minWidth = fittedViewport.width * MIN_VIEWPORT_WIDTH_RATIO
|
|
871
|
+
const minWidth = Math.min(fittedViewport.width * MIN_VIEWPORT_WIDTH_RATIO, MIN_VIEWPORT_WIDTH)
|
|
839
872
|
const maxWidth = fittedViewport.width * MAX_VIEWPORT_WIDTH_RATIO
|
|
840
873
|
|
|
841
874
|
return clamp(requestedWidth, Math.min(minWidth, currentWidth), Math.max(maxWidth, currentWidth))
|
|
@@ -2281,7 +2314,12 @@ function getWallHoverSidePaths(polygon: Point2D[], wall: WallNode): [string, str
|
|
|
2281
2314
|
return [rightSidePath, leftSidePath]
|
|
2282
2315
|
}
|
|
2283
2316
|
|
|
2284
|
-
function buildDraftWall(
|
|
2317
|
+
function buildDraftWall(
|
|
2318
|
+
levelId: string,
|
|
2319
|
+
start: WallPlanPoint,
|
|
2320
|
+
end: WallPlanPoint,
|
|
2321
|
+
thickness?: number,
|
|
2322
|
+
): WallNode {
|
|
2285
2323
|
return {
|
|
2286
2324
|
object: 'node',
|
|
2287
2325
|
id: 'wall_draft' as WallNode['id'],
|
|
@@ -2293,6 +2331,7 @@ function buildDraftWall(levelId: string, start: WallPlanPoint, end: WallPlanPoin
|
|
|
2293
2331
|
children: [],
|
|
2294
2332
|
start,
|
|
2295
2333
|
end,
|
|
2334
|
+
thickness,
|
|
2296
2335
|
frontSide: 'unknown',
|
|
2297
2336
|
backSide: 'unknown',
|
|
2298
2337
|
}
|
|
@@ -2438,8 +2477,8 @@ function formatMeasurement(
|
|
|
2438
2477
|
return formatLinearMeasurement(measuredValue, unit, metricNotation)
|
|
2439
2478
|
}
|
|
2440
2479
|
|
|
2441
|
-
function
|
|
2442
|
-
return
|
|
2480
|
+
export function formatFloorplanNumber(value: number, locale: string, fractionDigits = 2) {
|
|
2481
|
+
return formatLocalizedNumber(value, { locale, max: fractionDigits })
|
|
2443
2482
|
}
|
|
2444
2483
|
|
|
2445
2484
|
function convertReferenceLengthToMeters(value: number, unit: ReferenceScaleUnit) {
|
|
@@ -2465,22 +2504,52 @@ const REFERENCE_SCALE_LINGO_UNIT: Record<ReferenceScaleUnit, 'm' | 'cm' | 'ft' |
|
|
|
2465
2504
|
/** Lingo-parse the free-text real-length input in the dropdown's unit — a
|
|
2466
2505
|
* bare number means the dropdown unit, while `180cm`, `1m80` or `5'11"`
|
|
2467
2506
|
* override it. Returns `null` when the text isn't a readable length. */
|
|
2468
|
-
function parseReferenceScaleLength(
|
|
2507
|
+
export function parseReferenceScaleLength(
|
|
2508
|
+
raw: string,
|
|
2509
|
+
unit: ReferenceScaleUnit,
|
|
2510
|
+
locale: string,
|
|
2511
|
+
): number | null {
|
|
2469
2512
|
const unitId = REFERENCE_SCALE_LINGO_UNIT[unit]
|
|
2470
2513
|
return parseMeasurement(
|
|
2471
2514
|
raw,
|
|
2472
2515
|
{ kind: 'length', unitId },
|
|
2473
|
-
{
|
|
2516
|
+
{
|
|
2517
|
+
bareUnit: unitId,
|
|
2518
|
+
locale,
|
|
2519
|
+
system: unit === 'feet' || unit === 'inches' ? 'us' : 'metric',
|
|
2520
|
+
},
|
|
2474
2521
|
)
|
|
2475
2522
|
}
|
|
2476
2523
|
|
|
2477
|
-
function
|
|
2524
|
+
export function getReferenceScaleInputError(
|
|
2525
|
+
raw: string,
|
|
2526
|
+
unit: ReferenceScaleUnit,
|
|
2527
|
+
locale: string,
|
|
2528
|
+
): string | null {
|
|
2529
|
+
if (raw.trim() === '') return 'Enter the real length of the line.'
|
|
2530
|
+
const displayLength = parseReferenceScaleLength(raw, unit, locale)
|
|
2531
|
+
if (displayLength === null || Number.isNaN(displayLength)) {
|
|
2532
|
+
return `Enter a length like 3.5, 180cm or 5'11".`
|
|
2533
|
+
}
|
|
2534
|
+
if (!(displayLength > 0)) return 'Length must be greater than 0.'
|
|
2535
|
+
if (!isValidReferenceScaleDisplayLength(displayLength)) {
|
|
2536
|
+
return `Length must be at most ${formatFloorplanNumber(MAX_REFERENCE_SCALE_DISPLAY_LENGTH, locale)} ${REFERENCE_SCALE_LINGO_UNIT[unit]}.`
|
|
2537
|
+
}
|
|
2538
|
+
return null
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
function referenceScaleLengthHint(
|
|
2542
|
+
raw: string,
|
|
2543
|
+
unit: ReferenceScaleUnit,
|
|
2544
|
+
locale: string,
|
|
2545
|
+
): string | null {
|
|
2478
2546
|
const unitId = REFERENCE_SCALE_LINGO_UNIT[unit]
|
|
2479
2547
|
return measurementHint(
|
|
2480
2548
|
raw,
|
|
2481
2549
|
{ kind: 'length', unitId },
|
|
2482
2550
|
{
|
|
2483
2551
|
bareUnit: unitId,
|
|
2552
|
+
locale,
|
|
2484
2553
|
system: unit === 'feet' || unit === 'inches' ? 'us' : 'metric',
|
|
2485
2554
|
displayUnit: unitId,
|
|
2486
2555
|
precision: 2,
|
|
@@ -2488,23 +2557,6 @@ function referenceScaleLengthHint(raw: string, unit: ReferenceScaleUnit): string
|
|
|
2488
2557
|
)
|
|
2489
2558
|
}
|
|
2490
2559
|
|
|
2491
|
-
function getReferenceScaleUnitLabel(unit: ReferenceScaleUnit) {
|
|
2492
|
-
switch (unit) {
|
|
2493
|
-
case 'centimeters':
|
|
2494
|
-
return 'cm'
|
|
2495
|
-
case 'feet':
|
|
2496
|
-
return 'ft'
|
|
2497
|
-
case 'inches':
|
|
2498
|
-
return 'in'
|
|
2499
|
-
default:
|
|
2500
|
-
return 'm'
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2503
|
-
|
|
2504
|
-
function formatReferenceScaleLabel(value: number, unit: ReferenceScaleUnit) {
|
|
2505
|
-
return `${formatNumber(value)} ${getReferenceScaleUnitLabel(unit)}`
|
|
2506
|
-
}
|
|
2507
|
-
|
|
2508
2560
|
function getPolygonAreaAndCentroid(polygon: Point2D[]) {
|
|
2509
2561
|
let cx = 0
|
|
2510
2562
|
let cy = 0
|
|
@@ -2810,8 +2862,10 @@ function findClosestWallPoint(
|
|
|
2810
2862
|
const distSq = (point[0] - px) ** 2 + (point[1] - pz) ** 2
|
|
2811
2863
|
if (distSq < bestDistSq) {
|
|
2812
2864
|
bestDistSq = distSq
|
|
2813
|
-
//
|
|
2814
|
-
|
|
2865
|
+
// The face the cursor is on, as a 3D hover's normal reports it: a door
|
|
2866
|
+
// swings and a window faces toward the cursor's side, in 2D as in 3D.
|
|
2867
|
+
const side = wallSideOfPlanPoint(wall, [point[0], point[1]])
|
|
2868
|
+
best = { wall, point: [px, pz], t, normal: [0, 0, side === 'front' ? 1 : -1] }
|
|
2815
2869
|
}
|
|
2816
2870
|
}
|
|
2817
2871
|
|
|
@@ -3281,6 +3335,7 @@ function FloorplanReferenceScaleLayer({
|
|
|
3281
3335
|
unit: 'metric' | 'imperial'
|
|
3282
3336
|
unitsPerPixel: number
|
|
3283
3337
|
}) {
|
|
3338
|
+
const locale = useLocale()
|
|
3284
3339
|
const visibleReferences = guides
|
|
3285
3340
|
.filter((guide) => guideUi[guide.id]?.scaleReferenceVisible !== false)
|
|
3286
3341
|
.map((guide) => guide.scaleReference)
|
|
@@ -3290,16 +3345,19 @@ function FloorplanReferenceScaleLayer({
|
|
|
3290
3345
|
|
|
3291
3346
|
return (
|
|
3292
3347
|
<>
|
|
3293
|
-
{visibleReferences.map((reference, index) =>
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3348
|
+
{visibleReferences.map((reference, index) => {
|
|
3349
|
+
const label = formatReferenceScaleLabel(reference, locale)
|
|
3350
|
+
return (
|
|
3351
|
+
<FloorplanReferenceScaleLine
|
|
3352
|
+
end={reference.end}
|
|
3353
|
+
key={`${index}-${reference.start.join(',')}-${reference.end.join(',')}`}
|
|
3354
|
+
label={label}
|
|
3355
|
+
palette={palette}
|
|
3356
|
+
start={reference.start}
|
|
3357
|
+
unitsPerPixel={unitsPerPixel}
|
|
3358
|
+
/>
|
|
3359
|
+
)
|
|
3360
|
+
})}
|
|
3303
3361
|
{draft?.start && (
|
|
3304
3362
|
<FloorplanReferenceScaleDraftLine
|
|
3305
3363
|
palette={palette}
|
|
@@ -4442,6 +4500,8 @@ function FloorplanLinearDraftLayer({
|
|
|
4442
4500
|
sceneRotationDeg: number
|
|
4443
4501
|
}) {
|
|
4444
4502
|
const metricNotation = useViewer((state) => state.metricNotation)
|
|
4503
|
+
// The thickness the wall commits at, as the 3D draft shows it.
|
|
4504
|
+
const wallDraftThickness = useWallDraftThickness(DEFAULT_WALL_THICKNESS)
|
|
4445
4505
|
const wallDraftEnd = useFloorplanDraftPreview((s) => s.wallDraftEnd)
|
|
4446
4506
|
const fenceDraftEnd = useFloorplanDraftPreview((s) => s.fenceDraftEnd)
|
|
4447
4507
|
const roofDraftEnd = useFloorplanDraftPreview((s) => s.roofDraftEnd)
|
|
@@ -4465,10 +4525,12 @@ function FloorplanLinearDraftLayer({
|
|
|
4465
4525
|
) {
|
|
4466
4526
|
return null
|
|
4467
4527
|
}
|
|
4468
|
-
const draftWall = getSharedFloorplanWall(
|
|
4528
|
+
const draftWall = getSharedFloorplanWall(
|
|
4529
|
+
buildDraftWall(levelId, wallDraftStart, wallDraftEnd, wallDraftThickness),
|
|
4530
|
+
)
|
|
4469
4531
|
// Keep the live draft preview cheap; full level-wide mitering here runs on every mouse move.
|
|
4470
4532
|
return getWallPlanFootprint(draftWall, EMPTY_WALL_MITER_DATA)
|
|
4471
|
-
}, [levelId, wallDraftStart, wallDraftEnd])
|
|
4533
|
+
}, [levelId, wallDraftStart, wallDraftEnd, wallDraftThickness])
|
|
4472
4534
|
|
|
4473
4535
|
// Rectangle mode drafts four walls at once; they read exactly like a line
|
|
4474
4536
|
// draft — same mitered footprints, measurement plates and axis guides.
|
|
@@ -4838,6 +4900,7 @@ export function FloorplanPanel({
|
|
|
4838
4900
|
floorplanSceneSlot?: ReactNode
|
|
4839
4901
|
}) {
|
|
4840
4902
|
useFloorplanCameraSyncBridge()
|
|
4903
|
+
const locale = useLocale()
|
|
4841
4904
|
const viewportHostRef = useRef<HTMLDivElement>(null)
|
|
4842
4905
|
const svgRef = useRef<SVGSVGElement>(null)
|
|
4843
4906
|
const floorplanBackgroundRef = useRef<SVGRectElement>(null)
|
|
@@ -5042,6 +5105,9 @@ export function FloorplanPanel({
|
|
|
5042
5105
|
// Walls committed by the current 2D-only chain — exclusion set for the
|
|
5043
5106
|
// T-junction chain-termination test (mirrors the 3D tool's `chainWallIds`).
|
|
5044
5107
|
const wallChainWallIdsRef = useRef<string[]>([])
|
|
5108
|
+
// Direction of the run's last wall (a pulled run's source first): the next
|
|
5109
|
+
// holds square to it. Null before the run has a wall.
|
|
5110
|
+
const wallRunReferenceRef = useRef<WallPlanPoint | null>(null)
|
|
5045
5111
|
const setDraftEnd = useCallback(
|
|
5046
5112
|
(next: WallPlanPoint | null | ((prev: WallPlanPoint | null) => WallPlanPoint | null)) => {
|
|
5047
5113
|
const store = useFloorplanDraftPreview.getState()
|
|
@@ -5057,14 +5123,10 @@ export function FloorplanPanel({
|
|
|
5057
5123
|
},
|
|
5058
5124
|
[],
|
|
5059
5125
|
)
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
store.setRoofDraftEnd(typeof next === 'function' ? next(store.roofDraftEnd) : next)
|
|
5065
|
-
},
|
|
5066
|
-
[],
|
|
5067
|
-
)
|
|
5126
|
+
// The roof tool that owns the input (the 3D tool, or the plan's own in 2D-only view) writes the
|
|
5127
|
+
// roof draft from the corners it snaps and commits; the plan only draws it, so its rectangle is
|
|
5128
|
+
// the roof the next click makes.
|
|
5129
|
+
const roofDraftStart = useFloorplanDraftPreview((s) => s.roofDraftStart)
|
|
5068
5130
|
const [ceilingDraftPoints, setCeilingDraftPoints] = useState<WallPlanPoint[]>([])
|
|
5069
5131
|
const [slabDraftPoints, setSlabDraftPoints] = useState<WallPlanPoint[]>([])
|
|
5070
5132
|
const slabPlacementBaseRef = useRef<number | null>(null)
|
|
@@ -5077,9 +5139,6 @@ export function FloorplanPanel({
|
|
|
5077
5139
|
useEffect(() => {
|
|
5078
5140
|
useFloorplanDraftPreview.getState().setFenceDraftStart(fenceDraftStart)
|
|
5079
5141
|
}, [fenceDraftStart])
|
|
5080
|
-
useEffect(() => {
|
|
5081
|
-
useFloorplanDraftPreview.getState().setRoofDraftStart(roofDraftStart)
|
|
5082
|
-
}, [roofDraftStart])
|
|
5083
5142
|
const [zoneDraftPoints, setZoneDraftPoints] = useState<WallPlanPoint[]>([])
|
|
5084
5143
|
const [siteBoundaryDraft, setSiteBoundaryDraft] = useState<SiteBoundaryDraft | null>(null)
|
|
5085
5144
|
const [siteVertexDragState, setSiteVertexDragState] = useState<SiteVertexDragState | null>(null)
|
|
@@ -7161,8 +7220,8 @@ export function FloorplanPanel({
|
|
|
7161
7220
|
return
|
|
7162
7221
|
}
|
|
7163
7222
|
|
|
7164
|
-
const displayLength = parseReferenceScaleLength(referenceScaleValue, referenceScaleUnit)
|
|
7165
|
-
if (
|
|
7223
|
+
const displayLength = parseReferenceScaleLength(referenceScaleValue, referenceScaleUnit, locale)
|
|
7224
|
+
if (displayLength === null || !isValidReferenceScaleDisplayLength(displayLength)) {
|
|
7166
7225
|
return
|
|
7167
7226
|
}
|
|
7168
7227
|
|
|
@@ -7199,14 +7258,17 @@ export function FloorplanPanel({
|
|
|
7199
7258
|
{
|
|
7200
7259
|
position: nextGuidePosition,
|
|
7201
7260
|
scale: nextGuideScale,
|
|
7202
|
-
scaleReference:
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7261
|
+
scaleReference: createGuideScaleReference(
|
|
7262
|
+
{
|
|
7263
|
+
start: pendingReferenceScale.start,
|
|
7264
|
+
end: scaledEnd,
|
|
7265
|
+
realLengthMeters,
|
|
7266
|
+
measuredLengthUnits: scaledMeasuredLengthUnits,
|
|
7267
|
+
metersPerUnit,
|
|
7268
|
+
},
|
|
7269
|
+
displayLength,
|
|
7270
|
+
referenceScaleUnit,
|
|
7271
|
+
),
|
|
7210
7272
|
} as Partial<GuideNode>,
|
|
7211
7273
|
)
|
|
7212
7274
|
setGuideLocked(pendingReferenceScale.guideId, true)
|
|
@@ -7215,6 +7277,7 @@ export function FloorplanPanel({
|
|
|
7215
7277
|
setPendingReferenceScale(null)
|
|
7216
7278
|
}, [
|
|
7217
7279
|
guideById,
|
|
7280
|
+
locale,
|
|
7218
7281
|
pendingReferenceScale,
|
|
7219
7282
|
referenceScaleUnit,
|
|
7220
7283
|
referenceScaleValue,
|
|
@@ -7861,20 +7924,29 @@ export function FloorplanPanel({
|
|
|
7861
7924
|
setWallChainFirstVertex(null)
|
|
7862
7925
|
wallConstructionOptionsRef.current = undefined
|
|
7863
7926
|
wallChainWallIdsRef.current = []
|
|
7927
|
+
wallRunReferenceRef.current = null
|
|
7864
7928
|
setDraftEnd(null)
|
|
7865
7929
|
useSegmentDraftChain.getState().clear('wall')
|
|
7930
|
+
// A run pulled from a wall end ends with the plan's draft: back to Select.
|
|
7931
|
+
endWallPullRun()
|
|
7866
7932
|
}, [setDraftEnd])
|
|
7867
7933
|
const clearFencePlacementDraft = useCallback(() => {
|
|
7868
7934
|
setFenceDraftStart(null)
|
|
7869
7935
|
setFenceDraftEnd(null)
|
|
7870
7936
|
}, [setFenceDraftEnd])
|
|
7871
7937
|
const clearRoofPlacementDraft = useCallback(() => {
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7938
|
+
const draft = useFloorplanDraftPreview.getState()
|
|
7939
|
+
draft.setRoofDraftStart(null)
|
|
7940
|
+
draft.setRoofDraftEnd(null)
|
|
7941
|
+
}, [])
|
|
7875
7942
|
const clearCeilingPlacementDraft = useCallback(() => {
|
|
7876
7943
|
setCeilingDraftPoints([])
|
|
7877
7944
|
}, [])
|
|
7945
|
+
// Beside the 3D view its ceiling tool commits; in 2D-only view the plan does. Each keeps its own
|
|
7946
|
+
// corners, so a polygon begun under one owner must not be closed against the other's.
|
|
7947
|
+
useEffect(() => {
|
|
7948
|
+
if (shownScene) clearCeilingPlacementDraft()
|
|
7949
|
+
}, [clearCeilingPlacementDraft, shownScene])
|
|
7878
7950
|
const clearSlabPlacementDraft = useCallback(() => {
|
|
7879
7951
|
setSlabDraftPoints([])
|
|
7880
7952
|
slabPlacementBaseRef.current = null
|
|
@@ -7970,6 +8042,30 @@ export function FloorplanPanel({
|
|
|
7970
8042
|
}
|
|
7971
8043
|
}, [clearDraft])
|
|
7972
8044
|
|
|
8045
|
+
// A wall end's pull grip pressed in the plan opens the Walls run at that
|
|
8046
|
+
// end: start the plan's draft there, as if the end had been clicked. A pull
|
|
8047
|
+
// from the 3D view drafts there (the plan beside it follows its moves).
|
|
8048
|
+
const wallPullRun = useWallPullRun((state) => state.run)
|
|
8049
|
+
const seededWallPullSeqRef = useRef<number | null>(null)
|
|
8050
|
+
useEffect(() => {
|
|
8051
|
+
if (!(wallPullRun && wallPullRun.origin === '2d' && isWallBuildActive && levelId)) return
|
|
8052
|
+
if (seededWallPullSeqRef.current === wallPullRun.seq) return
|
|
8053
|
+
seededWallPullSeqRef.current = wallPullRun.seq
|
|
8054
|
+
const start = wallPullRun.start
|
|
8055
|
+
wallConstructionOptionsRef.current = resolveTerrainWallConstructionOptions(
|
|
8056
|
+
useScene.getState().nodes,
|
|
8057
|
+
levelId,
|
|
8058
|
+
start,
|
|
8059
|
+
useEditor.getState().toolDefaults.wall,
|
|
8060
|
+
)
|
|
8061
|
+
wallChainWallIdsRef.current = []
|
|
8062
|
+
wallRunReferenceRef.current = wallPullRun.reference
|
|
8063
|
+
setDraftStart(start)
|
|
8064
|
+
setWallChainFirstVertex(start)
|
|
8065
|
+
setDraftEnd(start)
|
|
8066
|
+
setCursorPoint(start)
|
|
8067
|
+
}, [isWallBuildActive, levelId, setCursorPoint, setDraftEnd, wallPullRun])
|
|
8068
|
+
|
|
7973
8069
|
const createZoneOnCurrentLevel = useCallback(
|
|
7974
8070
|
(points: WallPlanPoint[]) => {
|
|
7975
8071
|
if (!levelId) {
|
|
@@ -9284,14 +9380,6 @@ export function FloorplanPanel({
|
|
|
9284
9380
|
setCursorPoint((previousPoint) =>
|
|
9285
9381
|
previousPoint && pointsEqual(previousPoint, snappedPoint) ? previousPoint : snappedPoint,
|
|
9286
9382
|
)
|
|
9287
|
-
|
|
9288
|
-
if (roofDraftStart) {
|
|
9289
|
-
setRoofDraftEnd((previousPoint) =>
|
|
9290
|
-
previousPoint && pointsEqual(previousPoint, snappedPoint)
|
|
9291
|
-
? previousPoint
|
|
9292
|
-
: snappedPoint,
|
|
9293
|
-
)
|
|
9294
|
-
}
|
|
9295
9383
|
return
|
|
9296
9384
|
}
|
|
9297
9385
|
|
|
@@ -9506,35 +9594,61 @@ export function FloorplanPanel({
|
|
|
9506
9594
|
return
|
|
9507
9595
|
}
|
|
9508
9596
|
|
|
9509
|
-
// Wall draft: grid + magnetic snap, then Figma-style alignment.
|
|
9510
|
-
//
|
|
9511
|
-
//
|
|
9512
|
-
//
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9597
|
+
// Wall draft: grid + magnetic snap, then Figma-style alignment. A Walls
|
|
9598
|
+
// segment locks to 15° rays from its start; a run's segment (a Polygon
|
|
9599
|
+
// side, a pulled run) holds square to the last wall and the house's axes,
|
|
9600
|
+
// with projection guides and a labelled snap marker
|
|
9601
|
+
// (`snapWallRunDraftPoint`). Snapping is governed by the snapping mode
|
|
9602
|
+
// (`'off'` is the bypass); Alt frees a run's segment. An invisible wall
|
|
9603
|
+
// draft catches the invisible walls already drawn as well.
|
|
9604
|
+
let snappedPoint: WallPlanPoint
|
|
9605
|
+
const draftSnapWalls = [
|
|
9606
|
+
...walls,
|
|
9607
|
+
...wallPolygonDraftWalls(),
|
|
9608
|
+
...invisibleWallDraftSnapLines(),
|
|
9609
|
+
]
|
|
9610
|
+
if (draftStart && wallDraftIsRun()) {
|
|
9611
|
+
const runSnap = snapWallRunDraftPoint({
|
|
9612
|
+
point: planPoint,
|
|
9613
|
+
start: draftStart,
|
|
9614
|
+
runStart: wallChainFirstVertex ?? draftStart,
|
|
9615
|
+
reference: wallRunReferenceRef.current,
|
|
9616
|
+
walls: draftSnapWalls,
|
|
9617
|
+
free: isWallRunFree(event),
|
|
9618
|
+
})
|
|
9619
|
+
snappedPoint = runSnap.point
|
|
9620
|
+
publishWallRunFeedback(runSnap)
|
|
9528
9621
|
} else {
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9622
|
+
const wallAngleSnap = draftStart !== null && isAngleSnapActive()
|
|
9623
|
+
const wallSnap = snapWallDraftPointDetailed({
|
|
9624
|
+
point: planPoint,
|
|
9625
|
+
walls: draftSnapWalls,
|
|
9626
|
+
start: draftStart ?? undefined,
|
|
9627
|
+
angleSnap: wallAngleSnap,
|
|
9628
|
+
magnetic: isMagneticSnapActive(),
|
|
9629
|
+
// Same as the click: an end aimed into a wall's body joins it.
|
|
9630
|
+
captureWallBodies: true,
|
|
9533
9631
|
})
|
|
9632
|
+
const wallSnapped = wallSnap.point
|
|
9633
|
+
// Locked onto existing geometry (corner / midpoint / crossing / edge) →
|
|
9634
|
+
// that snap wins, so skip Figma alignment and stand the beacon there.
|
|
9635
|
+
const lockedToWall = wallSnap.snap !== null
|
|
9636
|
+
snappedPoint = wallSnapped
|
|
9637
|
+
if (lockedToWall) {
|
|
9638
|
+
useAlignmentGuides.getState().clear()
|
|
9639
|
+
} else {
|
|
9640
|
+
// Alignment lines show in every mode; the pull applies only when
|
|
9641
|
+
// magnetic ('lines') and the segment isn't angle-locked.
|
|
9642
|
+
snappedPoint = alignFloorplanDraftPoint(wallSnapped, {
|
|
9643
|
+
applySnap: isMagneticSnapActive() && !wallAngleSnap,
|
|
9644
|
+
})
|
|
9645
|
+
}
|
|
9646
|
+
useWallSnapIndicator
|
|
9647
|
+
.getState()
|
|
9648
|
+
.set(
|
|
9649
|
+
wallSnap.snap ? { x: snappedPoint[0], z: snappedPoint[1], kind: wallSnap.snap } : null,
|
|
9650
|
+
)
|
|
9534
9651
|
}
|
|
9535
|
-
useWallSnapIndicator
|
|
9536
|
-
.getState()
|
|
9537
|
-
.set(wallSnap.snap ? { x: snappedPoint[0], z: snappedPoint[1], kind: wallSnap.snap } : null)
|
|
9538
9652
|
|
|
9539
9653
|
// Emit `grid:move` so the registry-driven wall tool's 3D preview
|
|
9540
9654
|
// tracks the cursor. The local draftEnd update below is what
|
|
@@ -9592,15 +9706,14 @@ export function FloorplanPanel({
|
|
|
9592
9706
|
levelId,
|
|
9593
9707
|
publishFloorplanNavigationPose,
|
|
9594
9708
|
referenceScaleDraft,
|
|
9595
|
-
roofDraftStart,
|
|
9596
9709
|
siteVertexDragState,
|
|
9597
9710
|
surfaceSize.height,
|
|
9598
9711
|
surfaceSize.width,
|
|
9599
9712
|
svgAspectRatio,
|
|
9600
9713
|
walls,
|
|
9714
|
+
wallChainFirstVertex,
|
|
9601
9715
|
setCursorPoint,
|
|
9602
9716
|
setDraftEnd,
|
|
9603
|
-
setRoofDraftEnd,
|
|
9604
9717
|
setFenceDraftEnd,
|
|
9605
9718
|
],
|
|
9606
9719
|
)
|
|
@@ -9794,6 +9907,7 @@ export function FloorplanPanel({
|
|
|
9794
9907
|
) {
|
|
9795
9908
|
startWallPolygonDraft(levelId as AnyNodeId, point, wallConstructionOptionsRef.current)
|
|
9796
9909
|
}
|
|
9910
|
+
wallRunReferenceRef.current = null
|
|
9797
9911
|
setDraftStart(point)
|
|
9798
9912
|
setWallChainFirstVertex(point)
|
|
9799
9913
|
setDraftEnd(point)
|
|
@@ -9804,6 +9918,8 @@ export function FloorplanPanel({
|
|
|
9804
9918
|
if (!isSegmentLongEnough(draftStart, point)) {
|
|
9805
9919
|
return
|
|
9806
9920
|
}
|
|
9921
|
+
// The run's next segment holds square to this one.
|
|
9922
|
+
const placedDirection = wallRunDirection(draftStart, point)
|
|
9807
9923
|
|
|
9808
9924
|
// The 3D wall tool's `grid:click` listener
|
|
9809
9925
|
// (`packages/nodes/src/wall/tool.tsx`) owns the wall-create
|
|
@@ -9820,7 +9936,7 @@ export function FloorplanPanel({
|
|
|
9820
9936
|
// ceiling 2D-only committers: create locally here, gated on the
|
|
9821
9937
|
// view, so split / 3D keep their single-owner tool commit.
|
|
9822
9938
|
const viewIs2DOnly = visibleScene(useEditor.getState()) === '2d'
|
|
9823
|
-
let createdWall: WallNode | null = null
|
|
9939
|
+
let createdWall: WallNode | SeparatorNode | null = null
|
|
9824
9940
|
// 2D-only Polygon room: the corner joins the draft; nothing is written
|
|
9825
9941
|
// until it closes, seals or tees (then every wall at once).
|
|
9826
9942
|
if (viewIs2DOnly && getWallDrawVariant() === 'polygon') {
|
|
@@ -9830,17 +9946,18 @@ export function FloorplanPanel({
|
|
|
9830
9946
|
setCursorPoint(null)
|
|
9831
9947
|
return
|
|
9832
9948
|
}
|
|
9949
|
+
wallRunReferenceRef.current = placedDirection
|
|
9833
9950
|
setDraftStart(point)
|
|
9834
9951
|
setDraftEnd(point)
|
|
9835
9952
|
setCursorPoint(point)
|
|
9836
9953
|
return
|
|
9837
9954
|
}
|
|
9838
9955
|
if (viewIs2DOnly) {
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9956
|
+
// The Walls variant's Invisible draws room separators, not walls.
|
|
9957
|
+
createdWall =
|
|
9958
|
+
getWallLineKind() === 'invisible'
|
|
9959
|
+
? createInvisibleWallOnCurrentLevel(draftStart, point)
|
|
9960
|
+
: createWallOnCurrentLevel(draftStart, point, wallConstructionOptionsRef.current)
|
|
9844
9961
|
}
|
|
9845
9962
|
if (createdWall) {
|
|
9846
9963
|
wallChainWallIdsRef.current.push(createdWall.id)
|
|
@@ -9856,7 +9973,7 @@ export function FloorplanPanel({
|
|
|
9856
9973
|
: (publishedNextStart ?? point)
|
|
9857
9974
|
|
|
9858
9975
|
if (
|
|
9859
|
-
|
|
9976
|
+
!wallDraftIsRun() ||
|
|
9860
9977
|
(wallChainFirstVertex && isWithinWallJoinSnapRadius(nextStart, wallChainFirstVertex))
|
|
9861
9978
|
) {
|
|
9862
9979
|
clearWallPlacementDraft()
|
|
@@ -9878,7 +9995,7 @@ export function FloorplanPanel({
|
|
|
9878
9995
|
levelWalls,
|
|
9879
9996
|
wallChainWallIdsRef.current,
|
|
9880
9997
|
) ||
|
|
9881
|
-
wallClosesRoom(levelWalls, createdWall)
|
|
9998
|
+
(createdWall.type === 'wall' && wallClosesRoom(levelWalls, createdWall))
|
|
9882
9999
|
) {
|
|
9883
10000
|
clearWallPlacementDraft()
|
|
9884
10001
|
setCursorPoint(null)
|
|
@@ -9894,6 +10011,7 @@ export function FloorplanPanel({
|
|
|
9894
10011
|
return
|
|
9895
10012
|
}
|
|
9896
10013
|
|
|
10014
|
+
wallRunReferenceRef.current = placedDirection
|
|
9897
10015
|
setDraftStart(nextStart)
|
|
9898
10016
|
setDraftEnd(nextStart)
|
|
9899
10017
|
setCursorPoint(nextStart)
|
|
@@ -9924,6 +10042,11 @@ export function FloorplanPanel({
|
|
|
9924
10042
|
}) => snapWallDraftPoint({ ...args, magnetic: isMagneticSnapActive() }),
|
|
9925
10043
|
[],
|
|
9926
10044
|
)
|
|
10045
|
+
// What the click needs to snap an open run's segment as the move did.
|
|
10046
|
+
const getWallRunContext = useCallback(
|
|
10047
|
+
() => ({ runStart: wallChainFirstVertex, reference: wallRunReferenceRef.current }),
|
|
10048
|
+
[wallChainFirstVertex],
|
|
10049
|
+
)
|
|
9927
10050
|
const { handleBackgroundPlacementClick } = useFloorplanBackgroundPlacement({
|
|
9928
10051
|
activePolygonDraftPoints,
|
|
9929
10052
|
ceilingDraftPoints,
|
|
@@ -9936,6 +10059,7 @@ export function FloorplanPanel({
|
|
|
9936
10059
|
findClosestWallPoint,
|
|
9937
10060
|
floorplanOpeningLocalY,
|
|
9938
10061
|
getSnappedFloorplanPoint,
|
|
10062
|
+
getWallRunContext,
|
|
9939
10063
|
handleCeilingItemPlacementClick,
|
|
9940
10064
|
handleCeilingPlacementPoint,
|
|
9941
10065
|
handleSlabPlacementPoint,
|
|
@@ -9959,20 +10083,16 @@ export function FloorplanPanel({
|
|
|
9959
10083
|
isWallBuildActive,
|
|
9960
10084
|
isZoneBuildActive,
|
|
9961
10085
|
levelId,
|
|
9962
|
-
roofDraftStart,
|
|
9963
10086
|
setCursorPoint,
|
|
9964
10087
|
setFenceDraftEnd,
|
|
9965
10088
|
setFenceDraftStart,
|
|
9966
|
-
setRoofDraftEnd,
|
|
9967
|
-
setRoofDraftStart,
|
|
9968
10089
|
snapPolygonDraftPoint,
|
|
9969
10090
|
snapWallDraftPoint: snapWallDraftPointMagnetic,
|
|
9970
10091
|
toPoint2D,
|
|
9971
10092
|
walls,
|
|
9972
|
-
//
|
|
9973
|
-
//
|
|
9974
|
-
//
|
|
9975
|
-
// building rotation / position.
|
|
10093
|
+
// Building-grid snap: the lattice this plan draws inside its rotated
|
|
10094
|
+
// scene group, so drafts land on the visible grid however the
|
|
10095
|
+
// building is turned, and where the 3D tool would put them.
|
|
9976
10096
|
worldGridSnap: snapBuildingLocalToWorldGrid,
|
|
9977
10097
|
})
|
|
9978
10098
|
|
|
@@ -10021,10 +10141,11 @@ export function FloorplanPanel({
|
|
|
10021
10141
|
// confirming without editing is a no-op instead of a surprise
|
|
10022
10142
|
// rescale (plan units are meters; convert when defaulting to feet).
|
|
10023
10143
|
setReferenceScaleValue(
|
|
10024
|
-
|
|
10144
|
+
formatFloorplanNumber(
|
|
10025
10145
|
unit === 'imperial'
|
|
10026
10146
|
? measuredLengthUnits / linearUnitToMeters(1, 'imperial')
|
|
10027
10147
|
: measuredLengthUnits,
|
|
10148
|
+
locale,
|
|
10028
10149
|
2,
|
|
10029
10150
|
),
|
|
10030
10151
|
)
|
|
@@ -10174,6 +10295,16 @@ export function FloorplanPanel({
|
|
|
10174
10295
|
)
|
|
10175
10296
|
const handleSvgPointerUp = useCallback(
|
|
10176
10297
|
(event: ReactPointerEvent<SVGSVGElement>) => {
|
|
10298
|
+
// Letting go of a wall end's pull grip places the pulled wall, as a click
|
|
10299
|
+
// there would; the click the browser fires next is eaten.
|
|
10300
|
+
const pull = useWallPullRun.getState().run
|
|
10301
|
+
if (event.button === 0 && pull?.dragging && pull.origin === '2d') {
|
|
10302
|
+
releaseWallPullDrag()
|
|
10303
|
+
if (isWallBuildActive && draftStart) {
|
|
10304
|
+
swallowNextClick()
|
|
10305
|
+
handleBackgroundClick(event)
|
|
10306
|
+
}
|
|
10307
|
+
}
|
|
10177
10308
|
if (
|
|
10178
10309
|
isFenceBuildActive &&
|
|
10179
10310
|
useEditor.getState().getContinuation('fence') === 'freehand' &&
|
|
@@ -10185,14 +10316,30 @@ export function FloorplanPanel({
|
|
|
10185
10316
|
endFloorplanNavigation(event)
|
|
10186
10317
|
},
|
|
10187
10318
|
[
|
|
10319
|
+
draftStart,
|
|
10188
10320
|
emitFloorplanGridEvent,
|
|
10189
10321
|
endFloorplanNavigation,
|
|
10190
10322
|
getPlanPointFromClientPoint,
|
|
10323
|
+
handleBackgroundClick,
|
|
10191
10324
|
isFenceBuildActive,
|
|
10325
|
+
isWallBuildActive,
|
|
10192
10326
|
],
|
|
10193
10327
|
)
|
|
10194
10328
|
const handleBackgroundDoubleClick = useCallback(
|
|
10195
10329
|
(event: ReactMouseEvent<SVGSVGElement>) => {
|
|
10330
|
+
// A double-click ends a wall run as the 3D tool's detail-2 click does:
|
|
10331
|
+
// the walls already drawn stay, an open Polygon room is kept, and the
|
|
10332
|
+
// draft goes. The click path does this on its second click; this also
|
|
10333
|
+
// covers one the plan never routed there (the walls the run drew sit
|
|
10334
|
+
// under the pointer by then).
|
|
10335
|
+
if (isWallBuildActive) {
|
|
10336
|
+
if (draftStart) {
|
|
10337
|
+
commitWallPolygonDraft()
|
|
10338
|
+
clearWallPlacementDraft()
|
|
10339
|
+
setCursorPoint(null)
|
|
10340
|
+
}
|
|
10341
|
+
return
|
|
10342
|
+
}
|
|
10196
10343
|
if (!(isPolygonDraftBuildActive && !isRoofBuildActive)) {
|
|
10197
10344
|
return
|
|
10198
10345
|
}
|
|
@@ -10238,6 +10385,8 @@ export function FloorplanPanel({
|
|
|
10238
10385
|
},
|
|
10239
10386
|
[
|
|
10240
10387
|
activePolygonDraftPoints,
|
|
10388
|
+
clearWallPlacementDraft,
|
|
10389
|
+
draftStart,
|
|
10241
10390
|
emitFloorplanGridEvent,
|
|
10242
10391
|
handleCeilingPlacementConfirm,
|
|
10243
10392
|
getPlanPointFromClientPoint,
|
|
@@ -10246,8 +10395,10 @@ export function FloorplanPanel({
|
|
|
10246
10395
|
isCeilingBuildActive,
|
|
10247
10396
|
isPolygonDraftBuildActive,
|
|
10248
10397
|
isRoofBuildActive,
|
|
10398
|
+
isWallBuildActive,
|
|
10249
10399
|
isZoneBuildActive,
|
|
10250
10400
|
levelId,
|
|
10401
|
+
setCursorPoint,
|
|
10251
10402
|
],
|
|
10252
10403
|
)
|
|
10253
10404
|
|
|
@@ -11205,9 +11356,9 @@ export function FloorplanPanel({
|
|
|
11205
11356
|
isPanning || isRotatingFloorplan ? 'grabbing' : isSpacePanPressed ? 'grab' : null
|
|
11206
11357
|
const isFloorplanNavigationOverlayVisible = isSpacePanPressed || isPanning || isRotatingFloorplan
|
|
11207
11358
|
const pendingReferenceDisplayLength =
|
|
11208
|
-
parseReferenceScaleLength(referenceScaleValue, referenceScaleUnit) ?? Number.NaN
|
|
11359
|
+
parseReferenceScaleLength(referenceScaleValue, referenceScaleUnit, locale) ?? Number.NaN
|
|
11209
11360
|
const pendingReferenceRealLengthMeters =
|
|
11210
|
-
pendingReferenceScale && pendingReferenceDisplayLength
|
|
11361
|
+
pendingReferenceScale && isValidReferenceScaleDisplayLength(pendingReferenceDisplayLength)
|
|
11211
11362
|
? convertReferenceLengthToMeters(pendingReferenceDisplayLength, referenceScaleUnit)
|
|
11212
11363
|
: null
|
|
11213
11364
|
const pendingReferenceMetersPerUnit =
|
|
@@ -11218,17 +11369,14 @@ export function FloorplanPanel({
|
|
|
11218
11369
|
pendingReferenceScale && pendingReferenceRealLengthMeters
|
|
11219
11370
|
? pendingReferenceRealLengthMeters / pendingReferenceScale.measuredLengthUnits
|
|
11220
11371
|
: null
|
|
11221
|
-
const referenceScaleInputError =
|
|
11222
|
-
referenceScaleValue
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
: pendingReferenceDisplayLength > 0
|
|
11227
|
-
? null
|
|
11228
|
-
: 'Length must be greater than 0.'
|
|
11372
|
+
const referenceScaleInputError = getReferenceScaleInputError(
|
|
11373
|
+
referenceScaleValue,
|
|
11374
|
+
referenceScaleUnit,
|
|
11375
|
+
locale,
|
|
11376
|
+
)
|
|
11229
11377
|
const referenceScaleHint = referenceScaleInputError
|
|
11230
11378
|
? null
|
|
11231
|
-
: referenceScaleLengthHint(referenceScaleValue, referenceScaleUnit)
|
|
11379
|
+
: referenceScaleLengthHint(referenceScaleValue, referenceScaleUnit, locale)
|
|
11232
11380
|
return (
|
|
11233
11381
|
<div
|
|
11234
11382
|
className="pointer-events-auto flex h-full w-full flex-col overflow-hidden bg-background/95"
|
|
@@ -11383,8 +11531,8 @@ export function FloorplanPanel({
|
|
|
11383
11531
|
|
|
11384
11532
|
<div className="mt-3 rounded-lg bg-muted/45 px-3 py-2 text-muted-foreground text-xs">
|
|
11385
11533
|
{pendingReferenceImageScaleFactor
|
|
11386
|
-
? `Image will scale ${
|
|
11387
|
-
: 'Enter a length
|
|
11534
|
+
? `Image will scale ${formatFloorplanNumber(pendingReferenceImageScaleFactor, locale, 3)}x from the first point.`
|
|
11535
|
+
: 'Enter a valid length to preview the scale.'}
|
|
11388
11536
|
</div>
|
|
11389
11537
|
|
|
11390
11538
|
<div className="mt-4 flex justify-end gap-2">
|
|
@@ -11599,6 +11747,11 @@ export function FloorplanPanel({
|
|
|
11599
11747
|
`floorplan-wall-move-ghost-layer.tsx`. */}
|
|
11600
11748
|
<FloorplanWallMoveGhostLayer />
|
|
11601
11749
|
</g>
|
|
11750
|
+
{/* Dashed walls of the level below / above, over the plan but
|
|
11751
|
+
outside the measured group so they never change the fit. */}
|
|
11752
|
+
{activeDrawingType !== 'site-plan' && (
|
|
11753
|
+
<FloorplanAdjacentWallsLayer levelId={levelNode?.id} />
|
|
11754
|
+
)}
|
|
11602
11755
|
{activeDrawingType !== 'site-plan' && <FloorplanOpenWallEndsLayer />}
|
|
11603
11756
|
<FloorplanMeasurementToolLayer />
|
|
11604
11757
|
<FloorplanRegisteredToolLayer />
|
|
@@ -11614,7 +11767,7 @@ export function FloorplanPanel({
|
|
|
11614
11767
|
overlay during a free-translate drag. Sits above the
|
|
11615
11768
|
registry layer so the red lines and distance pills
|
|
11616
11769
|
paint on top of node geometry. */}
|
|
11617
|
-
<FloorplanAlignmentGuideLayer />
|
|
11770
|
+
<FloorplanAlignmentGuideLayer unitsPerPixel={floorplanUnitsPerPixel} />
|
|
11618
11771
|
|
|
11619
11772
|
{/* The property line, its vertex handles and its edge labels are
|
|
11620
11773
|
drawn in BUILDING-LOCAL metres (the frame the level's walls
|
|
@@ -11668,7 +11821,7 @@ export function FloorplanPanel({
|
|
|
11668
11821
|
{/* "Magnetic" wall-snap beacon — per-kind glyph at the active
|
|
11669
11822
|
draft / endpoint-move snap point. Same store + coord space as
|
|
11670
11823
|
the alignment guides. */}
|
|
11671
|
-
<FloorplanSnapBeaconLayer />
|
|
11824
|
+
<FloorplanSnapBeaconLayer unitsPerPixel={floorplanUnitsPerPixel} />
|
|
11672
11825
|
|
|
11673
11826
|
<FloorplanMarqueeOverlay cursorColor={palette.cursor} />
|
|
11674
11827
|
|