@pascal-app/editor 0.8.0 → 0.9.2
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 +15 -13
- package/src/components/editor/alignment-3d-guide-layer.tsx +168 -0
- package/src/components/editor/bake-exporter.tsx +47 -0
- package/src/components/editor/custom-camera-controls.tsx +795 -28
- package/src/components/editor/editor-layout-mobile.tsx +8 -3
- package/src/components/editor/editor-layout-v2.tsx +76 -51
- package/src/components/editor/export-manager.tsx +69 -32
- package/src/components/editor/fence-tangent-lines-3d.tsx +87 -0
- package/src/components/editor/first-person/build-collider-world.test.ts +182 -0
- package/src/components/editor/first-person/build-collider-world.ts +228 -83
- package/src/components/editor/first-person-controls.tsx +943 -27
- package/src/components/editor/floating-action-menu.tsx +676 -208
- package/src/components/editor/floating-building-action-menu.tsx +2 -2
- package/src/components/editor/floorplan-background-selection.test.ts +58 -0
- package/src/components/editor/floorplan-background-selection.ts +3 -2
- package/src/components/editor/floorplan-panel.tsx +6916 -12137
- package/src/components/editor/grid.tsx +197 -36
- package/src/components/editor/group-actions.ts +457 -0
- package/src/components/editor/group-floating-action-menu.tsx +130 -0
- package/src/components/editor/group-move-3d.ts +397 -0
- package/src/components/editor/group-rotate-handle.tsx +420 -0
- package/src/components/editor/group-selection-box-3d.tsx +173 -0
- package/src/components/editor/group-transform-shared.test.ts +396 -0
- package/src/components/editor/group-transform-shared.ts +489 -0
- package/src/components/editor/handles/handle-arrow.tsx +581 -0
- package/src/components/editor/handles/use-handle-drag.ts +246 -0
- package/src/components/editor/index.tsx +412 -162
- package/src/components/editor/measurement-pill.tsx +115 -0
- package/src/components/editor/node-action-menu.tsx +14 -1
- package/src/components/editor/node-arrow-handles.tsx +1646 -0
- package/src/components/editor/opening-guides-3d-layer.tsx +144 -0
- package/src/components/editor/quick-measurement-card.tsx +70 -0
- package/src/components/editor/quick-measurement-hud.tsx +28 -0
- package/src/components/editor/riser-diagram-panel.tsx +137 -0
- package/src/components/editor/selection-manager.tsx +1106 -483
- package/src/components/editor/site-edge-labels.tsx +45 -21
- package/src/components/editor/slab-hole-highlights.tsx +474 -0
- package/src/components/editor/snapshot-capture-overlay.tsx +369 -155
- package/src/components/editor/three-context-bridge.ts +22 -0
- package/src/components/editor/thumbnail-generator.tsx +140 -41
- package/src/components/editor/use-floorplan-background-placement.ts +190 -35
- package/src/components/editor/use-floorplan-hit-testing.ts +24 -0
- package/src/components/editor/use-floorplan-scene-data.ts +19 -7
- package/src/components/editor/use-mesh-settle-epoch.ts +26 -0
- package/src/components/editor/wall-measurement-label.tsx +14 -19
- package/src/components/editor/wall-move-side-handles.tsx +870 -0
- package/src/components/editor/wall-opening-highlights.tsx +181 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +304 -0
- package/src/components/editor-2d/floorplan-action-menu-layer.tsx +15 -6
- package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +142 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +5 -3
- package/src/components/editor-2d/floorplan-group-action-menu.tsx +87 -0
- package/src/components/editor-2d/floorplan-group-move.tsx +701 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +113 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +1656 -0
- package/src/components/editor-2d/floorplan-quick-measure-layer.tsx +212 -0
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +392 -0
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +857 -0
- package/src/components/editor-2d/floorplan-render-context.tsx +62 -0
- package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +77 -0
- package/src/components/editor-2d/floorplan-wall-move-ghost-layer.tsx +47 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +266 -0
- package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +15 -0
- package/src/components/editor-2d/renderers/floorplan-label-angle.ts +14 -0
- package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +108 -66
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +62 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +308 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +3325 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +24 -9
- package/src/components/editor-2d/renderers/floorplan-voronoi-layer.tsx +128 -0
- package/src/components/editor-2d/svg-paths.ts +10 -2
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +439 -57
- package/src/components/systems/ceiling/ceiling-system.tsx +109 -12
- package/src/components/systems/roof/roof-edit-system.tsx +1851 -22
- package/src/components/systems/selection-affordance-manager.tsx +38 -0
- package/src/components/systems/stair/stair-edit-system.tsx +1 -1
- package/src/components/systems/zone/zone-label-editor-system.tsx +14 -1
- package/src/components/systems/zone/zone-system.tsx +42 -13
- package/src/components/tools/elevator/elevator-defaults.ts +8 -0
- package/src/components/tools/elevator/elevator-tool.tsx +375 -0
- package/src/components/tools/elevator/move-elevator-tool.tsx +263 -0
- package/src/components/tools/fence/fence-drafting.ts +141 -27
- package/src/components/tools/item/move-tool.tsx +45 -105
- package/src/components/tools/item/placement-math.test.ts +22 -0
- package/src/components/tools/item/placement-math.ts +59 -7
- package/src/components/tools/item/placement-strategies.ts +432 -16
- package/src/components/tools/item/placement-types.ts +22 -1
- package/src/components/tools/item/use-draft-node.ts +51 -2
- package/src/components/tools/item/use-placement-coordinator.tsx +1399 -256
- package/src/components/tools/registry/move-registry-node-tool.tsx +1064 -0
- package/src/components/tools/roof/roof-tool.tsx +350 -28
- package/src/components/tools/select/box-select-state.ts +88 -0
- package/src/components/tools/select/box-select-tool.tsx +442 -520
- package/src/components/tools/select/marquee-geometry.test.ts +161 -0
- package/src/components/tools/select/marquee-geometry.ts +155 -0
- package/src/components/tools/select/plane-box-select-tool.tsx +570 -0
- package/src/components/tools/select/screen-rectangle-selection.ts +84 -0
- package/src/components/tools/select/select-candidates.test.ts +109 -0
- package/src/components/tools/select/select-candidates.ts +85 -0
- package/src/components/tools/shared/affordance-dispatch.ts +30 -0
- package/src/components/tools/shared/cursor-sphere.tsx +62 -26
- package/src/components/tools/shared/drag-bounding-box.tsx +195 -0
- package/src/components/tools/shared/facing-indicator.tsx +87 -0
- package/src/components/tools/shared/facing-pose-indicator.tsx +54 -0
- package/src/components/tools/shared/floor-stack-preview.ts +25 -0
- package/src/components/tools/shared/fresh-placement-visibility.ts +61 -0
- package/src/components/tools/shared/placement-box-geometry.ts +123 -0
- package/src/components/tools/shared/placement-box.tsx +305 -0
- package/src/components/tools/shared/polygon-editor.tsx +866 -175
- package/src/components/tools/shared/segment-angle.ts +56 -0
- package/src/components/tools/site/site-boundary-editor.tsx +520 -16
- package/src/components/tools/site/site-flag-model.ts +18 -0
- package/src/components/tools/stair/stair-defaults.ts +2 -1
- package/src/components/tools/stair/stair-tool.tsx +342 -37
- package/src/components/tools/tool-manager.tsx +239 -62
- package/src/components/tools/wall/wall-drafting.test.ts +330 -0
- package/src/components/tools/wall/wall-drafting.ts +248 -171
- package/src/components/tools/wall/wall-snap-geometry.test.ts +123 -0
- package/src/components/tools/wall/wall-snap-geometry.ts +331 -0
- package/src/components/tools/zone/zone-boundary-editor.tsx +5 -1
- package/src/components/tools/zone/zone-tool.tsx +86 -82
- package/src/components/ui/action-menu/action-button.tsx +21 -1
- package/src/components/ui/action-menu/camera-actions.tsx +24 -17
- package/src/components/ui/action-menu/control-modes.tsx +40 -170
- package/src/components/ui/action-menu/furnish-tools.tsx +5 -5
- package/src/components/ui/action-menu/index.tsx +3 -117
- package/src/components/ui/action-menu/measurement-control.tsx +188 -0
- package/src/components/ui/action-menu/structure-tools.tsx +27 -93
- package/src/components/ui/action-menu/view-toggles.tsx +279 -90
- package/src/components/ui/command-palette/editor-commands.tsx +20 -15
- package/src/components/ui/command-palette/index.tsx +5 -3
- package/src/components/ui/controls/material-paint-panel.tsx +152 -0
- package/src/components/ui/controls/material-picker.tsx +87 -177
- package/src/components/ui/controls/material-properties-editor.tsx +108 -0
- package/src/components/ui/controls/metric-control.tsx +142 -46
- package/src/components/ui/controls/scene-material-list.tsx +247 -0
- package/src/components/ui/controls/slider-control.tsx +88 -38
- package/src/components/ui/floating-level-selector.tsx +47 -10
- package/src/components/ui/helpers/building-helper.tsx +10 -23
- package/src/components/ui/helpers/contextual-helper-panel.tsx +428 -0
- package/src/components/ui/helpers/helper-manager.tsx +235 -23
- package/src/components/ui/helpers/item-helper.tsx +28 -32
- package/src/components/ui/helpers/registered-tool-helper.tsx +59 -0
- package/src/components/ui/helpers/roof-helper.tsx +10 -12
- package/src/components/ui/icon-ref.tsx +47 -0
- package/src/components/ui/item-catalog/catalog-items.tsx +40 -1
- package/src/components/ui/item-catalog/item-catalog.tsx +16 -36
- package/src/components/ui/level-duplicate-dialog.tsx +2 -1
- package/src/components/ui/panels/node-display.ts +17 -16
- package/src/components/ui/panels/panel-manager.tsx +55 -58
- package/src/components/ui/panels/panel-wrapper.tsx +238 -12
- package/src/components/ui/panels/parametric-inspector.tsx +482 -0
- package/src/components/ui/panels/reference-panel.tsx +54 -19
- package/src/components/ui/primitives/dropdown-menu.tsx +12 -8
- package/src/components/ui/primitives/shortcut-token.tsx +39 -3
- package/src/components/ui/primitives/sidebar.tsx +1 -0
- package/src/components/ui/scene-loader.tsx +1 -3
- package/src/components/ui/sidebar/app-sidebar.tsx +5 -1
- package/src/components/ui/sidebar/icon-rail.tsx +50 -31
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +263 -0
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +82 -8
- package/src/components/ui/sidebar/panels/plugins-panel.tsx +218 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +122 -41
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +66 -7
- package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +267 -0
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -1
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +9 -5
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +86 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +7 -2
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +92 -0
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +75 -0
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +3 -3
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +86 -0
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +42 -21
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +31 -17
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +140 -0
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +48 -8
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +132 -0
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +9 -5
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +86 -0
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +7 -1
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +112 -39
- package/src/components/ui/sidebar/panels/site-panel/tree-structure.ts +36 -0
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +7 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +3 -3
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +51 -2
- package/src/components/ui/sidebar/tab-bar.tsx +75 -1
- package/src/components/ui/sidebar/use-plugin-panels.tsx +132 -0
- package/src/components/ui/snap-target-badge.test.tsx +40 -0
- package/src/components/ui/snap-target-badge.tsx +88 -0
- package/src/components/viewer-overlay.tsx +253 -61
- package/src/components/viewer-zone-system.tsx +7 -2
- package/src/hooks/use-auto-save.test.ts +14 -0
- package/src/hooks/use-auto-save.ts +64 -10
- package/src/hooks/use-ceiling-events.ts +176 -0
- package/src/hooks/use-drag-action.ts +127 -0
- package/src/hooks/use-keyboard.ts +493 -95
- package/src/hooks/use-selection.ts +64 -0
- package/src/index.tsx +451 -6
- package/src/lib/active-placement-surface.ts +35 -0
- package/src/lib/ceiling-plan-snap.ts +24 -0
- package/src/lib/constants.ts +6 -2
- package/src/lib/contextual-help.test.ts +112 -0
- package/src/lib/contextual-help.ts +144 -0
- package/src/lib/continuation.ts +64 -0
- package/src/lib/direct-manipulation.test.ts +192 -0
- package/src/lib/direct-manipulation.ts +109 -0
- package/src/lib/editor-api.ts +50 -0
- package/src/lib/elevator-support.ts +96 -0
- package/src/lib/floorplan/apply-alignment.test.ts +25 -0
- package/src/lib/floorplan/apply-alignment.ts +117 -0
- package/src/lib/floorplan/floorplan-export.tsx +364 -0
- package/src/lib/floorplan/geometry.ts +53 -0
- package/src/lib/floorplan/index.ts +10 -0
- package/src/lib/floorplan/items.ts +5 -2
- package/src/lib/floorplan/plan-coords.ts +21 -0
- package/src/lib/floorplan/selection-tool.ts +40 -0
- package/src/lib/floorplan/stairs.ts +12 -1
- package/src/lib/fresh-planar-placement.test.ts +339 -0
- package/src/lib/fresh-planar-placement.ts +130 -0
- package/src/lib/glb-export.test.ts +441 -0
- package/src/lib/glb-export.ts +874 -0
- package/src/lib/history.ts +11 -1
- package/src/lib/interaction/hot-set.test.ts +133 -0
- package/src/lib/interaction/hot-set.ts +67 -0
- package/src/lib/interaction/overlay-policy.test.ts +51 -0
- package/src/lib/interaction/overlay-policy.ts +59 -0
- package/src/lib/interaction/scope.ts +211 -0
- package/src/lib/level-duplication.ts +54 -42
- package/src/lib/level-selection.ts +2 -2
- package/src/lib/material-paint.ts +227 -48
- package/src/lib/measurement-kind.test.ts +30 -0
- package/src/lib/measurement-kind.ts +19 -0
- package/src/lib/measurement-label.test.ts +47 -0
- package/src/lib/measurement-label.ts +68 -0
- package/src/lib/measurement-parser.ts +116 -0
- package/src/lib/measurements.test.ts +183 -0
- package/src/lib/measurements.ts +199 -0
- package/src/lib/paint-scope.test.ts +454 -0
- package/src/lib/paint-scope.ts +461 -0
- package/src/lib/placement-drag-release.ts +23 -0
- package/src/lib/placement-metadata.ts +29 -0
- package/src/lib/planar-cursor-placement.test.ts +100 -0
- package/src/lib/planar-cursor-placement.ts +42 -0
- package/src/lib/plugin-panels.test.ts +19 -0
- package/src/lib/plugin-panels.ts +91 -0
- package/src/lib/quick-measurement.test.ts +77 -0
- package/src/lib/quick-measurement.ts +109 -0
- package/src/lib/roof-duplication.ts +7 -7
- package/src/lib/roof-hover-outline-proxy.ts +22 -0
- package/src/lib/roof-wall-hit.ts +164 -0
- package/src/lib/scene-clipboard.test.ts +196 -0
- package/src/lib/scene-clipboard.ts +332 -0
- package/src/lib/scene.ts +40 -8
- package/src/lib/selection-routing.test.ts +298 -0
- package/src/lib/selection-routing.ts +175 -0
- package/src/lib/sfx/index.ts +1 -0
- package/src/lib/sfx/movement-tick.test.ts +65 -0
- package/src/lib/sfx/movement-tick.ts +18 -0
- package/src/lib/sfx-bus.ts +69 -10
- package/src/lib/sfx-player.test.ts +124 -0
- package/src/lib/sfx-player.ts +148 -55
- package/src/lib/site-boundary.ts +1 -0
- package/src/lib/slab-plan-snap.test.ts +93 -0
- package/src/lib/slab-plan-snap.ts +108 -0
- package/src/lib/snapping-mode.test.ts +138 -0
- package/src/lib/snapping-mode.ts +179 -0
- package/src/lib/stair-duplication.ts +4 -4
- package/src/lib/stair-levels.test.ts +146 -0
- package/src/lib/stair-levels.ts +177 -0
- package/src/lib/surface-plan-snap.test.ts +71 -0
- package/src/lib/surface-plan-snap.ts +256 -0
- package/src/lib/use-linear-display.ts +40 -0
- package/src/lib/world-grid-snap.ts +231 -0
- package/src/lib/zone-content.ts +130 -0
- package/src/store/use-alignment-guides.ts +21 -0
- package/src/store/use-direct-manipulation-feedback.ts +20 -0
- package/src/store/use-editor.tsx +767 -114
- package/src/store/use-facing-pose.ts +44 -0
- package/src/store/use-fence-curve-draft.ts +21 -0
- package/src/store/use-floorplan-draft-preview.ts +101 -0
- package/src/store/use-floorplan-marquee.ts +50 -0
- package/src/store/use-interaction-scope.test.ts +186 -0
- package/src/store/use-interaction-scope.ts +132 -0
- package/src/store/use-measurement-draft.test.ts +530 -0
- package/src/store/use-measurement-draft.ts +543 -0
- package/src/store/use-opening-guides.ts +41 -0
- package/src/store/use-placement-preview.ts +39 -0
- package/src/store/use-quick-measurement-hud.test.ts +53 -0
- package/src/store/use-quick-measurement-hud.ts +77 -0
- package/src/store/use-segment-draft-chain.ts +28 -0
- package/src/store/use-stair-build-preview.ts +43 -0
- package/src/store/use-wall-move-ghosts.ts +36 -0
- package/src/store/use-wall-snap-indicator.ts +34 -0
- package/src/components/editor/first-person/bvh-ecctrl.tsx +0 -860
- package/src/components/editor/preset-thumbnail-generator.tsx +0 -125
- package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +0 -113
- package/src/components/tools/building/move-building-tool.tsx +0 -157
- package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +0 -43
- package/src/components/tools/ceiling/ceiling-hole-editor.tsx +0 -49
- package/src/components/tools/ceiling/ceiling-tool.tsx +0 -465
- package/src/components/tools/ceiling/move-ceiling-tool.tsx +0 -264
- package/src/components/tools/column/column-tool.tsx +0 -97
- package/src/components/tools/column/move-column-tool.tsx +0 -105
- package/src/components/tools/door/door-math.ts +0 -110
- package/src/components/tools/door/door-tool.tsx +0 -324
- package/src/components/tools/door/move-door-tool.tsx +0 -412
- package/src/components/tools/fence/curve-fence-tool.tsx +0 -178
- package/src/components/tools/fence/fence-tool.tsx +0 -346
- package/src/components/tools/fence/move-fence-endpoint-tool.tsx +0 -441
- package/src/components/tools/fence/move-fence-tool.tsx +0 -302
- package/src/components/tools/item/item-tool.tsx +0 -26
- package/src/components/tools/roof/move-roof-tool.tsx +0 -364
- package/src/components/tools/slab/move-slab-tool.tsx +0 -182
- package/src/components/tools/slab/slab-boundary-editor.tsx +0 -43
- package/src/components/tools/slab/slab-hole-editor.tsx +0 -49
- package/src/components/tools/slab/slab-tool.tsx +0 -322
- package/src/components/tools/spawn/move-spawn-tool.tsx +0 -101
- package/src/components/tools/spawn/spawn-tool.tsx +0 -130
- package/src/components/tools/wall/curve-wall-tool.tsx +0 -178
- package/src/components/tools/wall/move-wall-endpoint-tool.tsx +0 -426
- package/src/components/tools/wall/move-wall-tool.tsx +0 -358
- package/src/components/tools/wall/wall-tool.tsx +0 -332
- package/src/components/tools/window/move-window-tool.tsx +0 -447
- package/src/components/tools/window/window-math.ts +0 -117
- package/src/components/tools/window/window-tool.tsx +0 -332
- package/src/components/ui/helpers/ceiling-helper.tsx +0 -20
- package/src/components/ui/helpers/slab-helper.tsx +0 -20
- package/src/components/ui/helpers/wall-helper.tsx +0 -20
- package/src/components/ui/panels/ceiling-panel.tsx +0 -249
- package/src/components/ui/panels/column-panel.tsx +0 -759
- package/src/components/ui/panels/door-panel.tsx +0 -1299
- package/src/components/ui/panels/fence-panel.tsx +0 -222
- package/src/components/ui/panels/item-panel.tsx +0 -306
- package/src/components/ui/panels/paint-panel.tsx +0 -163
- package/src/components/ui/panels/presets/presets-popover.tsx +0 -511
- package/src/components/ui/panels/roof-panel.tsx +0 -280
- package/src/components/ui/panels/roof-segment-panel.tsx +0 -311
- package/src/components/ui/panels/slab-panel.tsx +0 -250
- package/src/components/ui/panels/spawn-panel.tsx +0 -161
- package/src/components/ui/panels/stair-panel.tsx +0 -577
- package/src/components/ui/panels/stair-segment-panel.tsx +0 -325
- package/src/components/ui/panels/wall-panel.tsx +0 -273
- package/src/components/ui/panels/window-panel.tsx +0 -968
- package/src/contexts/presets-context.tsx +0 -121
- package/src/hooks/use-contextual-tools.ts +0 -61
|
@@ -2,20 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
import { emitter, type FenceNode, isCurvedWall, type WallNode } from '@pascal-app/core'
|
|
4
4
|
import { type MouseEvent as ReactMouseEvent, useCallback } from 'react'
|
|
5
|
-
import {
|
|
5
|
+
import { resolveCeilingPlanPointSnap } from '../../lib/ceiling-plan-snap'
|
|
6
|
+
import { alignFloorplanDraftPoint, getPlanPointDistance } from '../../lib/floorplan'
|
|
7
|
+
import { resolveSlabPlanPointSnap } from '../../lib/slab-plan-snap'
|
|
8
|
+
import useAlignmentGuides from '../../store/use-alignment-guides'
|
|
9
|
+
import useEditor, { isAngleSnapActive, isMagneticSnapActive } from '../../store/use-editor'
|
|
10
|
+
import usePlacementPreview from '../../store/use-placement-preview'
|
|
11
|
+
import useSegmentDraftChain from '../../store/use-segment-draft-chain'
|
|
6
12
|
import { snapFenceDraftPoint } from '../tools/fence/fence-drafting'
|
|
7
|
-
import type
|
|
13
|
+
import { getSegmentGridStep, type WallPlanPoint } from '../tools/wall/wall-drafting'
|
|
8
14
|
|
|
9
15
|
type UseFloorplanBackgroundPlacementArgs = {
|
|
10
16
|
activePolygonDraftPoints: WallPlanPoint[]
|
|
11
17
|
ceilingDraftPoints: WallPlanPoint[]
|
|
12
18
|
clearFencePlacementDraft: () => void
|
|
13
19
|
clearRoofPlacementDraft: () => void
|
|
20
|
+
clearWallPlacementDraft: () => void
|
|
14
21
|
emitFloorplanGridEvent: (
|
|
15
22
|
type: 'click' | 'double-click' | 'move',
|
|
16
23
|
planPoint: WallPlanPoint,
|
|
17
24
|
event: ReactMouseEvent<SVGSVGElement>,
|
|
18
|
-
) =>
|
|
25
|
+
) => void
|
|
19
26
|
fenceDraftStart: WallPlanPoint | null
|
|
20
27
|
fences: FenceNode[]
|
|
21
28
|
findClosestWallPoint: (
|
|
@@ -30,11 +37,16 @@ type UseFloorplanBackgroundPlacementArgs = {
|
|
|
30
37
|
} | null
|
|
31
38
|
floorplanOpeningLocalY: number
|
|
32
39
|
getSnappedFloorplanPoint: (point: WallPlanPoint) => WallPlanPoint
|
|
40
|
+
handleCeilingItemPlacementClick: (
|
|
41
|
+
planPoint: WallPlanPoint,
|
|
42
|
+
nativeEvent: ReactMouseEvent<SVGSVGElement>,
|
|
43
|
+
) => boolean
|
|
33
44
|
handleCeilingPlacementPoint: (point: WallPlanPoint) => void
|
|
34
45
|
handleSlabPlacementPoint: (point: WallPlanPoint) => void
|
|
35
46
|
handleWallPlacementPoint: (point: WallPlanPoint) => void
|
|
36
47
|
handleZonePlacementPoint: (point: WallPlanPoint) => void
|
|
37
48
|
isCeilingBuildActive: boolean
|
|
49
|
+
isCeilingItemPlacementActive: boolean
|
|
38
50
|
isFenceBuildActive: boolean
|
|
39
51
|
isFloorplanGridInteractionActive: boolean
|
|
40
52
|
isOpeningPlacementActive: boolean
|
|
@@ -42,18 +54,21 @@ type UseFloorplanBackgroundPlacementArgs = {
|
|
|
42
54
|
isRoofBuildActive: boolean
|
|
43
55
|
isWallBuildActive: boolean
|
|
44
56
|
isZoneBuildActive: boolean
|
|
57
|
+
levelId: string | null
|
|
45
58
|
roofDraftStart: WallPlanPoint | null
|
|
46
59
|
setCursorPoint: React.Dispatch<React.SetStateAction<WallPlanPoint | null>>
|
|
47
60
|
setFenceDraftEnd: React.Dispatch<React.SetStateAction<WallPlanPoint | null>>
|
|
48
61
|
setFenceDraftStart: React.Dispatch<React.SetStateAction<WallPlanPoint | null>>
|
|
49
62
|
setRoofDraftEnd: React.Dispatch<React.SetStateAction<WallPlanPoint | null>>
|
|
50
63
|
setRoofDraftStart: React.Dispatch<React.SetStateAction<WallPlanPoint | null>>
|
|
51
|
-
shiftPressed: boolean
|
|
52
64
|
snapWallDraftPoint: (args: {
|
|
53
65
|
point: WallPlanPoint
|
|
54
66
|
walls: WallNode[]
|
|
55
67
|
start?: WallPlanPoint
|
|
56
|
-
angleSnap
|
|
68
|
+
angleSnap?: boolean
|
|
69
|
+
bypassSnap?: boolean
|
|
70
|
+
step?: number
|
|
71
|
+
gridSnap?: (point: WallPlanPoint) => WallPlanPoint
|
|
57
72
|
}) => WallPlanPoint
|
|
58
73
|
snapPolygonDraftPoint: (args: {
|
|
59
74
|
point: WallPlanPoint
|
|
@@ -62,6 +77,13 @@ type UseFloorplanBackgroundPlacementArgs = {
|
|
|
62
77
|
}) => WallPlanPoint
|
|
63
78
|
toPoint2D: (point: WallPlanPoint) => { x: number; y: number }
|
|
64
79
|
walls: WallNode[]
|
|
80
|
+
/**
|
|
81
|
+
* Snap a building-local plan point to the world XZ grid at `step`.
|
|
82
|
+
* Injected so the hook doesn't have to know the building's rotation
|
|
83
|
+
* or position — used by wall / fence branches that snap at variable
|
|
84
|
+
* step.
|
|
85
|
+
*/
|
|
86
|
+
worldGridSnap: (point: WallPlanPoint, step: number) => WallPlanPoint
|
|
65
87
|
}
|
|
66
88
|
|
|
67
89
|
export function useFloorplanBackgroundPlacement({
|
|
@@ -69,17 +91,20 @@ export function useFloorplanBackgroundPlacement({
|
|
|
69
91
|
ceilingDraftPoints,
|
|
70
92
|
clearFencePlacementDraft,
|
|
71
93
|
clearRoofPlacementDraft,
|
|
94
|
+
clearWallPlacementDraft,
|
|
72
95
|
emitFloorplanGridEvent,
|
|
73
96
|
fenceDraftStart,
|
|
74
97
|
fences,
|
|
75
98
|
findClosestWallPoint,
|
|
76
99
|
floorplanOpeningLocalY,
|
|
77
100
|
getSnappedFloorplanPoint,
|
|
101
|
+
handleCeilingItemPlacementClick,
|
|
78
102
|
handleCeilingPlacementPoint,
|
|
79
103
|
handleSlabPlacementPoint,
|
|
80
104
|
handleWallPlacementPoint,
|
|
81
105
|
handleZonePlacementPoint,
|
|
82
106
|
isCeilingBuildActive,
|
|
107
|
+
isCeilingItemPlacementActive,
|
|
83
108
|
isFenceBuildActive,
|
|
84
109
|
isFloorplanGridInteractionActive,
|
|
85
110
|
isOpeningPlacementActive,
|
|
@@ -87,17 +112,18 @@ export function useFloorplanBackgroundPlacement({
|
|
|
87
112
|
isRoofBuildActive,
|
|
88
113
|
isWallBuildActive,
|
|
89
114
|
isZoneBuildActive,
|
|
115
|
+
levelId,
|
|
90
116
|
roofDraftStart,
|
|
91
117
|
setCursorPoint,
|
|
92
118
|
setFenceDraftEnd,
|
|
93
119
|
setFenceDraftStart,
|
|
94
120
|
setRoofDraftEnd,
|
|
95
121
|
setRoofDraftStart,
|
|
96
|
-
shiftPressed,
|
|
97
122
|
snapWallDraftPoint,
|
|
98
123
|
snapPolygonDraftPoint,
|
|
99
124
|
toPoint2D,
|
|
100
125
|
walls,
|
|
126
|
+
worldGridSnap,
|
|
101
127
|
}: UseFloorplanBackgroundPlacementArgs) {
|
|
102
128
|
const handleBackgroundPlacementClick = useCallback(
|
|
103
129
|
(
|
|
@@ -123,24 +149,42 @@ export function useFloorplanBackgroundPlacement({
|
|
|
123
149
|
stopPropagation: () => {},
|
|
124
150
|
} as any)
|
|
125
151
|
}
|
|
152
|
+
// Drop the off-wall ghost on commit so it doesn't linger at the
|
|
153
|
+
// just-placed spot before the next pointer move re-evaluates.
|
|
154
|
+
usePlacementPreview.getState().clear()
|
|
126
155
|
return true
|
|
127
156
|
}
|
|
128
157
|
|
|
129
158
|
if (isCeilingBuildActive) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
159
|
+
// Align the committed vertex the same way the move-preview did, so the
|
|
160
|
+
// placed point matches what the user saw — mode-driven (the chip):
|
|
161
|
+
// `grid` quantizes, `angles` locks 15° rays, `lines` snaps onto walls /
|
|
162
|
+
// alignment, `off` is free. Alt remains force/free at commit time.
|
|
163
|
+
const angleSnap = ceilingDraftPoints.length > 0 && isAngleSnapActive()
|
|
164
|
+
const fallbackPoint = snapPolygonDraftPoint({
|
|
133
165
|
point: planPoint,
|
|
134
166
|
start: ceilingDraftPoints[ceilingDraftPoints.length - 1],
|
|
135
|
-
angleSnap
|
|
167
|
+
angleSnap,
|
|
136
168
|
})
|
|
169
|
+
const snappedPoint = resolveCeilingPlanPointSnap({
|
|
170
|
+
rawPoint: planPoint,
|
|
171
|
+
fallbackPoint,
|
|
172
|
+
levelId,
|
|
173
|
+
align: !angleSnap,
|
|
174
|
+
}).point
|
|
137
175
|
|
|
176
|
+
emitFloorplanGridEvent('click', snappedPoint, event)
|
|
138
177
|
handleCeilingPlacementPoint(snappedPoint)
|
|
139
178
|
return true
|
|
140
179
|
}
|
|
141
180
|
|
|
142
181
|
if (isRoofBuildActive) {
|
|
143
|
-
|
|
182
|
+
// Footprint placement (polygon context: grid / lines / off, no angle),
|
|
183
|
+
// mode-driven to match the chip. Alt is force/free at commit time;
|
|
184
|
+
// alignment display/pull follows the active magnetic mode.
|
|
185
|
+
const snappedPoint = alignFloorplanDraftPoint(getSnappedFloorplanPoint(planPoint), {
|
|
186
|
+
applySnap: isMagneticSnapActive(),
|
|
187
|
+
})
|
|
144
188
|
emitFloorplanGridEvent('click', snappedPoint, event)
|
|
145
189
|
setCursorPoint(snappedPoint)
|
|
146
190
|
|
|
@@ -154,43 +198,95 @@ export function useFloorplanBackgroundPlacement({
|
|
|
154
198
|
}
|
|
155
199
|
|
|
156
200
|
if (isFenceBuildActive) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
201
|
+
// Fence draft: mode-driven (matches the chip), same as the move
|
|
202
|
+
// preview. `grid` snaps to the world XZ grid (rotation-safe via the
|
|
203
|
+
// `gridSnap` callback), `angles` locks 15° rays from the start, `lines`
|
|
204
|
+
// pulls onto walls / fences / alignment, `off` is free.
|
|
205
|
+
const fenceStep = getSegmentGridStep()
|
|
206
|
+
const fenceAngleSnap = fenceDraftStart !== null && isAngleSnapActive()
|
|
207
|
+
const fenceSnapped = snapFenceDraftPoint({
|
|
160
208
|
point: planPoint,
|
|
161
209
|
walls,
|
|
162
210
|
fences,
|
|
163
211
|
start: fenceDraftStart ?? undefined,
|
|
164
|
-
angleSnap:
|
|
212
|
+
angleSnap: fenceAngleSnap,
|
|
213
|
+
magnetic: isMagneticSnapActive(),
|
|
214
|
+
gridSnap: (p) => worldGridSnap(p, fenceStep),
|
|
165
215
|
})
|
|
216
|
+
const fenceGridBase = worldGridSnap(planPoint, fenceStep)
|
|
217
|
+
const fenceLocked =
|
|
218
|
+
fenceSnapped[0] !== fenceGridBase[0] || fenceSnapped[1] !== fenceGridBase[1]
|
|
219
|
+
const snappedPoint = fenceLocked
|
|
220
|
+
? fenceSnapped
|
|
221
|
+
: alignFloorplanDraftPoint(fenceSnapped, {
|
|
222
|
+
applySnap: isMagneticSnapActive() && !fenceAngleSnap,
|
|
223
|
+
})
|
|
166
224
|
|
|
225
|
+
emitFloorplanGridEvent('click', snappedPoint, event)
|
|
167
226
|
setCursorPoint(snappedPoint)
|
|
168
227
|
|
|
228
|
+
// Double-click finishes the chain. The emit above already made the
|
|
229
|
+
// 3D fence tool stopDrafting (its detail >= 2 guard), so close the
|
|
230
|
+
// 2D draft too — leaving it open desyncs the two views.
|
|
231
|
+
if (fenceDraftStart && event.detail >= 2) {
|
|
232
|
+
clearFencePlacementDraft()
|
|
233
|
+
return true
|
|
234
|
+
}
|
|
235
|
+
|
|
169
236
|
if (!fenceDraftStart) {
|
|
170
237
|
setFenceDraftStart(snappedPoint)
|
|
171
238
|
setFenceDraftEnd(snappedPoint)
|
|
172
239
|
} else if (
|
|
173
240
|
getPlanPointDistance(toPoint2D(fenceDraftStart), toPoint2D(snappedPoint)) >= 0.01
|
|
174
241
|
) {
|
|
175
|
-
|
|
242
|
+
// Single mode commits one segment per click: the same emit above
|
|
243
|
+
// already made the 3D fence tool stopDrafting, so close the 2D
|
|
244
|
+
// draft too instead of chaining.
|
|
245
|
+
if (useEditor.getState().getContinuation('fence') === 'single') {
|
|
246
|
+
clearFencePlacementDraft()
|
|
247
|
+
setCursorPoint(snappedPoint)
|
|
248
|
+
return true
|
|
249
|
+
}
|
|
250
|
+
// The 3D fence tool owns creation and keeps chaining from the
|
|
251
|
+
// committed fence's resolved end — chain the 2D draft from the
|
|
252
|
+
// same published point so both views draft the next segment
|
|
253
|
+
// from the same start.
|
|
254
|
+
const nextStart = useSegmentDraftChain.getState().fence ?? snappedPoint
|
|
255
|
+
setFenceDraftStart(nextStart)
|
|
256
|
+
setFenceDraftEnd(nextStart)
|
|
176
257
|
} else {
|
|
177
258
|
setFenceDraftEnd(snappedPoint)
|
|
178
259
|
}
|
|
179
260
|
return true
|
|
180
261
|
}
|
|
181
262
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
263
|
+
// Slab / zone polygon build — local draft state + grid emit.
|
|
264
|
+
// Must run BEFORE the `isFloorplanGridInteractionActive` catch-all
|
|
265
|
+
// (since slab is registry-driven, the catch-all would otherwise
|
|
266
|
+
// swallow the click and skip local draft state updates — leaving
|
|
267
|
+
// the 2D draft polygon invisible while the 3D tool builds fine).
|
|
188
268
|
if (isPolygonBuildActive) {
|
|
189
|
-
const
|
|
269
|
+
const angleSnap = activePolygonDraftPoints.length > 0 && isAngleSnapActive()
|
|
270
|
+
const fallbackPoint = snapPolygonDraftPoint({
|
|
190
271
|
point: planPoint,
|
|
191
272
|
start: activePolygonDraftPoints[activePolygonDraftPoints.length - 1],
|
|
192
|
-
angleSnap
|
|
273
|
+
angleSnap,
|
|
193
274
|
})
|
|
275
|
+
// Zone shares the slab surface snap (wall corners / midpoints /
|
|
276
|
+
// crossings + alignment) — it's the same polygon-on-a-level draw.
|
|
277
|
+
const snappedPoint = resolveSlabPlanPointSnap({
|
|
278
|
+
rawPoint: planPoint,
|
|
279
|
+
fallbackPoint,
|
|
280
|
+
levelId,
|
|
281
|
+
align: !angleSnap,
|
|
282
|
+
}).point
|
|
283
|
+
|
|
284
|
+
// Emit the grid event so the registry-driven slab tool also
|
|
285
|
+
// sees the click (parity with ceiling / fence / roof branches
|
|
286
|
+
// above). Zone has no registry tool — emit-or-not is irrelevant.
|
|
287
|
+
if (!isZoneBuildActive) {
|
|
288
|
+
emitFloorplanGridEvent('click', snappedPoint, event)
|
|
289
|
+
}
|
|
194
290
|
|
|
195
291
|
if (isZoneBuildActive) {
|
|
196
292
|
handleZonePlacementPoint(snappedPoint)
|
|
@@ -200,35 +296,93 @@ export function useFloorplanBackgroundPlacement({
|
|
|
200
296
|
return true
|
|
201
297
|
}
|
|
202
298
|
|
|
203
|
-
|
|
204
|
-
|
|
299
|
+
// Wall placement — local draft state + grid emit. Same reasoning
|
|
300
|
+
// as slab above: wall is registry-driven, so without this branch
|
|
301
|
+
// the catch-all would swallow the click and the local draftStart
|
|
302
|
+
// / draftEnd state in the floor plan would never update, leaving
|
|
303
|
+
// the dashed-line draft preview invisible.
|
|
304
|
+
if (isWallBuildActive) {
|
|
305
|
+
// Wall draft: mode-driven (matches the chip + the move-preview branch).
|
|
306
|
+
// `grid` snaps to the world XZ grid (rotation-safe via `gridSnap`),
|
|
307
|
+
// `angles` locks 15° rays from the start, `lines` pulls the endpoint
|
|
308
|
+
// onto existing wall corners / edges + alignment, `off` is free.
|
|
309
|
+
const wallStep = getSegmentGridStep()
|
|
310
|
+
const wallAngleSnap = draftStart !== null && isAngleSnapActive()
|
|
311
|
+
const wallSnapped = snapWallDraftPoint({
|
|
312
|
+
point: planPoint,
|
|
313
|
+
walls,
|
|
314
|
+
start: draftStart ?? undefined,
|
|
315
|
+
angleSnap: wallAngleSnap,
|
|
316
|
+
gridSnap: (p) => worldGridSnap(p, wallStep),
|
|
317
|
+
})
|
|
318
|
+
const wallGridBase = worldGridSnap(planPoint, wallStep)
|
|
319
|
+
const wallLocked = wallSnapped[0] !== wallGridBase[0] || wallSnapped[1] !== wallGridBase[1]
|
|
320
|
+
let snappedPoint = wallSnapped
|
|
321
|
+
if (wallLocked) {
|
|
322
|
+
useAlignmentGuides.getState().clear()
|
|
323
|
+
} else {
|
|
324
|
+
// Alignment lines are shown in every mode; the pull applies only when
|
|
325
|
+
// magnetic ('lines') and the segment isn't angle-locked.
|
|
326
|
+
snappedPoint = alignFloorplanDraftPoint(wallSnapped, {
|
|
327
|
+
applySnap: isMagneticSnapActive() && !wallAngleSnap,
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
emitFloorplanGridEvent('click', snappedPoint, event)
|
|
332
|
+
|
|
333
|
+
// Double-click finishes the chain. The emit above already made the
|
|
334
|
+
// 3D wall tool stopDrafting (its detail >= 2 guard), so close the
|
|
335
|
+
// 2D draft too — otherwise it stays open against a closed 3D tool
|
|
336
|
+
// and the next previewed segment is silently never created.
|
|
337
|
+
if (draftStart && event.detail >= 2) {
|
|
338
|
+
clearWallPlacementDraft()
|
|
339
|
+
setCursorPoint(snappedPoint)
|
|
340
|
+
return true
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
handleWallPlacementPoint(snappedPoint)
|
|
344
|
+
return true
|
|
205
345
|
}
|
|
206
346
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
347
|
+
// Ceiling-attached item placement (lights, fans). Routes the click
|
|
348
|
+
// through `ceiling:click` instead of `grid:click` so the placement
|
|
349
|
+
// strategy parents the new item to the ceiling at the correct
|
|
350
|
+
// height — mirrors the pointer-move handler in `floorplan-panel`.
|
|
351
|
+
if (isCeilingItemPlacementActive) {
|
|
352
|
+
handleCeilingItemPlacementClick(planPoint, event)
|
|
353
|
+
return true
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Generic catch-all — registry-driven tool whose kind has no
|
|
357
|
+
// local floor-plan draft handler (column / spawn / shelf / etc.).
|
|
358
|
+
// The tool's `grid:click` subscriber owns the placement.
|
|
359
|
+
if (isFloorplanGridInteractionActive) {
|
|
360
|
+
const snappedPoint = getSnappedFloorplanPoint(planPoint)
|
|
361
|
+
emitFloorplanGridEvent('click', snappedPoint, event)
|
|
362
|
+
setCursorPoint(snappedPoint)
|
|
363
|
+
return true
|
|
364
|
+
}
|
|
213
365
|
|
|
214
|
-
|
|
215
|
-
return true
|
|
366
|
+
return false
|
|
216
367
|
},
|
|
217
368
|
[
|
|
218
369
|
activePolygonDraftPoints,
|
|
219
370
|
ceilingDraftPoints,
|
|
220
371
|
clearFencePlacementDraft,
|
|
221
372
|
clearRoofPlacementDraft,
|
|
373
|
+
clearWallPlacementDraft,
|
|
222
374
|
emitFloorplanGridEvent,
|
|
223
375
|
fenceDraftStart,
|
|
224
376
|
fences,
|
|
225
377
|
findClosestWallPoint,
|
|
226
378
|
floorplanOpeningLocalY,
|
|
227
379
|
getSnappedFloorplanPoint,
|
|
380
|
+
handleCeilingItemPlacementClick,
|
|
228
381
|
handleCeilingPlacementPoint,
|
|
229
382
|
handleSlabPlacementPoint,
|
|
230
383
|
handleZonePlacementPoint,
|
|
231
384
|
isCeilingBuildActive,
|
|
385
|
+
isCeilingItemPlacementActive,
|
|
232
386
|
isFenceBuildActive,
|
|
233
387
|
isFloorplanGridInteractionActive,
|
|
234
388
|
isOpeningPlacementActive,
|
|
@@ -236,18 +390,19 @@ export function useFloorplanBackgroundPlacement({
|
|
|
236
390
|
isRoofBuildActive,
|
|
237
391
|
isWallBuildActive,
|
|
238
392
|
isZoneBuildActive,
|
|
393
|
+
levelId,
|
|
239
394
|
roofDraftStart,
|
|
240
395
|
setCursorPoint,
|
|
241
396
|
setFenceDraftEnd,
|
|
242
397
|
setFenceDraftStart,
|
|
243
398
|
setRoofDraftEnd,
|
|
244
399
|
setRoofDraftStart,
|
|
245
|
-
shiftPressed,
|
|
246
400
|
snapWallDraftPoint,
|
|
247
401
|
snapPolygonDraftPoint,
|
|
248
402
|
toPoint2D,
|
|
249
403
|
walls,
|
|
250
404
|
handleWallPlacementPoint,
|
|
405
|
+
worldGridSnap,
|
|
251
406
|
],
|
|
252
407
|
)
|
|
253
408
|
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
import type {
|
|
4
4
|
AnyNode,
|
|
5
5
|
CeilingNode,
|
|
6
|
+
ColumnNode,
|
|
6
7
|
DoorNode,
|
|
8
|
+
ElevatorNode,
|
|
7
9
|
ItemNode,
|
|
8
10
|
Point2D,
|
|
9
11
|
RoofNode,
|
|
@@ -46,6 +48,16 @@ type CeilingPolygonEntry = {
|
|
|
46
48
|
holes: Point2D[][]
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
type ColumnPolygonEntry = {
|
|
52
|
+
column: ColumnNode
|
|
53
|
+
polygon: Point2D[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type ElevatorPolygonEntry = {
|
|
57
|
+
elevator: ElevatorNode
|
|
58
|
+
polygon: Point2D[]
|
|
59
|
+
}
|
|
60
|
+
|
|
49
61
|
type FloorplanRoofEntry = {
|
|
50
62
|
roof: RoofNode
|
|
51
63
|
segments: Array<{
|
|
@@ -72,8 +84,10 @@ type FloorplanStairEntry = {
|
|
|
72
84
|
|
|
73
85
|
type UseFloorplanHitTestingArgs = {
|
|
74
86
|
ceilingPolygons: CeilingPolygonEntry[]
|
|
87
|
+
columnPolygons: ColumnPolygonEntry[]
|
|
75
88
|
displaySlabPolygons: SlabPolygonEntry[]
|
|
76
89
|
displayWallPolygons: WallPolygonEntry[]
|
|
90
|
+
floorplanElevatorEntries: ElevatorPolygonEntry[]
|
|
77
91
|
floorplanItemEntries: FloorplanItemEntry[]
|
|
78
92
|
floorplanOpeningHitTolerance: number
|
|
79
93
|
floorplanRoofEntries: FloorplanRoofEntry[]
|
|
@@ -88,8 +102,10 @@ type UseFloorplanHitTestingArgs = {
|
|
|
88
102
|
|
|
89
103
|
export function useFloorplanHitTesting({
|
|
90
104
|
ceilingPolygons,
|
|
105
|
+
columnPolygons,
|
|
91
106
|
displaySlabPolygons,
|
|
92
107
|
displayWallPolygons,
|
|
108
|
+
floorplanElevatorEntries,
|
|
93
109
|
floorplanItemEntries,
|
|
94
110
|
floorplanOpeningHitTolerance,
|
|
95
111
|
floorplanRoofEntries,
|
|
@@ -113,18 +129,22 @@ export function useFloorplanHitTesting({
|
|
|
113
129
|
openings: openingsPolygons,
|
|
114
130
|
roofs: floorplanRoofEntries,
|
|
115
131
|
stairs: floorplanStairEntries,
|
|
132
|
+
elevators: floorplanElevatorEntries,
|
|
116
133
|
walls: displayWallPolygons,
|
|
117
134
|
slabs: displaySlabPolygons,
|
|
118
135
|
openingHitTolerance: floorplanOpeningHitTolerance,
|
|
119
136
|
wallHitTolerance: floorplanWallHitTolerance,
|
|
137
|
+
columns: columnPolygons,
|
|
120
138
|
getOpeningCenterLine,
|
|
121
139
|
})
|
|
122
140
|
},
|
|
123
141
|
[
|
|
124
142
|
ceilingPolygons,
|
|
143
|
+
columnPolygons,
|
|
125
144
|
displaySlabPolygons,
|
|
126
145
|
displayWallPolygons,
|
|
127
146
|
floorplanItemEntries,
|
|
147
|
+
floorplanElevatorEntries,
|
|
128
148
|
floorplanOpeningHitTolerance,
|
|
129
149
|
floorplanRoofEntries,
|
|
130
150
|
floorplanStairEntries,
|
|
@@ -149,13 +169,17 @@ export function useFloorplanHitTesting({
|
|
|
149
169
|
openings: openingsPolygons,
|
|
150
170
|
roofs: floorplanRoofEntries,
|
|
151
171
|
slabs: displaySlabPolygons,
|
|
172
|
+
columns: columnPolygons,
|
|
173
|
+
elevators: floorplanElevatorEntries,
|
|
152
174
|
stairs: floorplanStairEntries,
|
|
153
175
|
}),
|
|
154
176
|
[
|
|
155
177
|
ceilingPolygons,
|
|
178
|
+
columnPolygons,
|
|
156
179
|
displaySlabPolygons,
|
|
157
180
|
displayWallPolygons,
|
|
158
181
|
floorplanItemEntries,
|
|
182
|
+
floorplanElevatorEntries,
|
|
159
183
|
floorplanRoofEntries,
|
|
160
184
|
floorplanStairEntries,
|
|
161
185
|
isFloorplanItemContextActive,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type SiteNode,
|
|
13
13
|
type SlabNode,
|
|
14
14
|
type SpawnNode,
|
|
15
|
+
useLiveTransforms,
|
|
15
16
|
useScene,
|
|
16
17
|
type WallNode,
|
|
17
18
|
type WindowNode,
|
|
@@ -35,7 +36,7 @@ function useLevelChildren<TNode extends AnyNode>(
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const levelNode = state.nodes[levelId]
|
|
38
|
-
if (
|
|
39
|
+
if (levelNode?.type !== 'level') {
|
|
39
40
|
return [] as TNode[]
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -59,13 +60,22 @@ export function useFloorplanSceneData({
|
|
|
59
60
|
? (levelNode.parentId as BuildingNode['id'])
|
|
60
61
|
: buildingId
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
// Live transform override — when the building is mid-drag (the move
|
|
64
|
+
// tool publishes per-frame pose to useLiveTransforms), the floor-plan
|
|
65
|
+
// follows that pose so the dimmed reference floor tracks the cursor
|
|
66
|
+
// instead of snapping only on commit.
|
|
67
|
+
const buildingLiveTransform = useLiveTransforms((state) =>
|
|
68
|
+
currentBuildingId ? state.transforms.get(currentBuildingId) : undefined,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const committedBuildingRotationY = useScene((state) => {
|
|
63
72
|
if (!currentBuildingId) return 0
|
|
64
73
|
const node = state.nodes[currentBuildingId]
|
|
65
74
|
return node?.type === 'building' ? (node.rotation[1] ?? 0) : 0
|
|
66
75
|
})
|
|
76
|
+
const buildingRotationY = buildingLiveTransform?.rotation ?? committedBuildingRotationY
|
|
67
77
|
|
|
68
|
-
const
|
|
78
|
+
const committedBuildingPosition = useScene((state) => {
|
|
69
79
|
if (!currentBuildingId) {
|
|
70
80
|
return DEFAULT_BUILDING_POSITION
|
|
71
81
|
}
|
|
@@ -75,6 +85,7 @@ export function useFloorplanSceneData({
|
|
|
75
85
|
? (node.position as [number, number, number])
|
|
76
86
|
: DEFAULT_BUILDING_POSITION
|
|
77
87
|
})
|
|
88
|
+
const buildingPosition = buildingLiveTransform?.position ?? committedBuildingPosition
|
|
78
89
|
|
|
79
90
|
const site = useScene((state) => {
|
|
80
91
|
for (const rootNodeId of state.rootNodeIds) {
|
|
@@ -94,7 +105,7 @@ export function useFloorplanSceneData({
|
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
const buildingNode = state.nodes[currentBuildingId]
|
|
97
|
-
if (
|
|
108
|
+
if (buildingNode?.type !== 'building') {
|
|
98
109
|
return [] as LevelNode[]
|
|
99
110
|
}
|
|
100
111
|
|
|
@@ -122,7 +133,7 @@ export function useFloorplanSceneData({
|
|
|
122
133
|
}
|
|
123
134
|
|
|
124
135
|
const nextLevelNode = state.nodes[levelId]
|
|
125
|
-
if (
|
|
136
|
+
if (nextLevelNode?.type !== 'level') {
|
|
126
137
|
return [] as RoofNode[]
|
|
127
138
|
}
|
|
128
139
|
|
|
@@ -138,7 +149,7 @@ export function useFloorplanSceneData({
|
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
const nextLevelNode = state.nodes[levelId]
|
|
141
|
-
if (
|
|
152
|
+
if (nextLevelNode?.type !== 'level') {
|
|
142
153
|
return [] as OpeningNode[]
|
|
143
154
|
}
|
|
144
155
|
|
|
@@ -160,7 +171,7 @@ export function useFloorplanSceneData({
|
|
|
160
171
|
}
|
|
161
172
|
|
|
162
173
|
const nextLevelNode = state.nodes[levelId]
|
|
163
|
-
if (
|
|
174
|
+
if (nextLevelNode?.type !== 'level') {
|
|
164
175
|
return [] as AnyNode[]
|
|
165
176
|
}
|
|
166
177
|
|
|
@@ -170,6 +181,7 @@ export function useFloorplanSceneData({
|
|
|
170
181
|
|
|
171
182
|
return {
|
|
172
183
|
buildingPosition,
|
|
184
|
+
committedBuildingPosition,
|
|
173
185
|
buildingRotationY,
|
|
174
186
|
currentBuildingId,
|
|
175
187
|
ceilings,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bumps after the 3D meshes have had a chance to settle following a scene
|
|
5
|
+
* change. `computeGroupBox` reads mesh WORLD bounds, but a scene commit
|
|
6
|
+
* (undo/redo included) reaches the meshes asynchronously — renderers
|
|
7
|
+
* reconcile on the next React commit and the geometry systems rebuild in the
|
|
8
|
+
* next frame — so anything that derives geometry from the meshes in the same
|
|
9
|
+
* render as the `nodes` change reads STALE positions. Depend on this epoch
|
|
10
|
+
* (alongside `nodes`) to recompute once more after two animation frames,
|
|
11
|
+
* when transforms and rebuilt geometry are in place.
|
|
12
|
+
*/
|
|
13
|
+
export function useMeshSettleEpoch(nodes: unknown): number {
|
|
14
|
+
const [epoch, setEpoch] = useState(0)
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
let raf2 = 0
|
|
17
|
+
const raf1 = requestAnimationFrame(() => {
|
|
18
|
+
raf2 = requestAnimationFrame(() => setEpoch((e) => e + 1))
|
|
19
|
+
})
|
|
20
|
+
return () => {
|
|
21
|
+
cancelAnimationFrame(raf1)
|
|
22
|
+
cancelAnimationFrame(raf2)
|
|
23
|
+
}
|
|
24
|
+
}, [nodes])
|
|
25
|
+
return epoch
|
|
26
|
+
}
|