@pascal-app/editor 1.0.5 → 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 +20 -17
- 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
|
@@ -13,33 +13,92 @@ import { useFloorplanRender } from './floorplan-render-context'
|
|
|
13
13
|
*
|
|
14
14
|
* endpoint (corner) → square midpoint → triangle
|
|
15
15
|
* intersection → ✕ cross wall body (edge) → circle
|
|
16
|
+
* perpendicular → ⊥ corner projection guide → diamond
|
|
17
|
+
* grid length → small +
|
|
16
18
|
*
|
|
17
19
|
* Indigo to match the 3D beacon, except the corner (endpoint) square which is
|
|
18
|
-
* green to match the alignment guides.
|
|
19
|
-
*
|
|
20
|
+
* green to match the alignment guides. A point that carries a `label` (a wall
|
|
21
|
+
* pulled from a wall end) also names what it caught in a pill beside the
|
|
22
|
+
* glyph. Sizes are pixel-budgeted via `unitsPerPixel` so the marker stays a
|
|
20
23
|
* constant size on screen at any zoom. Mounted inside the `data-floorplan-scene`
|
|
21
24
|
* group so coordinates are world meters (XZ) 1:1, like the alignment guides.
|
|
22
25
|
*/
|
|
23
26
|
const COLOR = '#6366f1' // indigo-500 — matches the 3D beacon
|
|
24
27
|
const ENDPOINT_COLOR = '#22c55e' // green-500 — corner (endpoint) snap accent
|
|
25
28
|
|
|
26
|
-
export const FloorplanSnapBeaconLayer = memo(function FloorplanSnapBeaconLayer(
|
|
29
|
+
export const FloorplanSnapBeaconLayer = memo(function FloorplanSnapBeaconLayer({
|
|
30
|
+
unitsPerPixel,
|
|
31
|
+
}: {
|
|
32
|
+
/** The plan's live scale, which keeps a labelled marker screen-sized. */
|
|
33
|
+
unitsPerPixel?: number
|
|
34
|
+
}) {
|
|
27
35
|
const point = useWallSnapIndicator((s) => s.point)
|
|
28
36
|
const ctx = useFloorplanRender()
|
|
29
37
|
|
|
30
38
|
if (!point) return null
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
// A labelled point (a wall pulled from a wall end) is sized on screen.
|
|
41
|
+
const upp = (point.label ? unitsPerPixel : undefined) ?? ctx?.unitsPerPixel ?? 0.01
|
|
33
42
|
const m = 6 * upp // base half-size of the glyph in world meters
|
|
34
43
|
const stroke = 1.5 * upp
|
|
35
44
|
|
|
36
45
|
return (
|
|
37
46
|
<g pointerEvents="none">
|
|
38
47
|
<SnapMarker color={COLOR} kind={point.kind} m={m} stroke={stroke} x={point.x} z={point.z} />
|
|
48
|
+
{point.label ? (
|
|
49
|
+
<SnapLabel
|
|
50
|
+
color={point.kind === 'endpoint' ? ENDPOINT_COLOR : COLOR}
|
|
51
|
+
sceneRotationDeg={ctx?.sceneRotationDeg ?? 0}
|
|
52
|
+
text={point.label}
|
|
53
|
+
upp={upp}
|
|
54
|
+
x={point.x}
|
|
55
|
+
z={point.z}
|
|
56
|
+
/>
|
|
57
|
+
) : null}
|
|
39
58
|
</g>
|
|
40
59
|
)
|
|
41
60
|
})
|
|
42
61
|
|
|
62
|
+
/** What the snap caught, in a pill down and to the right of the glyph, kept upright. */
|
|
63
|
+
function SnapLabel({
|
|
64
|
+
color,
|
|
65
|
+
sceneRotationDeg,
|
|
66
|
+
text,
|
|
67
|
+
upp,
|
|
68
|
+
x,
|
|
69
|
+
z,
|
|
70
|
+
}: {
|
|
71
|
+
color: string
|
|
72
|
+
sceneRotationDeg: number
|
|
73
|
+
text: string
|
|
74
|
+
upp: number
|
|
75
|
+
x: number
|
|
76
|
+
z: number
|
|
77
|
+
}) {
|
|
78
|
+
const fontSize = 10.5 * upp
|
|
79
|
+
const padX = 5 * upp
|
|
80
|
+
const width = text.length * fontSize * 0.56 + padX * 2
|
|
81
|
+
const height = fontSize + 6 * upp
|
|
82
|
+
return (
|
|
83
|
+
<g
|
|
84
|
+
transform={`translate(${x} ${z}) rotate(${-sceneRotationDeg}) translate(${12 * upp} ${16 * upp})`}
|
|
85
|
+
>
|
|
86
|
+
<rect fill={color} height={height} rx={3 * upp} width={width} x={0} y={-height / 2} />
|
|
87
|
+
<text
|
|
88
|
+
dominantBaseline="middle"
|
|
89
|
+
fill="#ffffff"
|
|
90
|
+
fontFamily="-apple-system, system-ui, sans-serif"
|
|
91
|
+
fontSize={fontSize}
|
|
92
|
+
fontWeight={600}
|
|
93
|
+
x={padX}
|
|
94
|
+
y={0}
|
|
95
|
+
>
|
|
96
|
+
{text}
|
|
97
|
+
</text>
|
|
98
|
+
</g>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
43
102
|
function SnapMarker({
|
|
44
103
|
color,
|
|
45
104
|
kind,
|
|
@@ -72,6 +131,39 @@ function SnapMarker({
|
|
|
72
131
|
</g>
|
|
73
132
|
)
|
|
74
133
|
}
|
|
134
|
+
if (kind === 'perpendicular') {
|
|
135
|
+
// ⊥ — a base line with an upright from its middle, and the square corner.
|
|
136
|
+
const c = m * 1.3
|
|
137
|
+
return (
|
|
138
|
+
<g fill="none" stroke={color} strokeLinecap="round" strokeWidth={stroke * 1.6}>
|
|
139
|
+
<line x1={x - c} x2={x + c} y1={z} y2={z} />
|
|
140
|
+
<line x1={x} x2={x} y1={z} y2={z - c * 1.4} />
|
|
141
|
+
<polyline
|
|
142
|
+
points={`${x + c * 0.5},${z} ${x + c * 0.5},${z - c * 0.5} ${x},${z - c * 0.5}`}
|
|
143
|
+
/>
|
|
144
|
+
</g>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
if (kind === 'alignment') {
|
|
148
|
+
const c = m * 1.2
|
|
149
|
+
return (
|
|
150
|
+
<polygon
|
|
151
|
+
fill="#ffffff"
|
|
152
|
+
points={`${x},${z - c} ${x + c},${z} ${x},${z + c} ${x - c},${z}`}
|
|
153
|
+
stroke={color}
|
|
154
|
+
strokeWidth={stroke * 1.4}
|
|
155
|
+
/>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
if (kind === 'grid') {
|
|
159
|
+
const c = m * 0.9
|
|
160
|
+
return (
|
|
161
|
+
<g stroke={color} strokeLinecap="round" strokeWidth={stroke * 1.4}>
|
|
162
|
+
<line x1={x - c} x2={x + c} y1={z} y2={z} />
|
|
163
|
+
<line x1={x} x2={x} y1={z - c} y2={z + c} />
|
|
164
|
+
</g>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
75
167
|
// 'wall' (edge / along-wall) → circle
|
|
76
168
|
return <circle cx={x} cy={z} fill={color} r={m} />
|
|
77
169
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import type { AnyNode, AnyNodeId } from '@pascal-app/core'
|
|
4
|
-
import { useScene } from '@pascal-app/core'
|
|
4
|
+
import { useLocale, useScene } from '@pascal-app/core'
|
|
5
5
|
import { useViewer } from '@pascal-app/viewer'
|
|
6
6
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
7
7
|
import { createPortal } from 'react-dom'
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
resolveDimensionDrive,
|
|
12
12
|
} from '../../../lib/floorplan/dimension-drive'
|
|
13
13
|
import { formatScheduleLength } from '../../../lib/floorplan/schedules'
|
|
14
|
+
import { commitRoomPush } from '../../../lib/room-handle-drag'
|
|
14
15
|
import useEditor from '../../../store/use-editor'
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -20,7 +21,8 @@ import useEditor from '../../../store/use-editor'
|
|
|
20
21
|
* plate, pre-filled with the current text. Enter commits, Esc cancels.
|
|
21
22
|
* Committing DRIVES geometry: the anchor the dimension runs toward moves
|
|
22
23
|
* along the dimension direction by the delta, and connected wall junctions
|
|
23
|
-
* follow (`planDimensionDrive`)
|
|
24
|
+
* follow (`planDimensionDrive`); a dimension across a wall slides the whole
|
|
25
|
+
* wall, as its push arrow does (`commitRoomPush`). When nothing bindable is under the far
|
|
24
26
|
* anchor the value is written as `textOverride` on a construction-dimension
|
|
25
27
|
* node instead, and the label renders an "override" badge.
|
|
26
28
|
*
|
|
@@ -60,6 +62,32 @@ export function useDimensionEditAllowed(): boolean {
|
|
|
60
62
|
return useEditor((state) => isDimensionEditAllowed(state, { readOnly }))
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
/**
|
|
66
|
+
* The typeable dimension value under a press, with the node typing it drives:
|
|
67
|
+
* the pressed element's or, when the plan's dashed group box lies over it, the
|
|
68
|
+
* one beneath (a multi-selection's walls keep their dimensions inside it).
|
|
69
|
+
*/
|
|
70
|
+
export function typeableDimensionAt(
|
|
71
|
+
target: EventTarget | null,
|
|
72
|
+
clientX: number,
|
|
73
|
+
clientY: number,
|
|
74
|
+
): { label: Element; ownerNodeId: AnyNodeId } | null {
|
|
75
|
+
const resolve = (element: Element | null | undefined) => {
|
|
76
|
+
const hit = element?.closest?.('[data-floorplan-dimension-hit]')
|
|
77
|
+
const label = hit?.closest('[data-floorplan-annotation-label]')
|
|
78
|
+
const ownerNodeId = hit?.closest('[data-node-id]')?.getAttribute('data-node-id')
|
|
79
|
+
return label && ownerNodeId ? { label, ownerNodeId: ownerNodeId as AnyNodeId } : null
|
|
80
|
+
}
|
|
81
|
+
const element = target as Element | null
|
|
82
|
+
const direct = resolve(element)
|
|
83
|
+
if (direct || !element?.closest?.('[data-group-selection-box]')) return direct
|
|
84
|
+
for (const below of document.elementsFromPoint(clientX, clientY)) {
|
|
85
|
+
const found = resolve(below)
|
|
86
|
+
if (found) return found
|
|
87
|
+
}
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
|
|
63
91
|
type EditTarget = {
|
|
64
92
|
ownerNodeId: AnyNodeId
|
|
65
93
|
text: string
|
|
@@ -78,6 +106,7 @@ export function FloorplanDimensionEditOverlay(): React.ReactElement | null {
|
|
|
78
106
|
const [error, setError] = useState<string | null>(null)
|
|
79
107
|
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
80
108
|
const unit = useViewer((state) => state.unit)
|
|
109
|
+
const locale = useLocale()
|
|
81
110
|
|
|
82
111
|
useEffect(() => {
|
|
83
112
|
let down: { x: number; y: number } | null = null
|
|
@@ -92,13 +121,9 @@ export function FloorplanDimensionEditOverlay(): React.ReactElement | null {
|
|
|
92
121
|
return
|
|
93
122
|
}
|
|
94
123
|
if (!isDimensionEditAllowed(useEditor.getState(), useScene.getState())) return
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const label = hit.closest('[data-floorplan-annotation-label]')
|
|
99
|
-
const owner = hit.closest('[data-node-id]')
|
|
100
|
-
const ownerNodeId = owner?.getAttribute('data-node-id')
|
|
101
|
-
if (!label || !ownerNodeId) return
|
|
124
|
+
const found = typeableDimensionAt(event.target, event.clientX, event.clientY)
|
|
125
|
+
if (!found) return
|
|
126
|
+
const { label, ownerNodeId } = found
|
|
102
127
|
|
|
103
128
|
const value = number(label.getAttribute('data-floorplan-dimension-value'))
|
|
104
129
|
const witnessStart = point(
|
|
@@ -116,7 +141,7 @@ export function FloorplanDimensionEditOverlay(): React.ReactElement | null {
|
|
|
116
141
|
const box = label.getBoundingClientRect()
|
|
117
142
|
const text = label.getAttribute('data-floorplan-dimension-text') ?? ''
|
|
118
143
|
setTarget({
|
|
119
|
-
ownerNodeId
|
|
144
|
+
ownerNodeId,
|
|
120
145
|
text,
|
|
121
146
|
value,
|
|
122
147
|
witnessStart,
|
|
@@ -154,7 +179,7 @@ export function FloorplanDimensionEditOverlay(): React.ReactElement | null {
|
|
|
154
179
|
|
|
155
180
|
const commit = useCallback(() => {
|
|
156
181
|
if (!target) return
|
|
157
|
-
const nextLength = parseDimensionInput(draft, unit)
|
|
182
|
+
const nextLength = parseDimensionInput(draft, unit, locale)
|
|
158
183
|
if (nextLength === null) {
|
|
159
184
|
setError(`Cannot read "${draft.trim()}"`)
|
|
160
185
|
return
|
|
@@ -166,7 +191,7 @@ export function FloorplanDimensionEditOverlay(): React.ReactElement | null {
|
|
|
166
191
|
}
|
|
167
192
|
setTarget(null)
|
|
168
193
|
setError(null)
|
|
169
|
-
}, [draft, target, unit])
|
|
194
|
+
}, [draft, locale, target, unit])
|
|
170
195
|
|
|
171
196
|
if (!target) return null
|
|
172
197
|
if (typeof document === 'undefined') return null
|
|
@@ -246,6 +271,21 @@ export function commitDimensionValue(args: {
|
|
|
246
271
|
return { ok: true }
|
|
247
272
|
}
|
|
248
273
|
|
|
274
|
+
if (resolution.target.kind === 'wall-offset') {
|
|
275
|
+
// The wall's own push: the whole wall slides along its normal, connected
|
|
276
|
+
// walls follow through the junction planner, rooms and floors re-form —
|
|
277
|
+
// one undo step, exactly as dragging its side arrow that far.
|
|
278
|
+
const delta = args.nextLength - args.target.value
|
|
279
|
+
if (Math.abs(delta) < 1e-9) return { ok: true }
|
|
280
|
+
return commitRoomPush(
|
|
281
|
+
{ wallId: resolution.target.wallId, t0: 0, t1: 1 },
|
|
282
|
+
resolution.target.direction,
|
|
283
|
+
delta,
|
|
284
|
+
)
|
|
285
|
+
? { ok: true }
|
|
286
|
+
: { ok: false, reason: "Can't move this wall there" }
|
|
287
|
+
}
|
|
288
|
+
|
|
249
289
|
const plan = planDimensionDrive({
|
|
250
290
|
nodes,
|
|
251
291
|
target: resolution.target,
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
collectFloorplanLinkedLevelNodes,
|
|
25
25
|
computeAffectedSiblingIds,
|
|
26
26
|
floorplanAffordanceReshapeScope,
|
|
27
|
+
floorplanEntryStripsHandles,
|
|
27
28
|
floorplanEntryYieldsToTool,
|
|
28
29
|
floorplanHandleDoubleClickAffordance,
|
|
29
30
|
InteractiveGeometry,
|
|
@@ -162,7 +163,6 @@ describe('site-scoped floorplan discovery', () => {
|
|
|
162
163
|
siblingEpoch: 0,
|
|
163
164
|
unit: 'metric' as const,
|
|
164
165
|
metricNotation: 'meters' as const,
|
|
165
|
-
wallDimensionReference: 'finished-faces' as const,
|
|
166
166
|
visibilityRootId: site.id,
|
|
167
167
|
}
|
|
168
168
|
const committedSnapshot = structuredClone(overlay)
|
|
@@ -793,6 +793,17 @@ describe('floorplan entry routing while a tool is active', () => {
|
|
|
793
793
|
expect(floorplanEntryYieldsToTool({ mode: 'select', openingPlacement: false })).toBe(false)
|
|
794
794
|
expect(floorplanEntryYieldsToTool({ mode: 'delete', openingPlacement: false })).toBe(false)
|
|
795
795
|
})
|
|
796
|
+
|
|
797
|
+
// A roof plane selected before arming the roof tool kept its live corner and edge handles: a
|
|
798
|
+
// click near its eave resized it instead of drawing.
|
|
799
|
+
test('a selected node shows no edit handles while a build tool is armed', () => {
|
|
800
|
+
const entry = { multiSelected: false, ceilingWithoutSession: false }
|
|
801
|
+
expect(floorplanEntryStripsHandles({ ...entry, mode: 'select' })).toBe(false)
|
|
802
|
+
expect(floorplanEntryStripsHandles({ ...entry, mode: 'build' })).toBe(true)
|
|
803
|
+
expect(floorplanEntryStripsHandles({ ...entry, mode: 'select', multiSelected: true })).toBe(
|
|
804
|
+
true,
|
|
805
|
+
)
|
|
806
|
+
})
|
|
796
807
|
})
|
|
797
808
|
|
|
798
809
|
describe('isFloorplanHierarchyVisible', () => {
|