@pascal-app/nodes 1.0.0-beta.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/block/commands.d.ts +68 -0
- package/dist/block/commands.d.ts.map +1 -0
- package/dist/block/commands.js +1144 -0
- package/dist/block/contextual-help.d.ts +3 -0
- package/dist/block/contextual-help.d.ts.map +1 -0
- package/dist/block/contextual-help.js +38 -0
- package/dist/block/definition.d.ts +8 -0
- package/dist/block/definition.d.ts.map +1 -0
- package/dist/block/definition.js +126 -0
- package/dist/block/edit-session.d.ts +16 -0
- package/dist/block/edit-session.d.ts.map +1 -0
- package/dist/block/edit-session.js +32 -0
- package/dist/block/face-host.d.ts +3 -0
- package/dist/block/face-host.d.ts.map +1 -0
- package/dist/block/face-host.js +178 -0
- package/dist/block/floorplan.d.ts +3 -0
- package/dist/block/floorplan.d.ts.map +1 -0
- package/dist/block/floorplan.js +43 -0
- package/dist/block/geometry-snap.d.ts +18 -0
- package/dist/block/geometry-snap.d.ts.map +1 -0
- package/dist/block/geometry-snap.js +137 -0
- package/dist/block/geometry.d.ts +11 -0
- package/dist/block/geometry.d.ts.map +1 -0
- package/dist/block/geometry.js +146 -0
- package/dist/block/gesture-wheel.d.ts +8 -0
- package/dist/block/gesture-wheel.d.ts.map +1 -0
- package/dist/block/gesture-wheel.js +7 -0
- package/dist/block/interaction-sfx.d.ts +3 -0
- package/dist/block/interaction-sfx.d.ts.map +1 -0
- package/dist/block/interaction-sfx.js +16 -0
- package/dist/block/last-operation.d.ts +48 -0
- package/dist/block/last-operation.d.ts.map +1 -0
- package/dist/block/last-operation.js +120 -0
- package/dist/block/loop-cut-interaction.d.ts +5 -0
- package/dist/block/loop-cut-interaction.d.ts.map +1 -0
- package/dist/block/loop-cut-interaction.js +13 -0
- package/dist/block/material-slots.d.ts +61 -0
- package/dist/block/material-slots.d.ts.map +1 -0
- package/dist/block/material-slots.js +131 -0
- package/dist/block/modal-face-operation.d.ts +13 -0
- package/dist/block/modal-face-operation.d.ts.map +1 -0
- package/dist/block/modal-face-operation.js +40 -0
- package/dist/block/modal-session.d.ts +15 -0
- package/dist/block/modal-session.d.ts.map +1 -0
- package/dist/block/modal-session.js +40 -0
- package/dist/block/modal-transform.d.ts +30 -0
- package/dist/block/modal-transform.d.ts.map +1 -0
- package/dist/block/modal-transform.js +144 -0
- package/dist/block/paint.d.ts +3 -0
- package/dist/block/paint.d.ts.map +1 -0
- package/dist/block/paint.js +136 -0
- package/dist/block/panel.d.ts +2 -0
- package/dist/block/panel.d.ts.map +1 -0
- package/dist/block/panel.js +234 -0
- package/dist/block/parametrics.d.ts +4 -0
- package/dist/block/parametrics.d.ts.map +1 -0
- package/dist/block/parametrics.js +9 -0
- package/dist/block/preview.d.ts +6 -0
- package/dist/block/preview.d.ts.map +1 -0
- package/dist/block/preview.js +46 -0
- package/dist/block/rotation-drag.d.ts +5 -0
- package/dist/block/rotation-drag.d.ts.map +1 -0
- package/dist/block/rotation-drag.js +18 -0
- package/dist/block/schema.d.ts +2 -0
- package/dist/block/schema.d.ts.map +1 -0
- package/dist/block/schema.js +1 -0
- package/dist/block/selection-geometry.d.ts +9 -0
- package/dist/block/selection-geometry.d.ts.map +1 -0
- package/dist/block/selection-geometry.js +31 -0
- package/dist/block/selection-model.d.ts +17 -0
- package/dist/block/selection-model.d.ts.map +1 -0
- package/dist/block/selection-model.js +84 -0
- package/dist/block/selection.d.ts +4 -0
- package/dist/block/selection.d.ts.map +1 -0
- package/dist/block/selection.js +2570 -0
- package/dist/block/slots.d.ts +4 -0
- package/dist/block/slots.d.ts.map +1 -0
- package/dist/block/slots.js +17 -0
- package/dist/block/tool.d.ts +3 -0
- package/dist/block/tool.d.ts.map +1 -0
- package/dist/block/tool.js +174 -0
- package/dist/block/toolbar-state.d.ts +46 -0
- package/dist/block/toolbar-state.d.ts.map +1 -0
- package/dist/block/toolbar-state.js +67 -0
- package/dist/block/use-block-face-operation.d.ts +39 -0
- package/dist/block/use-block-face-operation.d.ts.map +1 -0
- package/dist/block/use-block-face-operation.js +218 -0
- package/dist/box-vent/definition.d.ts.map +1 -1
- package/dist/box-vent/definition.js +8 -5
- package/dist/box-vent/geometry.d.ts +4 -0
- package/dist/box-vent/geometry.d.ts.map +1 -1
- package/dist/box-vent/geometry.js +76 -44
- package/dist/box-vent/paint.d.ts +4 -0
- package/dist/box-vent/paint.d.ts.map +1 -0
- package/dist/box-vent/paint.js +40 -0
- package/dist/box-vent/panel.js +1 -1
- package/dist/box-vent/parametrics.js +3 -3
- package/dist/box-vent/renderer.d.ts.map +1 -1
- package/dist/box-vent/renderer.js +29 -10
- package/dist/cabinet/compartment-card.d.ts.map +1 -1
- package/dist/cabinet/compartment-card.js +11 -3
- package/dist/cabinet/continuous-placement.d.ts +3 -0
- package/dist/cabinet/continuous-placement.d.ts.map +1 -1
- package/dist/cabinet/continuous-placement.js +6 -0
- package/dist/cabinet/definition.d.ts.map +1 -1
- package/dist/cabinet/definition.js +400 -54
- package/dist/cabinet/flame-index.d.ts +3 -0
- package/dist/cabinet/flame-index.d.ts.map +1 -0
- package/dist/cabinet/flame-index.js +13 -0
- package/dist/cabinet/floorplan-move.d.ts.map +1 -1
- package/dist/cabinet/floorplan-move.js +91 -22
- package/dist/cabinet/geometry/fridge.d.ts.map +1 -1
- package/dist/cabinet/geometry/fridge.js +25 -1
- package/dist/cabinet/geometry/fronts.d.ts +4 -0
- package/dist/cabinet/geometry/fronts.d.ts.map +1 -1
- package/dist/cabinet/geometry/fronts.js +1 -1
- package/dist/cabinet/geometry/run.d.ts.map +1 -1
- package/dist/cabinet/geometry/run.js +19 -1
- package/dist/cabinet/geometry.d.ts.map +1 -1
- package/dist/cabinet/geometry.js +63 -1
- package/dist/cabinet/index.d.ts +1 -0
- package/dist/cabinet/index.d.ts.map +1 -1
- package/dist/cabinet/index.js +1 -0
- package/dist/cabinet/insertion.d.ts +19 -0
- package/dist/cabinet/insertion.d.ts.map +1 -0
- package/dist/cabinet/insertion.js +49 -0
- package/dist/cabinet/move-frame.d.ts.map +1 -1
- package/dist/cabinet/move-frame.js +68 -2
- package/dist/cabinet/panel-context.d.ts +7 -0
- package/dist/cabinet/panel-context.d.ts.map +1 -0
- package/dist/cabinet/panel-context.js +16 -0
- package/dist/cabinet/panel-visibility.d.ts +9 -0
- package/dist/cabinet/panel-visibility.d.ts.map +1 -0
- package/dist/cabinet/panel-visibility.js +26 -0
- package/dist/cabinet/panel.d.ts.map +1 -1
- package/dist/cabinet/panel.js +221 -37
- package/dist/cabinet/placement-dimensions.d.ts +42 -0
- package/dist/cabinet/placement-dimensions.d.ts.map +1 -0
- package/dist/cabinet/placement-dimensions.js +227 -0
- package/dist/cabinet/placement-snap.d.ts +21 -1
- package/dist/cabinet/placement-snap.d.ts.map +1 -1
- package/dist/cabinet/placement-snap.js +48 -6
- package/dist/cabinet/preset-width-debt.d.ts +7 -0
- package/dist/cabinet/preset-width-debt.d.ts.map +1 -0
- package/dist/cabinet/preset-width-debt.js +45 -0
- package/dist/cabinet/presets.d.ts.map +1 -1
- package/dist/cabinet/presets.js +14 -13
- package/dist/cabinet/profiles.d.ts +14 -0
- package/dist/cabinet/profiles.d.ts.map +1 -0
- package/dist/cabinet/profiles.js +30 -0
- package/dist/cabinet/quick-actions.d.ts.map +1 -1
- package/dist/cabinet/quick-actions.js +8 -24
- package/dist/cabinet/reveals.d.ts +37 -0
- package/dist/cabinet/reveals.d.ts.map +1 -0
- package/dist/cabinet/reveals.js +13 -0
- package/dist/cabinet/run-layout.d.ts +90 -4
- package/dist/cabinet/run-layout.d.ts.map +1 -1
- package/dist/cabinet/run-layout.js +408 -23
- package/dist/cabinet/run-ops.d.ts +202 -10
- package/dist/cabinet/run-ops.d.ts.map +1 -1
- package/dist/cabinet/run-ops.js +520 -33
- package/dist/cabinet/run-panel.d.ts +6 -2
- package/dist/cabinet/run-panel.d.ts.map +1 -1
- package/dist/cabinet/run-panel.js +285 -108
- package/dist/cabinet/stack-transitions.d.ts.map +1 -1
- package/dist/cabinet/stack-transitions.js +35 -24
- package/dist/cabinet/stack.d.ts +169 -1
- package/dist/cabinet/stack.d.ts.map +1 -1
- package/dist/cabinet/stack.js +57 -18
- package/dist/cabinet/system.d.ts +2 -0
- package/dist/cabinet/system.d.ts.map +1 -1
- package/dist/cabinet/system.js +28 -8
- package/dist/cabinet/tool.d.ts.map +1 -1
- package/dist/cabinet/tool.js +616 -91
- package/dist/cabinet/validation.d.ts +27 -0
- package/dist/cabinet/validation.d.ts.map +1 -0
- package/dist/cabinet/validation.js +56 -0
- package/dist/cabinet/wall-height-presets.d.ts +12 -0
- package/dist/cabinet/wall-height-presets.d.ts.map +1 -0
- package/dist/cabinet/wall-height-presets.js +15 -0
- package/dist/cabinet/wall-snap.d.ts +27 -3
- package/dist/cabinet/wall-snap.d.ts.map +1 -1
- package/dist/cabinet/wall-snap.js +278 -62
- package/dist/cabinet/widths.d.ts +10 -0
- package/dist/cabinet/widths.d.ts.map +1 -0
- package/dist/cabinet/widths.js +13 -0
- package/dist/ceiling/definition.d.ts.map +1 -1
- package/dist/ceiling/definition.js +7 -1
- package/dist/ceiling/floorplan-affordances.d.ts +8 -8
- package/dist/ceiling/materials.d.ts.map +1 -1
- package/dist/ceiling/materials.js +0 -1
- package/dist/ceiling/panel.d.ts.map +1 -1
- package/dist/ceiling/panel.js +8 -2
- package/dist/ceiling/parametrics.js +1 -1
- package/dist/chimney/definition.js +1 -1
- package/dist/chimney/panel.js +1 -1
- package/dist/chimney/parametrics.js +3 -3
- package/dist/column/definition.d.ts.map +1 -1
- package/dist/column/definition.js +22 -4
- package/dist/column/panel.d.ts.map +1 -1
- package/dist/column/panel.js +37 -7
- package/dist/column/parametrics.d.ts.map +1 -1
- package/dist/column/parametrics.js +5 -3
- package/dist/column/tool.d.ts.map +1 -1
- package/dist/column/tool.js +50 -14
- package/dist/cupola/definition.d.ts.map +1 -1
- package/dist/cupola/definition.js +15 -6
- package/dist/cupola/geometry.d.ts +6 -0
- package/dist/cupola/geometry.d.ts.map +1 -1
- package/dist/cupola/geometry.js +60 -13
- package/dist/cupola/paint.d.ts +4 -0
- package/dist/cupola/paint.d.ts.map +1 -0
- package/dist/cupola/paint.js +39 -0
- package/dist/cupola/panel.js +1 -1
- package/dist/cupola/parametrics.js +3 -3
- package/dist/cupola/renderer.d.ts.map +1 -1
- package/dist/cupola/renderer.js +27 -8
- package/dist/door/floorplan-move.d.ts.map +1 -1
- package/dist/door/floorplan-move.js +1 -1
- package/dist/door/move-tool.d.ts.map +1 -1
- package/dist/door/move-tool.js +86 -32
- package/dist/door/panel.d.ts.map +1 -1
- package/dist/door/panel.js +1 -1
- package/dist/door/parametrics.js +2 -2
- package/dist/door/tool.d.ts.map +1 -1
- package/dist/door/tool.js +16 -4
- package/dist/dormer/csg-geometry.d.ts +6 -42
- package/dist/dormer/csg-geometry.d.ts.map +1 -1
- package/dist/dormer/csg-geometry.js +38 -209
- package/dist/dormer/definition.d.ts +4 -12
- package/dist/dormer/definition.d.ts.map +1 -1
- package/dist/dormer/definition.js +18 -193
- package/dist/dormer/floorplan.d.ts +3 -3
- package/dist/dormer/floorplan.d.ts.map +1 -1
- package/dist/dormer/floorplan.js +6 -17
- package/dist/dormer/geometry.d.ts +5 -24
- package/dist/dormer/geometry.d.ts.map +1 -1
- package/dist/dormer/geometry.js +64 -41
- package/dist/dormer/index.d.ts +1 -1
- package/dist/dormer/index.d.ts.map +1 -1
- package/dist/dormer/index.js +1 -1
- package/dist/dormer/move-tool.d.ts.map +1 -1
- package/dist/dormer/move-tool.js +31 -3
- package/dist/dormer/panel-windows-section.d.ts +9 -0
- package/dist/dormer/panel-windows-section.d.ts.map +1 -0
- package/dist/dormer/panel-windows-section.js +7 -0
- package/dist/dormer/panel.d.ts.map +1 -1
- package/dist/dormer/panel.js +100 -18
- package/dist/dormer/parametrics.d.ts.map +1 -1
- package/dist/dormer/parametrics.js +8 -77
- package/dist/dormer/placement-guides.d.ts.map +1 -1
- package/dist/dormer/placement-guides.js +5 -4
- package/dist/dormer/preview.d.ts.map +1 -1
- package/dist/dormer/preview.js +9 -1
- package/dist/dormer/renderer.d.ts.map +1 -1
- package/dist/dormer/renderer.js +24 -26
- package/dist/dormer/tool.d.ts.map +1 -1
- package/dist/dormer/tool.js +3 -1
- package/dist/dormer/window-layout.d.ts +15 -0
- package/dist/dormer/window-layout.d.ts.map +1 -0
- package/dist/dormer/window-layout.js +59 -0
- package/dist/downspout/definition.d.ts.map +1 -1
- package/dist/downspout/definition.js +17 -4
- package/dist/downspout/geometry.d.ts.map +1 -1
- package/dist/downspout/geometry.js +10 -0
- package/dist/downspout/inspector-editors.d.ts.map +1 -1
- package/dist/downspout/inspector-editors.js +9 -1
- package/dist/downspout/parametrics.d.ts.map +1 -1
- package/dist/downspout/parametrics.js +2 -1
- package/dist/downspout/renderer.d.ts.map +1 -1
- package/dist/downspout/renderer.js +19 -3
- package/dist/downspout/system.d.ts +7 -0
- package/dist/downspout/system.d.ts.map +1 -0
- package/dist/downspout/system.js +114 -0
- package/dist/duct-fitting/accessory-geometry.d.ts +4 -0
- package/dist/duct-fitting/accessory-geometry.d.ts.map +1 -0
- package/dist/duct-fitting/accessory-geometry.js +47 -0
- package/dist/duct-fitting/definition.d.ts.map +1 -1
- package/dist/duct-fitting/definition.js +11 -1
- package/dist/duct-fitting/floorplan.d.ts.map +1 -1
- package/dist/duct-fitting/floorplan.js +8 -0
- package/dist/duct-fitting/geometry.d.ts.map +1 -1
- package/dist/duct-fitting/geometry.js +52 -38
- package/dist/duct-fitting/parametrics.d.ts.map +1 -1
- package/dist/duct-fitting/parametrics.js +78 -15
- package/dist/duct-fitting/ports.d.ts +1 -0
- package/dist/duct-fitting/ports.d.ts.map +1 -1
- package/dist/duct-fitting/ports.js +46 -0
- package/dist/duct-fitting/tool.d.ts +19 -0
- package/dist/duct-fitting/tool.d.ts.map +1 -1
- package/dist/duct-fitting/tool.js +126 -34
- package/dist/duct-segment/continuation.d.ts +21 -0
- package/dist/duct-segment/continuation.d.ts.map +1 -0
- package/dist/duct-segment/continuation.js +167 -0
- package/dist/duct-segment/definition.d.ts.map +1 -1
- package/dist/duct-segment/definition.js +19 -18
- package/dist/duct-segment/floorplan.d.ts.map +1 -1
- package/dist/duct-segment/floorplan.js +43 -0
- package/dist/duct-segment/geometry.d.ts.map +1 -1
- package/dist/duct-segment/geometry.js +3 -0
- package/dist/duct-segment/move-tool.d.ts.map +1 -1
- package/dist/duct-segment/move-tool.js +33 -6
- package/dist/duct-segment/parametrics.d.ts.map +1 -1
- package/dist/duct-segment/parametrics.js +45 -1
- package/dist/duct-segment/selection.d.ts +3 -4
- package/dist/duct-segment/selection.d.ts.map +1 -1
- package/dist/duct-segment/selection.js +186 -158
- package/dist/duct-segment/tool.d.ts +37 -0
- package/dist/duct-segment/tool.d.ts.map +1 -1
- package/dist/duct-segment/tool.js +305 -678
- package/dist/duct-terminal/parametrics.js +2 -2
- package/dist/duct-terminal/tool.d.ts.map +1 -1
- package/dist/duct-terminal/tool.js +37 -7
- package/dist/elevator/definition.d.ts.map +1 -1
- package/dist/elevator/definition.js +2 -0
- package/dist/elevator/panel.d.ts.map +1 -1
- package/dist/elevator/panel.js +7 -7
- package/dist/elevator/system.d.ts.map +1 -1
- package/dist/elevator/system.js +10 -1
- package/dist/eyebrow-vent/definition.d.ts.map +1 -1
- package/dist/eyebrow-vent/definition.js +8 -5
- package/dist/eyebrow-vent/geometry.d.ts +4 -0
- package/dist/eyebrow-vent/geometry.d.ts.map +1 -1
- package/dist/eyebrow-vent/geometry.js +60 -15
- package/dist/eyebrow-vent/paint.d.ts +4 -0
- package/dist/eyebrow-vent/paint.d.ts.map +1 -0
- package/dist/eyebrow-vent/paint.js +33 -0
- package/dist/eyebrow-vent/panel.js +1 -1
- package/dist/eyebrow-vent/parametrics.js +3 -3
- package/dist/eyebrow-vent/renderer.d.ts.map +1 -1
- package/dist/eyebrow-vent/renderer.js +27 -8
- package/dist/fence/curve-tool.d.ts.map +1 -1
- package/dist/fence/curve-tool.js +14 -9
- package/dist/fence/definition.d.ts.map +1 -1
- package/dist/fence/definition.js +2 -1
- package/dist/fence/floorplan-affordances.d.ts +1 -0
- package/dist/fence/floorplan-affordances.d.ts.map +1 -1
- package/dist/fence/floorplan-affordances.js +30 -1
- package/dist/fence/floorplan.d.ts.map +1 -1
- package/dist/fence/floorplan.js +14 -0
- package/dist/fence/move-control-point-tool.d.ts.map +1 -1
- package/dist/fence/move-control-point-tool.js +13 -8
- package/dist/fence/move-tangent-tool.d.ts.map +1 -1
- package/dist/fence/move-tangent-tool.js +13 -8
- package/dist/fence/parametrics.js +3 -3
- package/dist/fence/tool.d.ts.map +1 -1
- package/dist/fence/tool.js +55 -31
- package/dist/gutter/corner-mitre.d.ts.map +1 -1
- package/dist/gutter/corner-mitre.js +47 -7
- package/dist/gutter/definition.d.ts.map +1 -1
- package/dist/gutter/definition.js +23 -13
- package/dist/gutter/eave-align.d.ts.map +1 -1
- package/dist/gutter/eave-align.js +10 -0
- package/dist/gutter/eave-snap.d.ts +1 -1
- package/dist/gutter/eave-snap.d.ts.map +1 -1
- package/dist/gutter/eave-snap.js +10 -23
- package/dist/gutter/geometry.d.ts.map +1 -1
- package/dist/gutter/geometry.js +126 -8
- package/dist/gutter/outlet-lookup.d.ts.map +1 -1
- package/dist/gutter/outlet-lookup.js +22 -1
- package/dist/gutter/paint.d.ts +3 -0
- package/dist/gutter/paint.d.ts.map +1 -0
- package/dist/gutter/paint.js +6 -0
- package/dist/gutter/parametrics.d.ts.map +1 -1
- package/dist/gutter/parametrics.js +4 -1
- package/dist/gutter/renderer.d.ts.map +1 -1
- package/dist/gutter/renderer.js +63 -7
- package/dist/gutter/trim-clip.d.ts +3 -0
- package/dist/gutter/trim-clip.d.ts.map +1 -0
- package/dist/gutter/trim-clip.js +5 -0
- package/dist/hvac-equipment/parametrics.js +3 -3
- package/dist/hvac-equipment/tool.d.ts.map +1 -1
- package/dist/hvac-equipment/tool.js +12 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/item/definition.d.ts.map +1 -1
- package/dist/item/definition.js +10 -2
- package/dist/item/floorplan-move.d.ts.map +1 -1
- package/dist/item/floorplan-move.js +20 -1
- package/dist/item/floorplan.d.ts.map +1 -1
- package/dist/item/floorplan.js +22 -1
- package/dist/item/move-tool.d.ts +28 -1
- package/dist/item/move-tool.d.ts.map +1 -1
- package/dist/item/move-tool.js +14 -2
- package/dist/item/renderer.d.ts.map +1 -1
- package/dist/item/renderer.js +16 -6
- package/dist/item/system.d.ts +5 -0
- package/dist/item/system.d.ts.map +1 -1
- package/dist/item/system.js +7 -1
- package/dist/lean-to-extension/arc.d.ts +14 -0
- package/dist/lean-to-extension/arc.d.ts.map +1 -0
- package/dist/lean-to-extension/arc.js +56 -0
- package/dist/lean-to-extension/assembly.d.ts +61 -0
- package/dist/lean-to-extension/assembly.d.ts.map +1 -0
- package/dist/lean-to-extension/assembly.js +761 -0
- package/dist/lean-to-extension/canopy-joint.d.ts +35 -0
- package/dist/lean-to-extension/canopy-joint.d.ts.map +1 -0
- package/dist/lean-to-extension/canopy-joint.js +234 -0
- package/dist/lean-to-extension/conical-host.d.ts +15 -0
- package/dist/lean-to-extension/conical-host.d.ts.map +1 -0
- package/dist/lean-to-extension/conical-host.js +113 -0
- package/dist/lean-to-extension/corner-joint.d.ts +29 -0
- package/dist/lean-to-extension/corner-joint.d.ts.map +1 -0
- package/dist/lean-to-extension/corner-joint.js +1202 -0
- package/dist/lean-to-extension/definition.d.ts +4 -0
- package/dist/lean-to-extension/definition.d.ts.map +1 -0
- package/dist/lean-to-extension/definition.js +433 -0
- package/dist/lean-to-extension/floorplan-affordances.d.ts +4 -0
- package/dist/lean-to-extension/floorplan-affordances.d.ts.map +1 -0
- package/dist/lean-to-extension/floorplan-affordances.js +144 -0
- package/dist/lean-to-extension/floorplan-move.d.ts +3 -0
- package/dist/lean-to-extension/floorplan-move.d.ts.map +1 -0
- package/dist/lean-to-extension/floorplan-move.js +157 -0
- package/dist/lean-to-extension/floorplan-tool.d.ts +4 -0
- package/dist/lean-to-extension/floorplan-tool.d.ts.map +1 -0
- package/dist/lean-to-extension/floorplan-tool.js +385 -0
- package/dist/lean-to-extension/floorplan.d.ts +3 -0
- package/dist/lean-to-extension/floorplan.d.ts.map +1 -0
- package/dist/lean-to-extension/floorplan.js +373 -0
- package/dist/lean-to-extension/geometry.d.ts +7 -0
- package/dist/lean-to-extension/geometry.d.ts.map +1 -0
- package/dist/lean-to-extension/geometry.js +662 -0
- package/dist/lean-to-extension/index.d.ts +8 -0
- package/dist/lean-to-extension/index.d.ts.map +1 -0
- package/dist/lean-to-extension/index.js +7 -0
- package/dist/lean-to-extension/layout.d.ts +108 -0
- package/dist/lean-to-extension/layout.d.ts.map +1 -0
- package/dist/lean-to-extension/layout.js +477 -0
- package/dist/lean-to-extension/managed-preview.d.ts +3 -0
- package/dist/lean-to-extension/managed-preview.d.ts.map +1 -0
- package/dist/lean-to-extension/managed-preview.js +93 -0
- package/dist/lean-to-extension/move-tool.d.ts +8 -0
- package/dist/lean-to-extension/move-tool.d.ts.map +1 -0
- package/dist/lean-to-extension/move-tool.js +202 -0
- package/dist/lean-to-extension/paint.d.ts +2 -0
- package/dist/lean-to-extension/paint.d.ts.map +1 -0
- package/dist/lean-to-extension/paint.js +16 -0
- package/dist/lean-to-extension/parametrics.d.ts +4 -0
- package/dist/lean-to-extension/parametrics.d.ts.map +1 -0
- package/dist/lean-to-extension/parametrics.js +570 -0
- package/dist/lean-to-extension/placement-scope.d.ts +3 -0
- package/dist/lean-to-extension/placement-scope.d.ts.map +1 -0
- package/dist/lean-to-extension/placement-scope.js +4 -0
- package/dist/lean-to-extension/placement-validation.d.ts +4 -0
- package/dist/lean-to-extension/placement-validation.d.ts.map +1 -0
- package/dist/lean-to-extension/placement-validation.js +324 -0
- package/dist/lean-to-extension/placement.d.ts +59 -0
- package/dist/lean-to-extension/placement.d.ts.map +1 -0
- package/dist/lean-to-extension/placement.js +331 -0
- package/dist/lean-to-extension/plan-footprint.d.ts +10 -0
- package/dist/lean-to-extension/plan-footprint.d.ts.map +1 -0
- package/dist/lean-to-extension/plan-footprint.js +27 -0
- package/dist/lean-to-extension/preview-geometry.d.ts +7 -0
- package/dist/lean-to-extension/preview-geometry.d.ts.map +1 -0
- package/dist/lean-to-extension/preview-geometry.js +40 -0
- package/dist/lean-to-extension/preview.d.ts +7 -0
- package/dist/lean-to-extension/preview.d.ts.map +1 -0
- package/dist/lean-to-extension/preview.js +20 -0
- package/dist/lean-to-extension/renderer.d.ts +6 -0
- package/dist/lean-to-extension/renderer.d.ts.map +1 -0
- package/dist/lean-to-extension/renderer.js +34 -0
- package/dist/lean-to-extension/roof-attachment.d.ts +28 -0
- package/dist/lean-to-extension/roof-attachment.d.ts.map +1 -0
- package/dist/lean-to-extension/roof-attachment.js +376 -0
- package/dist/lean-to-extension/schema.d.ts +2 -0
- package/dist/lean-to-extension/schema.d.ts.map +1 -0
- package/dist/lean-to-extension/schema.js +1 -0
- package/dist/lean-to-extension/slots.d.ts +5 -0
- package/dist/lean-to-extension/slots.d.ts.map +1 -0
- package/dist/lean-to-extension/slots.js +15 -0
- package/dist/lean-to-extension/system.d.ts +7 -0
- package/dist/lean-to-extension/system.d.ts.map +1 -0
- package/dist/lean-to-extension/system.js +726 -0
- package/dist/lean-to-extension/tool.d.ts +3 -0
- package/dist/lean-to-extension/tool.d.ts.map +1 -0
- package/dist/lean-to-extension/tool.js +580 -0
- package/dist/lean-to-extension/wall-target.d.ts +10 -0
- package/dist/lean-to-extension/wall-target.d.ts.map +1 -0
- package/dist/lean-to-extension/wall-target.js +29 -0
- package/dist/lineset/definition.d.ts.map +1 -1
- package/dist/lineset/definition.js +1 -0
- package/dist/liquid-line/definition.d.ts.map +1 -1
- package/dist/liquid-line/definition.js +1 -0
- package/dist/measurement/renderer.d.ts.map +1 -1
- package/dist/measurement/renderer.js +6 -5
- package/dist/measurement/surface-query.js +5 -5
- package/dist/measurement/tool.d.ts.map +1 -1
- package/dist/measurement/tool.js +7 -5
- package/dist/pipe-fitting/definition.d.ts.map +1 -1
- package/dist/pipe-fitting/definition.js +16 -2
- package/dist/pipe-fitting/floorplan.d.ts.map +1 -1
- package/dist/pipe-fitting/floorplan.js +4 -0
- package/dist/pipe-fitting/geometry.d.ts +3 -5
- package/dist/pipe-fitting/geometry.d.ts.map +1 -1
- package/dist/pipe-fitting/geometry.js +154 -21
- package/dist/pipe-fitting/inline-insertion.d.ts +13 -0
- package/dist/pipe-fitting/inline-insertion.d.ts.map +1 -0
- package/dist/pipe-fitting/inline-insertion.js +96 -0
- package/dist/pipe-fitting/parametrics.d.ts.map +1 -1
- package/dist/pipe-fitting/parametrics.js +17 -3
- package/dist/pipe-fitting/ports.d.ts.map +1 -1
- package/dist/pipe-fitting/ports.js +8 -1
- package/dist/pipe-fitting/tool.d.ts +26 -0
- package/dist/pipe-fitting/tool.d.ts.map +1 -1
- package/dist/pipe-fitting/tool.js +182 -37
- package/dist/pipe-segment/continuation.d.ts +22 -0
- package/dist/pipe-segment/continuation.d.ts.map +1 -0
- package/dist/pipe-segment/continuation.js +146 -0
- package/dist/pipe-segment/definition.d.ts.map +1 -1
- package/dist/pipe-segment/definition.js +23 -7
- package/dist/pipe-segment/floorplan.d.ts.map +1 -1
- package/dist/pipe-segment/floorplan.js +42 -0
- package/dist/pipe-segment/geometry.d.ts +2 -1
- package/dist/pipe-segment/geometry.d.ts.map +1 -1
- package/dist/pipe-segment/geometry.js +4 -1
- package/dist/pipe-segment/move-tool.d.ts.map +1 -1
- package/dist/pipe-segment/move-tool.js +25 -5
- package/dist/pipe-segment/parametrics.d.ts.map +1 -1
- package/dist/pipe-segment/parametrics.js +45 -0
- package/dist/pipe-segment/selection.d.ts +3 -4
- package/dist/pipe-segment/selection.d.ts.map +1 -1
- package/dist/pipe-segment/selection.js +169 -105
- package/dist/pipe-segment/slope.d.ts +5 -0
- package/dist/pipe-segment/slope.d.ts.map +1 -0
- package/dist/pipe-segment/slope.js +10 -0
- package/dist/pipe-segment/tool.d.ts.map +1 -1
- package/dist/pipe-segment/tool.js +325 -545
- package/dist/pipe-trap/tool.d.ts.map +1 -1
- package/dist/pipe-trap/tool.js +15 -2
- package/dist/ridge-vent/definition.js +1 -1
- package/dist/ridge-vent/panel.d.ts.map +1 -1
- package/dist/ridge-vent/panel.js +2 -2
- package/dist/ridge-vent/parametrics.js +1 -1
- package/dist/ridge-vent/renderer.d.ts.map +1 -1
- package/dist/ridge-vent/renderer.js +2 -2
- package/dist/roof/conical-roof-placement.d.ts +36 -0
- package/dist/roof/conical-roof-placement.d.ts.map +1 -0
- package/dist/roof/conical-roof-placement.js +134 -0
- package/dist/roof/conical-roof.d.ts +3 -0
- package/dist/roof/conical-roof.d.ts.map +1 -0
- package/dist/roof/conical-roof.js +53 -0
- package/dist/roof/definition.d.ts +3 -10
- package/dist/roof/definition.d.ts.map +1 -1
- package/dist/roof/definition.js +83 -13
- package/dist/roof/floorplan.d.ts +3 -10
- package/dist/roof/floorplan.d.ts.map +1 -1
- package/dist/roof/floorplan.js +139 -165
- package/dist/roof/index.d.ts +1 -0
- package/dist/roof/index.d.ts.map +1 -1
- package/dist/roof/index.js +1 -0
- package/dist/roof/panel.d.ts.map +1 -1
- package/dist/roof/panel.js +22 -23
- package/dist/roof/renderer.d.ts.map +1 -1
- package/dist/roof/roof-footprint-source.d.ts +16 -0
- package/dist/roof/roof-footprint-source.d.ts.map +1 -0
- package/dist/roof/roof-footprint-source.js +14 -0
- package/dist/roof/roof-footprint.d.ts +25 -0
- package/dist/roof/roof-footprint.d.ts.map +1 -0
- package/dist/roof/roof-footprint.js +94 -0
- package/dist/roof/roof-placement-mode.d.ts +18 -0
- package/dist/roof/roof-placement-mode.d.ts.map +1 -0
- package/dist/roof/roof-placement-mode.js +24 -0
- package/dist/roof/tool.d.ts +10 -0
- package/dist/roof/tool.d.ts.map +1 -0
- package/dist/roof/tool.js +922 -0
- package/dist/roof-segment/definition.d.ts.map +1 -1
- package/dist/roof-segment/definition.js +52 -8
- package/dist/roof-segment/floorplan-affordances.d.ts +5 -6
- package/dist/roof-segment/floorplan-affordances.d.ts.map +1 -1
- package/dist/roof-segment/floorplan-affordances.js +64 -18
- package/dist/roof-segment/floorplan.d.ts +3 -2
- package/dist/roof-segment/floorplan.d.ts.map +1 -1
- package/dist/roof-segment/floorplan.js +95 -46
- package/dist/roof-segment/panel.d.ts.map +1 -1
- package/dist/roof-segment/panel.js +122 -27
- package/dist/scan/definition.d.ts +2 -3
- package/dist/scan/definition.d.ts.map +1 -1
- package/dist/scan/definition.js +9 -7
- package/dist/scan/parametrics.d.ts.map +1 -1
- package/dist/scan/parametrics.js +13 -1
- package/dist/scan/renderer.d.ts.map +1 -1
- package/dist/scan/renderer.js +8 -2
- package/dist/shared/accessory-cursor.d.ts +7 -0
- package/dist/shared/accessory-cursor.d.ts.map +1 -0
- package/dist/shared/accessory-cursor.js +83 -0
- package/dist/shared/accessory-floorplan.d.ts +4 -0
- package/dist/shared/accessory-floorplan.d.ts.map +1 -0
- package/dist/shared/accessory-floorplan.js +51 -0
- package/dist/shared/accessory-geometry.d.ts +7 -0
- package/dist/shared/accessory-geometry.d.ts.map +1 -0
- package/dist/shared/accessory-geometry.js +79 -0
- package/dist/shared/accessory-placement.d.ts +10 -0
- package/dist/shared/accessory-placement.d.ts.map +1 -0
- package/dist/shared/accessory-placement.js +126 -0
- package/dist/shared/accessory-snapping.d.ts +5 -0
- package/dist/shared/accessory-snapping.d.ts.map +1 -0
- package/dist/shared/accessory-snapping.js +40 -0
- package/dist/shared/automatic-run-end-cap.d.ts +11 -0
- package/dist/shared/automatic-run-end-cap.d.ts.map +1 -0
- package/dist/shared/automatic-run-end-cap.js +159 -0
- package/dist/shared/block-face-host.d.ts +15 -0
- package/dist/shared/block-face-host.d.ts.map +1 -0
- package/dist/shared/block-face-host.js +36 -0
- package/dist/shared/connection-compatibility.d.ts +8 -0
- package/dist/shared/connection-compatibility.d.ts.map +1 -0
- package/dist/shared/connection-compatibility.js +28 -0
- package/dist/shared/connection-feedback.d.ts +10 -0
- package/dist/shared/connection-feedback.d.ts.map +1 -0
- package/dist/shared/connection-feedback.js +17 -0
- package/dist/shared/distribution-run-contract.d.ts +49 -0
- package/dist/shared/distribution-run-contract.d.ts.map +1 -0
- package/dist/shared/distribution-run-contract.js +30 -0
- package/dist/shared/distribution-run-tool.d.ts +107 -0
- package/dist/shared/distribution-run-tool.d.ts.map +1 -0
- package/dist/shared/distribution-run-tool.js +941 -0
- package/dist/shared/dormer-wall-opening-placement.d.ts +21 -0
- package/dist/shared/dormer-wall-opening-placement.d.ts.map +1 -0
- package/dist/shared/dormer-wall-opening-placement.js +101 -0
- package/dist/shared/duct-adapter.d.ts +10 -0
- package/dist/shared/duct-adapter.d.ts.map +1 -0
- package/dist/shared/duct-adapter.js +43 -0
- package/dist/shared/elbow-branch-continuation.d.ts +20 -0
- package/dist/shared/elbow-branch-continuation.d.ts.map +1 -0
- package/dist/shared/elbow-branch-continuation.js +200 -0
- package/dist/shared/fitting-clearance.d.ts +5 -0
- package/dist/shared/fitting-clearance.d.ts.map +1 -0
- package/dist/shared/fitting-clearance.js +11 -0
- package/dist/shared/fitting-deletion-cleanup.d.ts +14 -0
- package/dist/shared/fitting-deletion-cleanup.d.ts.map +1 -0
- package/dist/shared/fitting-deletion-cleanup.js +297 -0
- package/dist/shared/fitting-surface-support.d.ts +5 -0
- package/dist/shared/fitting-surface-support.d.ts.map +1 -0
- package/dist/shared/fitting-surface-support.js +42 -0
- package/dist/shared/fitting-tool-options.d.ts +4 -0
- package/dist/shared/fitting-tool-options.d.ts.map +1 -0
- package/dist/shared/fitting-tool-options.js +175 -0
- package/dist/shared/floor-placement.d.ts +1 -1
- package/dist/shared/floor-placement.d.ts.map +1 -1
- package/dist/shared/floor-placement.js +13 -33
- package/dist/shared/ghost-materials.d.ts.map +1 -1
- package/dist/shared/ghost-materials.js +2 -1
- package/dist/shared/lean-to-post-omissions.d.ts +8 -0
- package/dist/shared/lean-to-post-omissions.d.ts.map +1 -0
- package/dist/shared/lean-to-post-omissions.js +51 -0
- package/dist/shared/mep-fitting-actions.d.ts +5 -0
- package/dist/shared/mep-fitting-actions.d.ts.map +1 -0
- package/dist/shared/mep-fitting-actions.js +16 -0
- package/dist/shared/mep-ghost.d.ts.map +1 -1
- package/dist/shared/mep-ghost.js +19 -1
- package/dist/shared/mep-presets.d.ts +11 -0
- package/dist/shared/mep-presets.d.ts.map +1 -0
- package/dist/shared/mep-presets.js +34 -0
- package/dist/shared/node-batch/candidates.d.ts +29 -0
- package/dist/shared/node-batch/candidates.d.ts.map +1 -0
- package/dist/shared/node-batch/candidates.js +260 -0
- package/dist/shared/node-batch/store.d.ts +26 -0
- package/dist/shared/node-batch/store.d.ts.map +1 -0
- package/dist/shared/node-batch/store.js +319 -0
- package/dist/shared/node-batch/system.d.ts +8 -0
- package/dist/shared/node-batch/system.d.ts.map +1 -0
- package/dist/shared/node-batch/system.js +569 -0
- package/dist/shared/node-batch/types.d.ts +99 -0
- package/dist/shared/node-batch/types.d.ts.map +1 -0
- package/dist/shared/node-batch/types.js +4 -0
- package/dist/shared/opening-move-history.d.ts +47 -0
- package/dist/shared/opening-move-history.d.ts.map +1 -0
- package/dist/shared/opening-move-history.js +18 -0
- package/dist/shared/opening-move-wall-gate.d.ts +55 -0
- package/dist/shared/opening-move-wall-gate.d.ts.map +1 -0
- package/dist/shared/opening-move-wall-gate.js +44 -0
- package/dist/shared/path-point-affordance.d.ts +2 -2
- package/dist/shared/path-point-affordance.d.ts.map +1 -1
- package/dist/shared/path-point-affordance.js +30 -6
- package/dist/shared/placeholder-geometry.d.ts.map +1 -1
- package/dist/shared/placeholder-geometry.js +4 -0
- package/dist/shared/ports.d.ts +20 -3
- package/dist/shared/ports.d.ts.map +1 -1
- package/dist/shared/ports.js +148 -7
- package/dist/shared/primitive-uv.d.ts +17 -0
- package/dist/shared/primitive-uv.d.ts.map +1 -0
- package/dist/shared/primitive-uv.js +107 -0
- package/dist/shared/reducer-size.d.ts +2 -0
- package/dist/shared/reducer-size.d.ts.map +1 -0
- package/dist/shared/reducer-size.js +12 -0
- package/dist/shared/ridge-snap.js +1 -1
- package/dist/shared/roof-surface.d.ts.map +1 -1
- package/dist/shared/roof-surface.js +17 -2
- package/dist/shared/run-cursor.d.ts +22 -0
- package/dist/shared/run-cursor.d.ts.map +1 -0
- package/dist/shared/run-cursor.js +19 -0
- package/dist/shared/run-direction-feedback.d.ts +20 -0
- package/dist/shared/run-direction-feedback.d.ts.map +1 -0
- package/dist/shared/run-direction-feedback.js +139 -0
- package/dist/shared/run-hanger-controls.d.ts +7 -0
- package/dist/shared/run-hanger-controls.d.ts.map +1 -0
- package/dist/shared/run-hanger-controls.js +44 -0
- package/dist/shared/run-hanger-inspector.d.ts +5 -0
- package/dist/shared/run-hanger-inspector.d.ts.map +1 -0
- package/dist/shared/run-hanger-inspector.js +43 -0
- package/dist/shared/run-hanger-mode.d.ts +11 -0
- package/dist/shared/run-hanger-mode.d.ts.map +1 -0
- package/dist/shared/run-hanger-mode.js +32 -0
- package/dist/shared/run-hanger-system.d.ts +3 -0
- package/dist/shared/run-hanger-system.d.ts.map +1 -0
- package/dist/shared/run-hanger-system.js +39 -0
- package/dist/shared/run-hangers.d.ts +25 -0
- package/dist/shared/run-hangers.d.ts.map +1 -0
- package/dist/shared/run-hangers.js +261 -0
- package/dist/shared/run-port-snap.d.ts +11 -0
- package/dist/shared/run-port-snap.d.ts.map +1 -0
- package/dist/shared/run-port-snap.js +19 -0
- package/dist/shared/run-screen-direction.d.ts +5 -0
- package/dist/shared/run-screen-direction.d.ts.map +1 -0
- package/dist/shared/run-screen-direction.js +24 -0
- package/dist/shared/selection-handles.d.ts +4 -0
- package/dist/shared/selection-handles.d.ts.map +1 -1
- package/dist/shared/selection-handles.js +10 -0
- package/dist/shared/slot-paint.d.ts +8 -1
- package/dist/shared/slot-paint.d.ts.map +1 -1
- package/dist/shared/slot-paint.js +120 -27
- package/dist/shared/surface-paint.d.ts +1 -2
- package/dist/shared/surface-paint.d.ts.map +1 -1
- package/dist/shared/surface-paint.js +9 -20
- package/dist/shared/system-check-panel.d.ts +6 -0
- package/dist/shared/system-check-panel.d.ts.map +1 -0
- package/dist/shared/system-check-panel.js +62 -0
- package/dist/shared/system-checks.d.ts +9 -0
- package/dist/shared/system-checks.d.ts.map +1 -0
- package/dist/shared/system-checks.js +131 -0
- package/dist/shared/wall-attach-target.d.ts +14 -2
- package/dist/shared/wall-attach-target.d.ts.map +1 -1
- package/dist/shared/wall-attach-target.js +108 -4
- package/dist/shared/wall-opening-clearance.d.ts +19 -0
- package/dist/shared/wall-opening-clearance.d.ts.map +1 -0
- package/dist/shared/wall-opening-clearance.js +28 -0
- package/dist/shared/wall-run-move.d.ts +17 -0
- package/dist/shared/wall-run-move.d.ts.map +1 -0
- package/dist/shared/wall-run-move.js +56 -0
- package/dist/shelf/parametrics.js +4 -4
- package/dist/site/renderer.d.ts.map +1 -1
- package/dist/site/renderer.js +40 -28
- package/dist/site/terrain-geometry.d.ts +1 -1
- package/dist/site/terrain-geometry.d.ts.map +1 -1
- package/dist/site/terrain-geometry.js +35 -9
- package/dist/site/terrain-mesh.d.ts +5 -0
- package/dist/site/terrain-mesh.d.ts.map +1 -1
- package/dist/site/terrain-mesh.js +63 -34
- package/dist/skylight/definition.js +1 -1
- package/dist/skylight/panel.js +1 -1
- package/dist/skylight/parametrics.js +2 -2
- package/dist/slab/definition.d.ts.map +1 -1
- package/dist/slab/definition.js +6 -1
- package/dist/slab/dependency-tracker.d.ts +3 -0
- package/dist/slab/dependency-tracker.d.ts.map +1 -0
- package/dist/slab/dependency-tracker.js +104 -0
- package/dist/slab/floorplan-affordances.d.ts +8 -8
- package/dist/slab/geometry.d.ts.map +1 -1
- package/dist/slab/geometry.js +16 -2
- package/dist/slab/panel.d.ts.map +1 -1
- package/dist/slab/panel.js +1 -1
- package/dist/slab/parametrics.js +1 -1
- package/dist/slab/system.d.ts.map +1 -1
- package/dist/slab/system.js +8 -64
- package/dist/solar-panel/definition.js +1 -1
- package/dist/solar-panel/panel.js +1 -1
- package/dist/solar-panel/parametrics.js +2 -2
- package/dist/stair/panel.d.ts.map +1 -1
- package/dist/stair/panel.js +18 -13
- package/dist/stair/stair-type.d.ts +18 -0
- package/dist/stair/stair-type.d.ts.map +1 -0
- package/dist/stair/stair-type.js +25 -0
- package/dist/stair-segment/panel.d.ts.map +1 -1
- package/dist/stair-segment/panel.js +44 -5
- package/dist/turbine-vent/definition.d.ts.map +1 -1
- package/dist/turbine-vent/definition.js +8 -5
- package/dist/turbine-vent/geometry.d.ts.map +1 -1
- package/dist/turbine-vent/geometry.js +48 -18
- package/dist/turbine-vent/paint.d.ts +4 -0
- package/dist/turbine-vent/paint.d.ts.map +1 -0
- package/dist/turbine-vent/paint.js +35 -0
- package/dist/turbine-vent/panel.js +1 -1
- package/dist/turbine-vent/parametrics.js +2 -2
- package/dist/turbine-vent/renderer.d.ts.map +1 -1
- package/dist/turbine-vent/renderer.js +31 -9
- package/dist/wall/curve-eligibility.d.ts +3 -0
- package/dist/wall/curve-eligibility.d.ts.map +1 -0
- package/dist/wall/curve-eligibility.js +10 -0
- package/dist/wall/curve-tool.d.ts +3 -4
- package/dist/wall/curve-tool.d.ts.map +1 -1
- package/dist/wall/curve-tool.js +24 -23
- package/dist/wall/definition.d.ts +1 -1
- package/dist/wall/definition.d.ts.map +1 -1
- package/dist/wall/definition.js +36 -13
- package/dist/wall/floorplan-affordances.d.ts +1 -0
- package/dist/wall/floorplan-affordances.d.ts.map +1 -1
- package/dist/wall/floorplan-affordances.js +46 -7
- package/dist/wall/floorplan-move.d.ts +3 -4
- package/dist/wall/floorplan-move.d.ts.map +1 -1
- package/dist/wall/floorplan-move.js +122 -9
- package/dist/wall/floorplan.d.ts.map +1 -1
- package/dist/wall/floorplan.js +19 -23
- package/dist/wall/move-shared.d.ts.map +1 -1
- package/dist/wall/move-shared.js +10 -7
- package/dist/wall/paint.d.ts.map +1 -1
- package/dist/wall/paint.js +2 -0
- package/dist/wall/panel.d.ts.map +1 -1
- package/dist/wall/panel.js +62 -27
- package/dist/wall/parametrics.js +2 -2
- package/dist/wall/pointer-transparency.d.ts +128 -0
- package/dist/wall/pointer-transparency.d.ts.map +1 -0
- package/dist/wall/pointer-transparency.js +147 -0
- package/dist/wall/renderer.d.ts.map +1 -1
- package/dist/wall/renderer.js +65 -8
- package/dist/wall/selection-hit-owner.d.ts +53 -0
- package/dist/wall/selection-hit-owner.d.ts.map +1 -0
- package/dist/wall/selection-hit-owner.js +79 -0
- package/dist/wall/system.d.ts +5 -0
- package/dist/wall/system.d.ts.map +1 -1
- package/dist/wall/system.js +71 -26
- package/dist/wall/tool.d.ts.map +1 -1
- package/dist/wall/tool.js +29 -27
- package/dist/wall/treatment-level-data.d.ts +6 -1
- package/dist/wall/treatment-level-data.d.ts.map +1 -1
- package/dist/wall/treatment-level-data.js +82 -5
- package/dist/wall/treatments.d.ts +1 -0
- package/dist/wall/treatments.d.ts.map +1 -1
- package/dist/wall/treatments.js +5 -0
- package/dist/wall/wall-batch-system.d.ts +55 -0
- package/dist/wall/wall-batch-system.d.ts.map +1 -0
- package/dist/wall/wall-batch-system.js +486 -0
- package/dist/wall/wall-batch.d.ts +66 -0
- package/dist/wall/wall-batch.d.ts.map +1 -0
- package/dist/wall/wall-batch.js +169 -0
- package/dist/window/definition.d.ts +2 -1
- package/dist/window/definition.d.ts.map +1 -1
- package/dist/window/definition.js +51 -24
- package/dist/window/floorplan-move.d.ts.map +1 -1
- package/dist/window/floorplan-move.js +1 -1
- package/dist/window/move-tool.d.ts +7 -5
- package/dist/window/move-tool.d.ts.map +1 -1
- package/dist/window/move-tool.js +344 -58
- package/dist/window/panel.d.ts.map +1 -1
- package/dist/window/panel.js +2 -0
- package/dist/window/parametrics.js +2 -2
- package/dist/window/renderer.d.ts.map +1 -1
- package/dist/window/renderer.js +5 -7
- package/dist/window/tool.d.ts.map +1 -1
- package/dist/window/tool.js +227 -13
- package/dist/zone/floorplan-affordances.d.ts +12 -12
- package/dist/zone/quantities-panel.d.ts.map +1 -1
- package/dist/zone/quantities-panel.js +4 -3
- package/package.json +11 -9
- package/dist/dormer/panel-window-section.d.ts +0 -14
- package/dist/dormer/panel-window-section.d.ts.map +0 -1
- package/dist/dormer/panel-window-section.js +0 -59
- package/dist/dormer/window-assembly.d.ts +0 -22
- package/dist/dormer/window-assembly.d.ts.map +0 -1
- package/dist/dormer/window-assembly.js +0 -111
- package/dist/dormer/window-frame.d.ts +0 -24
- package/dist/dormer/window-frame.d.ts.map +0 -1
- package/dist/dormer/window-frame.js +0 -100
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
3
|
-
import { DuctSegmentNode
|
|
4
|
-
import {
|
|
5
|
-
import { useViewer } from '@pascal-app/viewer';
|
|
6
|
-
import { Html } from '@react-three/drei';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { DuctSegmentNode } from '@pascal-app/core';
|
|
4
|
+
import { EDITOR_LAYER, triggerSFX, useEditor, usePathDraftPreview, useRegistryToolContext, } from '@pascal-app/editor';
|
|
7
5
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
8
|
-
import {
|
|
6
|
+
import { Euler, Vector3 } from 'three';
|
|
9
7
|
import { getDuctFittingPorts } from '../duct-fitting/ports';
|
|
10
8
|
import { planCrossAtRunBody, planElbowAtPort, planElbowRealign, planTeeAtRunBody, } from '../shared/auto-fitting';
|
|
11
|
-
import {
|
|
9
|
+
import { createDuctRunEndCap, findMatedRunEndCapIds, isRunEndCapPort, } from '../shared/automatic-run-end-cap';
|
|
10
|
+
import { ConnectionFeedback } from '../shared/connection-feedback';
|
|
11
|
+
import { createRunWallAttachment } from '../shared/distribution-run-contract';
|
|
12
|
+
import { DistributionRunCursor, runDistanceSquared as dist2, runSectionHalfSizeM, stepNominalRunSize, useDistributionRunTool, } from '../shared/distribution-run-tool';
|
|
13
|
+
import { ductProfilesMatch, planDuctAdapter } from '../shared/duct-adapter';
|
|
14
|
+
import { FITTING_CLEARANCE_MESSAGE, hasFittingClearance } from '../shared/fitting-clearance';
|
|
12
15
|
import { LevelOffsetGroup } from '../shared/level-offset-group';
|
|
13
|
-
import { FittingGhost } from '../shared/mep-ghost';
|
|
14
|
-
import { collectScenePorts, DUCT_PORT_SYSTEMS,
|
|
16
|
+
import { DuctSegmentGhost, FittingGhost } from '../shared/mep-ghost';
|
|
17
|
+
import { collectScenePorts, DUCT_PORT_SYSTEMS, findNearestRunBody3D, findRunBodyCrossingSurface, } from '../shared/ports';
|
|
18
|
+
import { RunHangerPreview } from '../shared/run-hanger-controls';
|
|
19
|
+
import { useRunHangerMode } from '../shared/run-hanger-mode';
|
|
20
|
+
import { currentDuctContinuationSeed, ductEndpointPort } from './continuation';
|
|
15
21
|
import { ductSegmentDefinition } from './definition';
|
|
16
|
-
import {
|
|
22
|
+
import { rectSectionAxes, rollToContinueAcrossElbow } from './geometry';
|
|
17
23
|
/**
|
|
18
24
|
* Continuous placement tool for duct segments.
|
|
19
25
|
*
|
|
@@ -42,46 +48,15 @@ import { ductPortDiameterIn, rectSectionAxes, rollToContinueAcrossElbow } from '
|
|
|
42
48
|
* vertical mouse motion drives Y. Click commits the riser segment.
|
|
43
49
|
* - **[ / ]** step the duct diameter through nominal US sizes; the
|
|
44
50
|
* ghost preview and the committed node both use it.
|
|
45
|
-
* -
|
|
46
|
-
* the ceiling actually covering it (duct top hugging that ceiling)
|
|
47
|
-
* instead of the floor, so a run tracks per-room ceiling heights.
|
|
48
|
-
* Points not under any ceiling fall back to the floor.
|
|
49
|
-
* - Esc clears an anchored start point.
|
|
51
|
+
* - Esc exits drawing.
|
|
50
52
|
*/
|
|
51
|
-
const PREVIEW_OPACITY = 0.55;
|
|
52
53
|
/**
|
|
53
54
|
* Nominal US round-duct sizes (inches): 4"–10" in 1" steps, 12"+ in 2"
|
|
54
55
|
* steps — matches what flex and rigid round actually ship in.
|
|
55
56
|
*/
|
|
56
57
|
const DUCT_DIAMETERS_IN = [4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20];
|
|
57
|
-
/** Snap radius (meters) for joining onto an existing duct's start/end. */
|
|
58
|
-
const ENDPOINT_SNAP_RADIUS_M = 0.5;
|
|
59
|
-
/** Snap radius (meters) for tapping the SIDE of an existing run — a tee
|
|
60
|
-
* is minted there. Tighter than the port radius so run ends keep
|
|
61
|
-
* priority near their last stretch. */
|
|
62
58
|
const BODY_SNAP_RADIUS_M = 0.35;
|
|
63
59
|
/** Angle step (radians) for the XZ angle lock — 45°. */
|
|
64
|
-
const ANGLE_STEP_RAD = Math.PI / 4;
|
|
65
|
-
/** Mouse pixels → meters mapping for Alt-vertical drag. 100 px ≈ 1 m. */
|
|
66
|
-
const ALT_PIXELS_PER_METER = 100;
|
|
67
|
-
/** Bounds on Alt-driven Y so a wild fling doesn't fly off. */
|
|
68
|
-
const ALT_Y_MIN_M = -3;
|
|
69
|
-
const ALT_Y_MAX_M = 10;
|
|
70
|
-
/** green-500 — the project's bounding-box / placeable accent. The cursor
|
|
71
|
-
* ring + vertical line recolour to this while the point is snapped onto an
|
|
72
|
-
* existing run, so the coincidence reads with the familiar snap green. */
|
|
73
|
-
const SNAP_CURSOR_COLOR = '#22c55e';
|
|
74
|
-
function snap(value, step) {
|
|
75
|
-
if (step <= 0)
|
|
76
|
-
return value;
|
|
77
|
-
return Math.round(value / step) * step;
|
|
78
|
-
}
|
|
79
|
-
function dist2(a, b) {
|
|
80
|
-
const dx = a[0] - b[0];
|
|
81
|
-
const dy = a[1] - b[1];
|
|
82
|
-
const dz = a[2] - b[2];
|
|
83
|
-
return dx * dx + dy * dy + dz * dz;
|
|
84
|
-
}
|
|
85
60
|
/**
|
|
86
61
|
* Cross-section roll for a new rect run leaving `port` along `newDir`,
|
|
87
62
|
* so its profile stays continuous with whatever it joined: a turn
|
|
@@ -92,11 +67,15 @@ function dist2(a, b) {
|
|
|
92
67
|
* there). Null when the port doesn't carry a rect orientation. Shared
|
|
93
68
|
* by the ghost preview and the commit so what you see is what lands.
|
|
94
69
|
*/
|
|
95
|
-
function continuityRollFrom(port, newDir) {
|
|
70
|
+
function continuityRollFrom(port, newDir, nodes) {
|
|
96
71
|
if (!port)
|
|
97
72
|
return null;
|
|
98
|
-
const nodes = useScene.getState().nodes;
|
|
99
73
|
const owner = nodes[port.nodeId];
|
|
74
|
+
if (owner?.type === 'duct-fitting' && ['reducer', 'transition'].includes(owner.fittingType)) {
|
|
75
|
+
const width = new Vector3(0, 0, 1).applyEuler(new Euler(...owner.rotation));
|
|
76
|
+
const basis = rectSectionAxes(newDir);
|
|
77
|
+
return Math.atan2(width.dot(basis.height), width.dot(basis.width));
|
|
78
|
+
}
|
|
100
79
|
let srcDir = null;
|
|
101
80
|
let srcRoll = 0;
|
|
102
81
|
if ((owner?.type === 'hvac-equipment' || owner?.type === 'duct-terminal') &&
|
|
@@ -134,26 +113,26 @@ function continuityRollFrom(port, newDir) {
|
|
|
134
113
|
if (!srcDir)
|
|
135
114
|
return null;
|
|
136
115
|
const cross = new Vector3().crossVectors(srcDir, newDir);
|
|
137
|
-
if (cross.lengthSq() < 1e-8)
|
|
116
|
+
if (cross.lengthSq() < 1e-8) {
|
|
117
|
+
if (owner?.type === 'duct-segment') {
|
|
118
|
+
const i = port.id === 'start' ? 0 : owner.path.length - 2;
|
|
119
|
+
const direction = new Vector3(...owner.path[i + 1]).sub(new Vector3(...owner.path[i]));
|
|
120
|
+
const width = rectSectionAxes(direction, owner.roll).width;
|
|
121
|
+
const basis = rectSectionAxes(newDir);
|
|
122
|
+
return Math.atan2(width.dot(basis.height), width.dot(basis.width));
|
|
123
|
+
}
|
|
138
124
|
return srcRoll;
|
|
125
|
+
}
|
|
139
126
|
return rollToContinueAcrossElbow(srcDir, srcRoll, srcDir, newDir);
|
|
140
127
|
}
|
|
141
|
-
function continuityRollForRun(startPort, endPort, dir) {
|
|
142
|
-
return continuityRollFrom(startPort, dir) ?? continuityRollFrom(endPort, dir) ?? 0;
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Nearest typed port — duct run ends, fitting collars, anything whose
|
|
146
|
-
* kind registers `def.ports` — within snap range of `point` on the XZ
|
|
147
|
-
* plane. Y is ignored for the distance check (grid events ride the floor
|
|
148
|
-
* while ports hang at duct height); the snap adopts the port's full 3D
|
|
149
|
-
* position. The full port is returned so the commit knows what it joined
|
|
150
|
-
* (auto-elbow insertion needs the port's direction and owner).
|
|
151
|
-
*/
|
|
152
|
-
function findNearbyPort(point) {
|
|
153
|
-
return findNearestPortXZ(point, collectScenePorts({ systems: DUCT_PORT_SYSTEMS }), ENDPOINT_SNAP_RADIUS_M);
|
|
128
|
+
function continuityRollForRun(startPort, endPort, dir, nodes) {
|
|
129
|
+
return continuityRollFrom(startPort, dir, nodes) ?? continuityRollFrom(endPort, dir, nodes) ?? 0;
|
|
154
130
|
}
|
|
155
|
-
function
|
|
156
|
-
return
|
|
131
|
+
function getConnectionPorts(levelId, nodes) {
|
|
132
|
+
return collectScenePorts({
|
|
133
|
+
systems: DUCT_PORT_SYSTEMS,
|
|
134
|
+
levelId: levelId ?? undefined,
|
|
135
|
+
}).filter((port) => !isRunEndCapPort(port, nodes));
|
|
157
136
|
}
|
|
158
137
|
/**
|
|
159
138
|
* Profile to inherit when the segment start snaps onto `port` — joining
|
|
@@ -161,16 +140,16 @@ function portPoint(port) {
|
|
|
161
140
|
* run / fitting collar keeps its diameter. Equipment and terminal
|
|
162
141
|
* collars are round at the port's advertised size.
|
|
163
142
|
*/
|
|
164
|
-
function inheritProfile(port) {
|
|
165
|
-
const owner =
|
|
143
|
+
function inheritProfile(port, nodes) {
|
|
144
|
+
const owner = nodes[port.nodeId];
|
|
166
145
|
if (!owner)
|
|
167
146
|
return null;
|
|
168
147
|
if (owner.type === 'duct-segment' || owner.type === 'duct-fitting') {
|
|
169
148
|
return {
|
|
170
|
-
shape: owner.shape,
|
|
149
|
+
shape: port.shape ?? owner.shape,
|
|
171
150
|
diameter: Math.min(48, Math.max(2, owner.type === 'duct-segment' ? owner.diameter : port.diameter)),
|
|
172
|
-
width: owner.width,
|
|
173
|
-
height: owner.height,
|
|
151
|
+
width: port.width ?? owner.width,
|
|
152
|
+
height: port.height ?? owner.height,
|
|
174
153
|
};
|
|
175
154
|
}
|
|
176
155
|
if (owner.type === 'hvac-equipment' || owner.type === 'duct-terminal') {
|
|
@@ -195,52 +174,38 @@ function inheritProfile(port) {
|
|
|
195
174
|
}
|
|
196
175
|
return null;
|
|
197
176
|
}
|
|
198
|
-
|
|
199
|
-
* Project `raw` onto the nearest of the eight 45° rays emanating from
|
|
200
|
-
* `from` in the XZ plane. Y is preserved from `from`. The projection
|
|
201
|
-
* keeps the cursor's *distance* along the chosen ray so the user feels
|
|
202
|
-
* the segment grow with their mouse motion rather than snap to a fixed
|
|
203
|
-
* length.
|
|
204
|
-
*/
|
|
205
|
-
function projectToAngleLock(from, raw) {
|
|
206
|
-
const dx = raw[0] - from[0];
|
|
207
|
-
const dz = raw[2] - from[2];
|
|
208
|
-
const len = Math.hypot(dx, dz);
|
|
209
|
-
if (len < 1e-4)
|
|
210
|
-
return [from[0], from[1], from[2]];
|
|
211
|
-
const theta = Math.atan2(dz, dx);
|
|
212
|
-
const snapped = Math.round(theta / ANGLE_STEP_RAD) * ANGLE_STEP_RAD;
|
|
213
|
-
// Distance along the chosen ray = projection of raw onto that direction.
|
|
214
|
-
const proj = dx * Math.cos(snapped) + dz * Math.sin(snapped);
|
|
215
|
-
const d = Math.max(0, proj);
|
|
216
|
-
return [from[0] + Math.cos(snapped) * d, from[1], from[2] + Math.sin(snapped) * d];
|
|
217
|
-
}
|
|
218
|
-
const elbowPlanFor = (port, awayDir, profile) => {
|
|
177
|
+
const elbowPlanFor = (port, awayDir, profile, nodes) => {
|
|
219
178
|
if (!port)
|
|
220
179
|
return null;
|
|
221
|
-
const owner =
|
|
180
|
+
const owner = nodes[port.nodeId];
|
|
222
181
|
if (owner?.type !== 'duct-segment')
|
|
223
182
|
return null;
|
|
224
|
-
const
|
|
183
|
+
const source = inheritProfile(port, nodes) ?? profile;
|
|
184
|
+
const plan = planElbowAtPort(port, awayDir, source);
|
|
225
185
|
if (!plan)
|
|
226
186
|
return null;
|
|
227
187
|
// Trim the run's snapped endpoint back to the elbow's inlet collar.
|
|
228
188
|
const path = owner.path.map((p) => [...p]);
|
|
229
189
|
const index = port.id === 'start' ? 0 : path.length - 1;
|
|
230
190
|
const neighbor = path[index === 0 ? 1 : index - 1];
|
|
231
|
-
const remaining = Math.hypot(plan.trimmedPortPoint[0] - neighbor[0], plan.trimmedPortPoint[1] - neighbor[1], plan.trimmedPortPoint[2] - neighbor[2]);
|
|
232
|
-
// The trim must leave a real piece of the existing run AND not flip it.
|
|
233
191
|
const original = path[index];
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
192
|
+
const direction = [
|
|
193
|
+
original[0] - neighbor[0],
|
|
194
|
+
original[1] - neighbor[1],
|
|
195
|
+
original[2] - neighbor[2],
|
|
196
|
+
];
|
|
197
|
+
const hasClearance = hasFittingClearance(neighbor, plan.trimmedPortPoint, direction, 0.08);
|
|
237
198
|
path[index] = plan.trimmedPortPoint;
|
|
238
|
-
return {
|
|
199
|
+
return {
|
|
200
|
+
...plan,
|
|
201
|
+
hasClearance,
|
|
202
|
+
trim: { id: port.nodeId, data: { path } },
|
|
203
|
+
};
|
|
239
204
|
};
|
|
240
|
-
const realignPlanFor = (port, awayDir) => {
|
|
205
|
+
const realignPlanFor = (port, awayDir, nodes) => {
|
|
241
206
|
if (!port)
|
|
242
207
|
return null;
|
|
243
|
-
const owner =
|
|
208
|
+
const owner = nodes[port.nodeId];
|
|
244
209
|
if (owner?.type !== 'duct-fitting')
|
|
245
210
|
return null;
|
|
246
211
|
return planElbowRealign(owner, port.id, awayDir);
|
|
@@ -251,10 +216,10 @@ const realignPlanFor = (port, awayDir) => {
|
|
|
251
216
|
* tap), decide every node the commit creates / updates — auto-inserted
|
|
252
217
|
* elbows / tees / crosses, the drawn run (split in two when it crosses a
|
|
253
218
|
* trunk), trunk tails, and trim / realign updates. Reads the live scene
|
|
254
|
-
* graph but mutates nothing, so the live preview can call it each
|
|
255
|
-
* to ghost the fittings before the commit applies the identical plan.
|
|
219
|
+
* graph snapshot but mutates nothing, so the live preview can call it each
|
|
220
|
+
* frame to ghost the fittings before the commit applies the identical plan.
|
|
256
221
|
*/
|
|
257
|
-
function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profile) {
|
|
222
|
+
export function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profile, nodes, surface, autoHangers = false, toolDefaults = {}, hangerStyle = 'single') {
|
|
258
223
|
const length = Math.hypot(end[0] - start[0], end[1] - start[1], end[2] - start[2]);
|
|
259
224
|
if (length < 1e-4)
|
|
260
225
|
return null;
|
|
@@ -263,56 +228,107 @@ function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profil
|
|
|
263
228
|
(end[1] - start[1]) / length,
|
|
264
229
|
(end[2] - start[2]) / length,
|
|
265
230
|
];
|
|
266
|
-
const startPlan = elbowPlanFor(startPort, dir, profile);
|
|
267
|
-
const endPlan = elbowPlanFor(endPort, [-dir[0], -dir[1], -dir[2]], profile);
|
|
268
|
-
const
|
|
269
|
-
|
|
231
|
+
const startPlan = elbowPlanFor(startPort, dir, profile, nodes);
|
|
232
|
+
const endPlan = elbowPlanFor(endPort, [-dir[0], -dir[1], -dir[2]], profile, nodes);
|
|
233
|
+
const invalidPlan = () => ({
|
|
234
|
+
validationMessage: FITTING_CLEARANCE_MESSAGE,
|
|
235
|
+
fittings: [],
|
|
236
|
+
ducts: [],
|
|
237
|
+
tails: [],
|
|
238
|
+
updates: [],
|
|
239
|
+
delete: [],
|
|
240
|
+
});
|
|
241
|
+
if (startPlan?.hasClearance === false || endPlan?.hasClearance === false)
|
|
242
|
+
return invalidPlan();
|
|
243
|
+
const startRealign = startPlan ? null : realignPlanFor(startPort, dir, nodes);
|
|
244
|
+
const endRealign = endPlan ? null : realignPlanFor(endPort, [-dir[0], -dir[1], -dir[2]], nodes);
|
|
270
245
|
const trunkBody = startPlan ? null : startBody;
|
|
271
|
-
const trunkOwner = trunkBody ?
|
|
246
|
+
const trunkOwner = trunkBody ? nodes[trunkBody.nodeId] : null;
|
|
272
247
|
const teePlan = trunkBody && trunkOwner?.type === 'duct-segment'
|
|
273
248
|
? planTeeAtRunBody(trunkOwner, trunkBody, dir, profile)
|
|
274
249
|
: null;
|
|
275
250
|
const endTrunkBody = endPlan || endRealign ? null : endBody;
|
|
276
|
-
const endTrunkOwner = endTrunkBody ?
|
|
251
|
+
const endTrunkOwner = endTrunkBody ? nodes[endTrunkBody.nodeId] : null;
|
|
277
252
|
const endTeePlan = endTrunkBody && endTrunkOwner?.type === 'duct-segment'
|
|
278
253
|
? planTeeAtRunBody(endTrunkOwner, endTrunkBody, [-dir[0], -dir[1], -dir[2]], profile)
|
|
279
254
|
: null;
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
255
|
+
const adapterFor = (port, corner, away) => {
|
|
256
|
+
if (!port)
|
|
257
|
+
return null;
|
|
258
|
+
const source = inheritProfile(port, nodes);
|
|
259
|
+
if (!source)
|
|
260
|
+
return null;
|
|
261
|
+
const axis = new Vector3(...away);
|
|
262
|
+
if (!corner && axis.dot(new Vector3(...port.direction).normalize()) < 0.9999)
|
|
263
|
+
return null;
|
|
264
|
+
const owner = nodes[port.nodeId];
|
|
265
|
+
const roll = continuityRollFrom(port, axis, nodes) ?? 0;
|
|
266
|
+
const width = rectSectionAxes(axis, roll).width;
|
|
267
|
+
if (owner?.type === 'duct-fitting' && !corner) {
|
|
268
|
+
width.set(0, 0, 1).applyEuler(new Euler(...owner.rotation));
|
|
269
|
+
}
|
|
270
|
+
return planDuctAdapter({ ...port, position: corner?.collarPoint ?? port.position, direction: away }, source, profile, width);
|
|
271
|
+
};
|
|
272
|
+
const startAdapter = adapterFor(startPort, startPlan, dir);
|
|
273
|
+
const endAdapter = adapterFor(endPort, endPlan, [-dir[0], -dir[1], -dir[2]]);
|
|
274
|
+
for (const [port, adapter] of [
|
|
275
|
+
[startPort, startAdapter],
|
|
276
|
+
[endPort, endAdapter],
|
|
277
|
+
]) {
|
|
278
|
+
const source = port ? inheritProfile(port, nodes) : null;
|
|
279
|
+
if (source && !ductProfilesMatch(source, profile) && !adapter) {
|
|
280
|
+
return {
|
|
281
|
+
...invalidPlan(),
|
|
282
|
+
validationMessage: 'Align the connection or draw a 15–90° bend to fit the profile change.',
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const ductStart = startAdapter?.collarPoint ??
|
|
287
|
+
startPlan?.collarPoint ??
|
|
288
|
+
teePlan?.branchCollar ??
|
|
289
|
+
startRealign?.collarPoint ??
|
|
290
|
+
start;
|
|
291
|
+
let ductEnd = endAdapter?.collarPoint ??
|
|
292
|
+
endPlan?.collarPoint ??
|
|
293
|
+
endTeePlan?.branchCollar ??
|
|
294
|
+
endRealign?.collarPoint ??
|
|
295
|
+
end;
|
|
296
|
+
const plans = [startPlan, endPlan].filter((p) => p !== null);
|
|
297
|
+
const tee = teePlan;
|
|
285
298
|
let endTee = endTeePlan && endTrunkBody?.nodeId === trunkBody?.nodeId ? null : endTeePlan;
|
|
286
299
|
if (!endTee && endTeePlan)
|
|
287
300
|
ductEnd = endRealign?.collarPoint ?? end;
|
|
288
|
-
|
|
289
|
-
const crossHit =
|
|
290
|
-
|
|
301
|
+
const realigns = [startRealign, endRealign].filter((p) => p !== null);
|
|
302
|
+
const crossHit = surface
|
|
303
|
+
? findRunBodyCrossingSurface(start, end, BODY_SNAP_RADIUS_M, surface)
|
|
304
|
+
: null;
|
|
305
|
+
const crossOwner = crossHit ? nodes[crossHit.nodeId] : null;
|
|
291
306
|
const crossTappedElsewhere = crossHit?.nodeId === trunkBody?.nodeId || crossHit?.nodeId === endTrunkBody?.nodeId;
|
|
292
|
-
|
|
307
|
+
const cross = crossHit && !crossTappedElsewhere && crossOwner?.type === 'duct-segment'
|
|
293
308
|
? planCrossAtRunBody(crossOwner, crossHit, dir, profile)
|
|
294
309
|
: null;
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
ductStart
|
|
302
|
-
|
|
303
|
-
|
|
310
|
+
if (!hasFittingClearance(ductStart, ductEnd, dir, 0.08) ||
|
|
311
|
+
(trunkBody && !teePlan) ||
|
|
312
|
+
(endTrunkBody && !endTeePlan) ||
|
|
313
|
+
(crossHit && !crossTappedElsewhere && !cross))
|
|
314
|
+
return invalidPlan();
|
|
315
|
+
if (cross &&
|
|
316
|
+
(!hasFittingClearance(ductStart, cross.branchCollarNear, dir, 0.08) ||
|
|
317
|
+
!hasFittingClearance(cross.branchCollarFar, ductEnd, dir, 0.08)))
|
|
318
|
+
return invalidPlan();
|
|
304
319
|
// Rect / oval continuity: roll the new run's cross-section so its
|
|
305
320
|
// profile stays continuous with whatever either end joined.
|
|
306
321
|
let roll = 0;
|
|
307
322
|
if (profile.shape !== 'round') {
|
|
308
323
|
const newDir = new Vector3(...dir);
|
|
309
|
-
roll = continuityRollForRun(startPort, endPort, newDir);
|
|
324
|
+
roll = continuityRollForRun(startPort, endPort, newDir, nodes);
|
|
310
325
|
}
|
|
311
326
|
const defaults = ductSegmentDefinition.defaults();
|
|
312
|
-
const toolDefaults = useEditor.getState().toolDefaults['duct-segment'] ?? {};
|
|
313
327
|
const makeDuct = (from, to) => DuctSegmentNode.parse({
|
|
314
328
|
...defaults,
|
|
315
329
|
...toolDefaults,
|
|
330
|
+
autoHangers,
|
|
331
|
+
hangerStyle,
|
|
316
332
|
name: profile.shape === 'rect' ? 'Trunk' : 'Duct run',
|
|
317
333
|
path: [from, to],
|
|
318
334
|
shape: profile.shape,
|
|
@@ -333,6 +349,7 @@ function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profil
|
|
|
333
349
|
: [makeDuct(ductStart, ductEnd)];
|
|
334
350
|
const fittings = [
|
|
335
351
|
...plans.map((p) => p.fitting),
|
|
352
|
+
...[startAdapter, endAdapter].flatMap((p) => (p ? [p.fitting] : [])),
|
|
336
353
|
...(tee ? [tee.fitting] : []),
|
|
337
354
|
...(endTee ? [endTee.fitting] : []),
|
|
338
355
|
...(cross ? [cross.fitting] : []),
|
|
@@ -349,587 +366,197 @@ function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profil
|
|
|
349
366
|
...(cross ? [cross.trunkUpdate] : []),
|
|
350
367
|
...realigns.map((p) => p.update),
|
|
351
368
|
];
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const direction = len < 1e-9 ? [1, 0, 0] : [dx / len, dy / len, dz / len];
|
|
366
|
-
return {
|
|
367
|
-
id,
|
|
368
|
-
nodeId: duct.id,
|
|
369
|
-
position,
|
|
370
|
-
direction,
|
|
371
|
-
diameter: ductPortDiameterIn(duct),
|
|
372
|
-
system: duct.system,
|
|
373
|
-
};
|
|
369
|
+
const deleteIds = Array.from(new Set([
|
|
370
|
+
...findMatedRunEndCapIds(startPort, nodes, 'duct-fitting'),
|
|
371
|
+
...findMatedRunEndCapIds(endPort, nodes, 'duct-fitting'),
|
|
372
|
+
]));
|
|
373
|
+
const firstDuct = ducts[0];
|
|
374
|
+
const nextDuct = ducts.at(-1);
|
|
375
|
+
const startEndCap = !startPort && !startBody && firstDuct ? createDuctRunEndCap(firstDuct, 'start') : null;
|
|
376
|
+
const nextEndCap = !endPort && !endBody && nextDuct ? createDuctRunEndCap(nextDuct) : null;
|
|
377
|
+
if (startEndCap)
|
|
378
|
+
fittings.push(startEndCap);
|
|
379
|
+
if (nextEndCap)
|
|
380
|
+
fittings.push(nextEndCap);
|
|
381
|
+
return { validationMessage: null, fittings, ducts, tails, updates, delete: deleteIds };
|
|
374
382
|
}
|
|
375
383
|
const DuctSegmentTool = () => {
|
|
376
|
-
const activeLevelId =
|
|
377
|
-
const unit = useViewer((s) => s.unit);
|
|
384
|
+
const { activeLevelId, sceneApi, unit } = useRegistryToolContext();
|
|
378
385
|
const cursorRef = useRef(null);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
386
|
+
const continuationSeedRef = useRef(currentDuctContinuationSeed());
|
|
387
|
+
const continuationSeed = continuationSeedRef.current;
|
|
388
|
+
const hangerDefaults = useEditor((state) => state.toolDefaults['duct-segment']);
|
|
389
|
+
const initialAutoHangersRef = useRef(Boolean(hangerDefaults?.autoHangers ?? continuationSeed?.duct.autoHangers ?? false));
|
|
390
|
+
const autoHangers = useRunHangerMode((state) => state.enabled['duct-segment']);
|
|
391
|
+
const hangerStyle = (hangerDefaults?.hangerStyle ?? continuationSeed?.duct.hangerStyle) === 'double'
|
|
392
|
+
? 'double'
|
|
393
|
+
: 'single';
|
|
394
|
+
const hangerStyleRef = useRef(hangerStyle);
|
|
395
|
+
hangerStyleRef.current = hangerStyle;
|
|
396
|
+
const autoHangersRef = useRef(autoHangers);
|
|
397
|
+
autoHangersRef.current = autoHangers;
|
|
398
|
+
const pendingPromotionRef = useRef(continuationSeed?.promotedFitting ?? null);
|
|
384
399
|
const [profile, setProfile] = useState(() => {
|
|
385
400
|
const defaults = ductSegmentDefinition.defaults();
|
|
386
401
|
const seeded = useEditor.getState().toolDefaults['duct-segment'];
|
|
387
402
|
return {
|
|
388
|
-
shape: seeded?.shape ?? defaults.shape,
|
|
389
|
-
diameter: seeded?.diameter ?? defaults.diameter,
|
|
390
|
-
width: seeded?.width ?? defaults.width,
|
|
391
|
-
height: seeded?.height ?? defaults.height,
|
|
403
|
+
shape: continuationSeed?.duct.shape ?? seeded?.shape ?? defaults.shape,
|
|
404
|
+
diameter: continuationSeed?.duct.diameter ?? seeded?.diameter ?? defaults.diameter,
|
|
405
|
+
width: continuationSeed?.duct.width ?? seeded?.width ?? defaults.width,
|
|
406
|
+
height: continuationSeed?.duct.height ?? seeded?.height ?? defaults.height,
|
|
392
407
|
};
|
|
393
408
|
});
|
|
394
|
-
const [draftPoints, setDraftPoints] = useState([]);
|
|
395
|
-
const [cursorPos, setCursorPos] = useState(null);
|
|
396
|
-
// Ceiling mode (toggle with C): the first point lands at the level's
|
|
397
|
-
// ceiling height (duct top hugging the ceiling) instead of the floor.
|
|
398
|
-
const [ceilingMode, setCeilingMode] = useState(false);
|
|
399
|
-
// The shared coordinate when the cursor is within snap range of an existing
|
|
400
|
-
// duct (null = free placement). Drives the green cursor highlight so the
|
|
401
|
-
// user sees the next click will join an existing run, not freeform-place.
|
|
402
|
-
const [snapTarget, setSnapTarget] = useState(null);
|
|
403
|
-
// In ceiling mode, the ceiling the cursor is currently under — rendered as
|
|
404
|
-
// a translucent overlay so the duct reads as hung against a real surface
|
|
405
|
-
// rather than a dot floating in space. Null when off-ceiling.
|
|
406
|
-
const [hoverCeiling, setHoverCeiling] = useState(null);
|
|
407
|
-
// True while Alt is held with a last point on the draft — drives the
|
|
408
|
-
// vertical-cylinder ghost and the cursor HUD label.
|
|
409
|
-
const [altActive, setAltActive] = useState(false);
|
|
410
|
-
// What the in-flight cursor end currently snaps onto (port end, or a
|
|
411
|
-
// run body for a tee / cross tap). Drives the auto-fitting GHOST so the
|
|
412
|
-
// user sees the elbow / tee / cross the next click will mint.
|
|
413
|
-
const [endSnap, setEndSnap] = useState({
|
|
414
|
-
port: null,
|
|
415
|
-
body: null,
|
|
416
|
-
});
|
|
417
|
-
// Mirror into refs so emitter callbacks (closing over the first render's
|
|
418
|
-
// setState) read the latest values without re-subscribing.
|
|
419
|
-
const draftRef = useRef(draftPoints);
|
|
420
|
-
draftRef.current = draftPoints;
|
|
421
|
-
const cursorPosRef = useRef(cursorPos);
|
|
422
|
-
cursorPosRef.current = cursorPos;
|
|
423
409
|
const profileRef = useRef(profile);
|
|
424
410
|
profileRef.current = profile;
|
|
425
|
-
const ceilingModeRef = useRef(ceilingMode);
|
|
426
|
-
ceilingModeRef.current = ceilingMode;
|
|
427
|
-
// Port the anchored START point snapped onto (null = free placement).
|
|
428
|
-
// Read at commit so a turn off an existing run mints an elbow there.
|
|
429
|
-
const startPortRef = useRef(null);
|
|
430
|
-
// Centerline hit the anchored START point snapped onto (null = none).
|
|
431
|
-
// Read at commit so a branch off a trunk's side mints a tee there.
|
|
432
|
-
const startBodyRef = useRef(null);
|
|
433
|
-
// Anchor captured when Alt is pressed: screen Y at that moment and the
|
|
434
|
-
// base elevation (= last point's Y). Cleared on Alt release.
|
|
435
|
-
const altAnchorRef = useRef(null);
|
|
436
|
-
// Latest mouse clientY from grid:move; used so the Alt anchor knows where
|
|
437
|
-
// the cursor was at key-press time.
|
|
438
|
-
const lastClientYRef = useRef(null);
|
|
439
|
-
const ghostFittings = useMemo(() => {
|
|
440
|
-
const last = draftPoints.at(-1);
|
|
441
|
-
if (!(activeLevelId && last && cursorPos) || altActive)
|
|
442
|
-
return [];
|
|
443
|
-
const fittings = planDuctDraw(last, cursorPos, startPortRef.current, startBodyRef.current, endSnap.port, endSnap.body, profile)?.fittings ?? [];
|
|
444
|
-
return fittings.map((fitting, index) => ({
|
|
445
|
-
...fitting,
|
|
446
|
-
id: `duct-fitting_live-draft-${index}`,
|
|
447
|
-
parentId: activeLevelId,
|
|
448
|
-
}));
|
|
449
|
-
}, [activeLevelId, altActive, cursorPos, draftPoints, endSnap, profile]);
|
|
450
411
|
useEffect(() => {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}, [
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
412
|
+
const mode = useRunHangerMode.getState();
|
|
413
|
+
mode.setEnabled('duct-segment', initialAutoHangersRef.current);
|
|
414
|
+
return () => mode.setEnabled('duct-segment', false);
|
|
415
|
+
}, []);
|
|
416
|
+
const run = useDistributionRunTool({
|
|
417
|
+
active: !!activeLevelId,
|
|
418
|
+
levelId: activeLevelId,
|
|
419
|
+
toolName: 'duct-segment',
|
|
420
|
+
initialStart: continuationSeed
|
|
421
|
+
? [...(continuationSeed.port?.position ?? continuationSeed.body?.point ?? [0, 0, 0])]
|
|
422
|
+
: null,
|
|
423
|
+
initialConnection: continuationSeed
|
|
424
|
+
? { port: continuationSeed.port, body: continuationSeed.body }
|
|
425
|
+
: null,
|
|
426
|
+
getPorts: () => getConnectionPorts(activeLevelId, sceneApi.nodes()),
|
|
427
|
+
findBody: (point) => findNearestRunBody3D(point, BODY_SNAP_RADIUS_M, { levelId: activeLevelId ?? undefined }),
|
|
428
|
+
surfaceClearance: (surface) => surface
|
|
429
|
+
? runSectionHalfSizeM(profileRef.current.shape === 'round'
|
|
430
|
+
? profileRef.current.diameter
|
|
431
|
+
: profileRef.current.height)
|
|
432
|
+
: 0,
|
|
433
|
+
minimumSegmentLength: 0.08,
|
|
434
|
+
inheritFromConnection: ({ port }) => {
|
|
435
|
+
if (!port)
|
|
469
436
|
return;
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
437
|
+
const inherited = inheritProfile(port, sceneApi.nodes());
|
|
438
|
+
if (inherited)
|
|
439
|
+
setProfile(inherited);
|
|
440
|
+
},
|
|
441
|
+
commit: ({ start, end, startConnection, endConnection, surfaceTarget }) => {
|
|
442
|
+
if (!activeLevelId)
|
|
443
|
+
return null;
|
|
444
|
+
const promotedFitting = pendingPromotionRef.current;
|
|
445
|
+
const plan = planDuctDraw(start, end, promotedFitting ? null : startConnection.port, startConnection.body, endConnection.port, endConnection.body, profileRef.current, sceneApi.nodes(), surfaceTarget, autoHangersRef.current, hangerDefaults, hangerStyleRef.current);
|
|
446
|
+
if (!plan || plan.validationMessage)
|
|
447
|
+
return null;
|
|
448
|
+
const attachDuct = (node) => {
|
|
449
|
+
const wallAttachment = surfaceTarget?.kind === 'wall'
|
|
450
|
+
? createRunWallAttachment(surfaceTarget.hostId, surfaceTarget.side, node.path[0], node.path.at(-1), surfaceTarget, profileRef.current.shape === 'round'
|
|
451
|
+
? runSectionHalfSizeM(profileRef.current.diameter)
|
|
452
|
+
: runSectionHalfSizeM(profileRef.current.height))
|
|
453
|
+
: undefined;
|
|
454
|
+
return { ...node, wallAttachment };
|
|
455
|
+
};
|
|
456
|
+
const ducts = plan.ducts.map(attachDuct);
|
|
457
|
+
const tails = plan.tails;
|
|
458
|
+
if (!sceneApi.applyChanges)
|
|
459
|
+
throw new Error('Registry SceneApi must support atomic changes');
|
|
460
|
+
sceneApi.applyChanges({
|
|
473
461
|
create: [
|
|
474
462
|
...plan.fittings.map((node) => ({ node, parentId: activeLevelId })),
|
|
475
|
-
...
|
|
476
|
-
...
|
|
463
|
+
...tails.map((node) => ({ node, parentId: activeLevelId })),
|
|
464
|
+
...ducts.map((node) => ({ node, parentId: activeLevelId })),
|
|
477
465
|
],
|
|
478
|
-
update:
|
|
466
|
+
update: [
|
|
467
|
+
...(promotedFitting
|
|
468
|
+
? [
|
|
469
|
+
{
|
|
470
|
+
id: promotedFitting.id,
|
|
471
|
+
data: {
|
|
472
|
+
name: promotedFitting.name,
|
|
473
|
+
fittingType: promotedFitting.fittingType,
|
|
474
|
+
rotation: promotedFitting.rotation,
|
|
475
|
+
branchAngle: promotedFitting.branchAngle,
|
|
476
|
+
shape2: promotedFitting.shape2,
|
|
477
|
+
width2: promotedFitting.width2,
|
|
478
|
+
height2: promotedFitting.height2,
|
|
479
|
+
diameter2: promotedFitting.diameter2,
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
]
|
|
483
|
+
: []),
|
|
484
|
+
...plan.updates,
|
|
485
|
+
],
|
|
486
|
+
delete: plan.delete,
|
|
479
487
|
});
|
|
488
|
+
pendingPromotionRef.current = null;
|
|
480
489
|
const nextDuct = plan.ducts.at(-1);
|
|
481
490
|
const nextStart = nextDuct ? nextDuct.path[nextDuct.path.length - 1] : end;
|
|
482
|
-
const nextPort = nextDuct ?
|
|
483
|
-
triggerSFX('sfx:item-place');
|
|
484
|
-
setDraftPoints([nextStart]);
|
|
485
|
-
setSnapTarget(null);
|
|
486
|
-
setEndSnap({ port: null, body: null });
|
|
487
|
-
startPortRef.current = nextPort;
|
|
488
|
-
startBodyRef.current = nextPort ? null : endBody;
|
|
489
|
-
altAnchorRef.current = null;
|
|
490
|
-
setAltActive(false);
|
|
491
|
-
};
|
|
492
|
-
// Y for a point at level-local `[x, z]`. Floor (0) when ceiling mode is
|
|
493
|
-
// off. In ceiling mode, query the ceiling actually covering that point
|
|
494
|
-
// and hang the duct just below it (centerline = ceiling underside −
|
|
495
|
-
// half the duct's vertical dimension) so its top hugs the ceiling. Each
|
|
496
|
-
// point follows its own ceiling, so a run stepping into a room with a
|
|
497
|
-
// different ceiling height tracks that change. Points not under any
|
|
498
|
-
// ceiling fall back to the floor.
|
|
499
|
-
const resolveCeilingY = (x, z) => {
|
|
500
|
-
if (!ceilingModeRef.current)
|
|
501
|
-
return 0;
|
|
502
|
-
const ceiling = getCeilingHeightAt(activeLevelId, useScene.getState().nodes, x, z);
|
|
503
|
-
if (ceiling === null)
|
|
504
|
-
return 0;
|
|
505
|
-
const p = profileRef.current;
|
|
506
|
-
const verticalIn = p.shape === 'round' ? p.diameter : p.height;
|
|
507
|
-
return Math.max(0, ceiling - (verticalIn * 0.0254) / 2);
|
|
508
|
-
};
|
|
509
|
-
const resolveSnappedPoint = (event) => {
|
|
510
|
-
// Port / body mating is the run's primary affordance; it stays on in
|
|
511
|
-
// every snapping mode except `off` (the raw-cursor bypass).
|
|
512
|
-
const snapEnabled = isGridSnapActive() || isMagneticSnapActive() || isAngleSnapActive();
|
|
513
|
-
const last = draftRef.current.at(-1);
|
|
514
|
-
// First point of the run: grid-snapped placement. Y follows the
|
|
515
|
-
// ceiling under the cursor in ceiling mode (floor otherwise).
|
|
516
|
-
// Endpoint snap can still join an existing run.
|
|
517
|
-
if (!last) {
|
|
518
|
-
const baseY = resolveCeilingY(event.localPosition[0], event.localPosition[2]);
|
|
519
|
-
const raw = [
|
|
520
|
-
event.localPosition[0],
|
|
521
|
-
baseY,
|
|
522
|
-
event.localPosition[2],
|
|
523
|
-
];
|
|
524
|
-
const step = isGridSnapActive() ? useEditor.getState().gridSnapStep : 0;
|
|
525
|
-
if (event.nativeEvent?.altKey !== true && snapEnabled) {
|
|
526
|
-
const target = findNearbyPort(raw);
|
|
527
|
-
if (target)
|
|
528
|
-
return {
|
|
529
|
-
point: portPoint(target),
|
|
530
|
-
snapped: portPoint(target),
|
|
531
|
-
port: target,
|
|
532
|
-
body: null,
|
|
533
|
-
};
|
|
534
|
-
// No open end nearby — try the side of a run (tee tap). Probe
|
|
535
|
-
// with a grid-snapped cursor so the tap steps along the duct
|
|
536
|
-
// like every other placement; `off` mode (step 0) rides smoothly.
|
|
537
|
-
const probe = [snap(raw[0], step), baseY, snap(raw[2], step)];
|
|
538
|
-
const body = findNearestRunBodyXZ(probe, BODY_SNAP_RADIUS_M);
|
|
539
|
-
if (body)
|
|
540
|
-
return { point: body.point, snapped: body.point, port: null, body };
|
|
541
|
-
}
|
|
542
|
-
const sx = snap(raw[0], step);
|
|
543
|
-
const sz = snap(raw[2], step);
|
|
544
|
-
return {
|
|
545
|
-
point: [sx, resolveCeilingY(sx, sz), sz],
|
|
546
|
-
snapped: null,
|
|
547
|
-
port: null,
|
|
548
|
-
body: null,
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
// Subsequent points: angle-locked to 45° from `last` in `angles` mode.
|
|
552
|
-
// Y inherits `last[1]` for the angle/probe math; the free placement below
|
|
553
|
-
// re-resolves it from the ceiling under the point in ceiling mode, so a run
|
|
554
|
-
// stepping into a room with a different ceiling height tracks that change.
|
|
555
|
-
// Depth changes otherwise come from Alt-vertical risers.
|
|
556
|
-
const rawXZ = [
|
|
557
|
-
event.localPosition[0],
|
|
558
|
-
last[1],
|
|
559
|
-
event.localPosition[2],
|
|
560
|
-
];
|
|
561
|
-
// The 45° lock is now the `angles` snapping mode (Shift cycles to it),
|
|
562
|
-
// not a held key.
|
|
563
|
-
const angled = isAngleSnapActive() ? projectToAngleLock(last, rawXZ) : rawXZ;
|
|
564
|
-
const step = isGridSnapActive() ? useEditor.getState().gridSnapStep : 0;
|
|
565
|
-
// Port snap (Alt bypass) — checked against the RAW cursor, not the
|
|
566
|
-
// angle-locked projection, so a port slightly off the 45° ray can
|
|
567
|
-
// still capture the cursor. Joining beats the lock.
|
|
568
|
-
if (event.nativeEvent?.altKey !== true && snapEnabled) {
|
|
569
|
-
const target = findNearbyPort(rawXZ);
|
|
570
|
-
if (target)
|
|
571
|
-
return { point: portPoint(target), snapped: portPoint(target), port: target, body: null };
|
|
572
|
-
// No open end nearby — landing on the side of a run taps a tee
|
|
573
|
-
// there (mirror of the first-point tee tap). Probe with a
|
|
574
|
-
// grid-snapped cursor so the tap steps along the duct instead of
|
|
575
|
-
// sliding smoothly (Shift above frees it). Checked against the
|
|
576
|
-
// cursor, not the 45° projection, so a slightly-off trunk captures.
|
|
577
|
-
const probe = [
|
|
578
|
-
snap(rawXZ[0], step),
|
|
579
|
-
rawXZ[1],
|
|
580
|
-
snap(rawXZ[2], step),
|
|
581
|
-
];
|
|
582
|
-
const body = findNearestRunBodyXZ(probe, BODY_SNAP_RADIUS_M);
|
|
583
|
-
if (body)
|
|
584
|
-
return { point: body.point, snapped: body.point, port: null, body };
|
|
585
|
-
}
|
|
586
|
-
const fx = snap(angled[0], step);
|
|
587
|
-
const fz = snap(angled[2], step);
|
|
588
|
-
const fy = ceilingModeRef.current ? resolveCeilingY(fx, fz) : angled[1];
|
|
491
|
+
const nextPort = nextDuct ? ductEndpointPort(nextDuct, 'end') : endConnection.port;
|
|
589
492
|
return {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
493
|
+
nextStart,
|
|
494
|
+
nextConnection: {
|
|
495
|
+
port: nextPort,
|
|
496
|
+
body: nextPort ? null : endConnection.body,
|
|
497
|
+
},
|
|
594
498
|
};
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
const last = draftRef.current.at(-1);
|
|
604
|
-
if (!anchor || !last)
|
|
605
|
-
return null;
|
|
606
|
-
const step = isGridSnapActive() ? useEditor.getState().gridSnapStep : 0;
|
|
607
|
-
// Screen +Y points down, so subtract to map "drag up = raise Y".
|
|
608
|
-
const dy = (anchor.clientY - clientY) / ALT_PIXELS_PER_METER;
|
|
609
|
-
const snappedDy = snap(dy, step);
|
|
610
|
-
const y = Math.min(ALT_Y_MAX_M, Math.max(ALT_Y_MIN_M, anchor.baseY + snappedDy));
|
|
611
|
-
return [last[0], y, last[2]];
|
|
612
|
-
};
|
|
613
|
-
// Resolve the cursor point (port / body / grid / angle snap) and then
|
|
614
|
-
// layer Figma-style alignment on top so a run lines up with other runs,
|
|
615
|
-
// fittings, and items as it's drawn. A free point (first vertex, or no
|
|
616
|
-
// angle lock) snaps; an angle-locked continuation shows the guide passively
|
|
617
|
-
// without leaving its 45° ray. Alignment follows the `lines` mode; a
|
|
618
|
-
// port / body snap or Alt-vertical bypasses it.
|
|
619
|
-
const resolveAlignedPoint = (event) => {
|
|
620
|
-
const r = resolveSnappedPoint(event);
|
|
621
|
-
const hasStart = draftRef.current.length > 0;
|
|
622
|
-
const alt = event.nativeEvent?.altKey === true;
|
|
623
|
-
const point = alignDrawPoint(r.point, {
|
|
624
|
-
applySnap: isMagneticSnapActive() && (!hasStart || !isAngleSnapActive()),
|
|
625
|
-
bypass: alt || r.snapped !== null,
|
|
626
|
-
});
|
|
627
|
-
return { ...r, point };
|
|
628
|
-
};
|
|
629
|
-
// The ceiling the cursor is under (ceiling mode only) — drives the
|
|
630
|
-
// translucent surface overlay so the in-flight point reads as hung
|
|
631
|
-
// against a real ceiling. Cleared when off-ceiling or out of mode.
|
|
632
|
-
const updateHoverCeiling = (x, z) => {
|
|
633
|
-
if (!ceilingModeRef.current) {
|
|
634
|
-
setHoverCeiling(null);
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
setHoverCeiling(getCeilingAt(activeLevelId, useScene.getState().nodes, x, z));
|
|
638
|
-
};
|
|
639
|
-
const onMove = (event) => {
|
|
640
|
-
const clientY = event.nativeEvent?.clientY;
|
|
641
|
-
if (typeof clientY === 'number')
|
|
642
|
-
lastClientYRef.current = clientY;
|
|
643
|
-
// Alt vertical mode wins over the XZ logic.
|
|
644
|
-
if (altAnchorRef.current && typeof clientY === 'number') {
|
|
645
|
-
const point = resolveAltVerticalPoint(clientY);
|
|
646
|
-
if (point) {
|
|
647
|
-
clearDrawAlignment();
|
|
648
|
-
setCursorPos(point);
|
|
649
|
-
setSnapTarget(null);
|
|
650
|
-
setEndSnap({ port: null, body: null });
|
|
651
|
-
updateHoverCeiling(point[0], point[2]);
|
|
652
|
-
return;
|
|
499
|
+
},
|
|
500
|
+
onShortcut: (event) => {
|
|
501
|
+
if (event.key === '[' || event.key === ']') {
|
|
502
|
+
event.preventDefault();
|
|
503
|
+
const next = stepNominalRunSize(DUCT_DIAMETERS_IN, profileRef.current.diameter, event.key === ']' ? 1 : -1);
|
|
504
|
+
if (next !== profileRef.current.diameter) {
|
|
505
|
+
setProfile((current) => ({ ...current, diameter: next }));
|
|
506
|
+
triggerSFX('sfx:grid-snap');
|
|
653
507
|
}
|
|
654
508
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const onClick = (event) => {
|
|
662
|
-
const start = draftRef.current.at(-1);
|
|
663
|
-
// Vertical mode with a start anchored: the click commits the riser
|
|
664
|
-
// segment right there. Never falls through to the XZ logic — a
|
|
665
|
-
// no-op Alt click (height unchanged) must not place anything.
|
|
666
|
-
if (altAnchorRef.current && start) {
|
|
667
|
-
const clientY = event.nativeEvent?.clientY ?? lastClientYRef.current;
|
|
668
|
-
if (typeof clientY === 'number') {
|
|
669
|
-
const point = resolveAltVerticalPoint(clientY);
|
|
670
|
-
if (point && Math.abs(point[1] - start[1]) >= 1e-4) {
|
|
671
|
-
commitSegment(start, point);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
const { point, port, body } = resolveAlignedPoint(event);
|
|
677
|
-
if (!start) {
|
|
678
|
-
// First click: anchor the segment start, remembering the port or
|
|
679
|
-
// run body it snapped to so the commit can mint an elbow / tee.
|
|
680
|
-
// Joining a port INHERITS the source's cross-section — continuing
|
|
681
|
-
// a rect trunk keeps drawing rect at its W×H, a round collar its
|
|
682
|
-
// diameter. Body taps (tee branches) keep the tool's own profile.
|
|
683
|
-
triggerSFX('sfx:grid-snap');
|
|
684
|
-
startPortRef.current = port;
|
|
685
|
-
startBodyRef.current = port ? null : body;
|
|
686
|
-
if (port) {
|
|
687
|
-
const inherited = inheritProfile(port);
|
|
688
|
-
if (inherited)
|
|
689
|
-
setProfile(inherited);
|
|
690
|
-
}
|
|
691
|
-
setDraftPoints([point]);
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
694
|
-
// Second click: commit the segment and re-arm. A body hit on the end
|
|
695
|
-
// (no end port) taps a tee into that run's side.
|
|
696
|
-
commitSegment(start, point, port, port ? null : body);
|
|
697
|
-
};
|
|
698
|
-
const enterAltMode = () => {
|
|
699
|
-
const last = draftRef.current.at(-1);
|
|
700
|
-
if (!last || lastClientYRef.current === null)
|
|
701
|
-
return;
|
|
702
|
-
if (altAnchorRef.current)
|
|
703
|
-
return;
|
|
704
|
-
altAnchorRef.current = { clientY: lastClientYRef.current, baseY: last[1] };
|
|
705
|
-
setAltActive(true);
|
|
706
|
-
};
|
|
707
|
-
const exitAltMode = () => {
|
|
708
|
-
if (!altAnchorRef.current)
|
|
709
|
-
return;
|
|
710
|
-
altAnchorRef.current = null;
|
|
711
|
-
setAltActive(false);
|
|
712
|
-
};
|
|
713
|
-
const stepDiameter = (step) => {
|
|
714
|
-
const sizes = DUCT_DIAMETERS_IN;
|
|
715
|
-
const current = profileRef.current.diameter;
|
|
716
|
-
// Nearest catalogue index, then step — handles seeded off-catalogue
|
|
717
|
-
// values (e.g. a preset's 7.5") gracefully.
|
|
718
|
-
let nearest = 0;
|
|
719
|
-
for (let i = 1; i < sizes.length; i++) {
|
|
720
|
-
if (Math.abs(sizes[i] - current) < Math.abs(sizes[nearest] - current))
|
|
721
|
-
nearest = i;
|
|
722
|
-
}
|
|
723
|
-
const next = sizes[Math.min(sizes.length - 1, Math.max(0, nearest + step))];
|
|
724
|
-
if (next === current)
|
|
725
|
-
return;
|
|
726
|
-
setProfile((p) => ({ ...p, diameter: next }));
|
|
727
|
-
triggerSFX('sfx:grid-snap');
|
|
728
|
-
};
|
|
729
|
-
const onKeyDown = (e) => {
|
|
730
|
-
const tag = e.target?.tagName;
|
|
731
|
-
if (tag === 'INPUT' || tag === 'TEXTAREA')
|
|
732
|
-
return;
|
|
733
|
-
if (e.key === 'Alt') {
|
|
734
|
-
e.preventDefault();
|
|
735
|
-
enterAltMode();
|
|
736
|
-
}
|
|
737
|
-
else if (e.key === '[') {
|
|
738
|
-
e.preventDefault();
|
|
739
|
-
stepDiameter(-1);
|
|
740
|
-
}
|
|
741
|
-
else if (e.key === ']') {
|
|
742
|
-
e.preventDefault();
|
|
743
|
-
stepDiameter(1);
|
|
744
|
-
}
|
|
745
|
-
else if (e.key === 'q' || e.key === 'Q') {
|
|
746
|
-
e.preventDefault();
|
|
747
|
-
setProfile((p) => ({ ...p, shape: p.shape === 'round' ? 'rect' : 'round' }));
|
|
509
|
+
else if (event.key === 'q' || event.key === 'Q') {
|
|
510
|
+
event.preventDefault();
|
|
511
|
+
setProfile((current) => ({
|
|
512
|
+
...current,
|
|
513
|
+
shape: current.shape === 'round' ? 'rect' : current.shape === 'rect' ? 'oval' : 'round',
|
|
514
|
+
}));
|
|
748
515
|
triggerSFX('sfx:grid-snap');
|
|
749
516
|
}
|
|
750
|
-
else if (
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
// Only flip while unanchored — already-placed points keep their Y,
|
|
754
|
-
// so a mid-run toggle would split a run across two height regimes.
|
|
755
|
-
if (draftRef.current.length > 0)
|
|
756
|
-
return;
|
|
757
|
-
e.preventDefault();
|
|
758
|
-
setCeilingMode((m) => !m);
|
|
759
|
-
setHoverCeiling(null);
|
|
517
|
+
else if (event.key === 'h' || event.key === 'H') {
|
|
518
|
+
event.preventDefault();
|
|
519
|
+
useRunHangerMode.getState().toggle('duct-segment');
|
|
760
520
|
triggerSFX('sfx:grid-snap');
|
|
761
521
|
}
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
emitter.off('grid:click', onClick);
|
|
790
|
-
emitter.off('tool:cancel', onCancel);
|
|
791
|
-
window.removeEventListener('keydown', onKeyDown);
|
|
792
|
-
window.removeEventListener('keyup', onKeyUp);
|
|
793
|
-
altAnchorRef.current = null;
|
|
794
|
-
clearDrawAlignment();
|
|
795
|
-
};
|
|
796
|
-
}, [activeLevelId]);
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
const previewPlan = useMemo(() => {
|
|
525
|
+
if (!(activeLevelId && run.start && run.cursor))
|
|
526
|
+
return null;
|
|
527
|
+
return planDuctDraw(run.start, run.cursor, pendingPromotionRef.current ? null : run.startConnection.port, run.startConnection.body, run.endConnection.port, run.endConnection.body, profile, sceneApi.nodes(), run.surfaceTarget, autoHangers, hangerDefaults, hangerStyle);
|
|
528
|
+
}, [
|
|
529
|
+
activeLevelId,
|
|
530
|
+
autoHangers,
|
|
531
|
+
hangerDefaults,
|
|
532
|
+
hangerStyle,
|
|
533
|
+
profile,
|
|
534
|
+
run.start,
|
|
535
|
+
run.cursor,
|
|
536
|
+
run.startConnection,
|
|
537
|
+
run.endConnection,
|
|
538
|
+
run.surfaceTarget,
|
|
539
|
+
sceneApi,
|
|
540
|
+
]);
|
|
541
|
+
const ghostFittings = useMemo(() => previewPlan?.fittings ?? [], [previewPlan]);
|
|
542
|
+
useEffect(() => {
|
|
543
|
+
usePathDraftPreview
|
|
544
|
+
.getState()
|
|
545
|
+
.setDraft('duct-segment', run.start ? [run.start] : [], run.cursor, { ...profile, autoHangers, hangerStyle }, ghostFittings);
|
|
546
|
+
}, [autoHangers, hangerStyle, ghostFittings, profile, run.cursor, run.start]);
|
|
547
|
+
useEffect(() => () => usePathDraftPreview.getState().clear('duct-segment'), []);
|
|
548
|
+
useEffect(() => () => useEditor.getState().setToolDefaults('duct-segment', null), []);
|
|
797
549
|
if (!activeLevelId)
|
|
798
550
|
return null;
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
// a segment is in flight. The actively-driven axis is emphasised — Y in
|
|
810
|
-
// Alt-vertical mode, otherwise whichever horizontal axis dominates. A
|
|
811
|
-
// trailing Ø readout shows the diameter the next click commits ([ / ]).
|
|
812
|
-
const pillParts = cursorPos
|
|
813
|
-
? [
|
|
814
|
-
...['x', 'y', 'z'].map((axis, i) => ({
|
|
815
|
-
key: axis,
|
|
816
|
-
prefix: axis.toUpperCase(),
|
|
817
|
-
value: last ? cursorPos[i] - last[i] : cursorPos[i],
|
|
818
|
-
signed: !!last,
|
|
819
|
-
})),
|
|
820
|
-
...(profile.shape === 'round'
|
|
821
|
-
? [{ key: 'diameter', prefix: 'Ø', value: profile.diameter * 0.0254, signed: false }]
|
|
822
|
-
: [
|
|
823
|
-
{ key: 'trunk-w', prefix: 'W', value: profile.width * 0.0254, signed: false },
|
|
824
|
-
{ key: 'trunk-h', prefix: 'H', value: profile.height * 0.0254, signed: false },
|
|
825
|
-
]),
|
|
826
|
-
]
|
|
827
|
-
: null;
|
|
828
|
-
const pillPrimary = last && cursorPos
|
|
829
|
-
? altActive
|
|
830
|
-
? 'y'
|
|
831
|
-
: Math.abs(cursorPos[0] - last[0]) >= Math.abs(cursorPos[2] - last[2])
|
|
832
|
-
? 'x'
|
|
833
|
-
: 'z'
|
|
834
|
-
: undefined;
|
|
835
|
-
// When the in-flight point hangs above the floor (ceiling mode, or an
|
|
836
|
-
// Alt riser), the cursor marker itself rides AT the point (where the
|
|
837
|
-
// mouse is aiming and the next click commits), and a plumb line drops
|
|
838
|
-
// straight down to a faint ground ring on the floor below — so the plan
|
|
839
|
-
// position stays legible from any angle. A floor-level point keeps the
|
|
840
|
-
// standard fixed-height cursor look.
|
|
841
|
-
const cursorElevation = cursorPos ? cursorPos[1] : 0;
|
|
842
|
-
const isElevated = cursorElevation > 0.001;
|
|
843
|
-
const cursorGround = cursorPos
|
|
844
|
-
? [cursorPos[0], 0, cursorPos[2]]
|
|
845
|
-
: null;
|
|
846
|
-
return (_jsxs(LevelOffsetGroup, { children: [ceilingMode && hoverCeiling && _jsx(CeilingHighlight, { ceiling: hoverCeiling }), cursorPos && cursorGround && (_jsxs(_Fragment, { children: [isElevated ? (_jsx(CursorSphere, { color: snapTarget ? SNAP_CURSOR_COLOR : undefined, dotAtTip: true, height: cursorElevation, position: cursorGround, ref: cursorRef })) : (_jsx(CursorSphere, { color: snapTarget ? SNAP_CURSOR_COLOR : undefined, position: cursorPos, ref: cursorRef })), pillParts && (_jsx("group", { position: cursorPos, children: _jsx(Html, { center: true, position: [0, 1.45, 0], style: { pointerEvents: 'none', userSelect: 'none' }, zIndexRange: [100, 0], children: _jsxs("div", { className: "flex flex-col items-center gap-2", children: [ceilingMode && !last && (_jsx("div", { className: "whitespace-nowrap rounded-full border border-border/60 bg-background/90 px-3 py-0.5 text-[10px] text-muted-foreground shadow-sm backdrop-blur", children: "Ceiling \u00B7 C to toggle" })), _jsx(DimensionPill, { parts: pillParts, primary: pillPrimary, unit: unit })] }) }) }))] })), draftPoints.map((p, i) => (_jsxs("mesh", { layers: EDITOR_LAYER, position: p, children: [_jsx("sphereGeometry", { args: [0.07, 16, 12] }), _jsx("meshBasicMaterial", { color: "#818cf8", depthTest: false })] }, `pt-${i}`))), previewSegments.map((seg, i) => (_jsx(PreviewSegment, { a: seg.a, b: seg.b, endPort: endSnap.port, profile: profile, startPort: startPortRef.current }, `seg-${i}`))), ghostFittings.map((fitting) => (_jsx(FittingGhost, { fitting: fitting }, fitting.id)))] }));
|
|
551
|
+
const extraParts = profile.shape === 'round'
|
|
552
|
+
? [{ key: 'diameter', prefix: 'Ø', value: profile.diameter * 0.0254 }]
|
|
553
|
+
: [
|
|
554
|
+
{ key: 'trunk-w', prefix: 'W', value: profile.width * 0.0254 },
|
|
555
|
+
{ key: 'trunk-h', prefix: 'H', value: profile.height * 0.0254 },
|
|
556
|
+
];
|
|
557
|
+
return (_jsxs(LevelOffsetGroup, { children: [_jsx(ConnectionFeedback, { point: run.cursor, target: run.endConnection.port, levelId: activeLevelId, profile: {
|
|
558
|
+
...profile,
|
|
559
|
+
system: String(hangerDefaults?.system ?? ductSegmentDefinition.defaults().system),
|
|
560
|
+
} }), _jsx(DistributionRunCursor, { altActive: run.altActive, cursor: run.cursor, cursorRef: cursorRef, directionMode: run.directionMode, extraParts: extraParts, lengthInput: run.lengthInput, onLengthInputChange: run.onLengthInputChange, onDirectionSelect: run.onDirectionSelect, validationMessage: previewPlan?.validationMessage ?? run.validationMessage, snapTarget: run.snapTarget, snapScreen: run.snapScreen, start: run.start, startDirection: run.startConnection.port?.direction ?? null, unit: unit }), run.start && (_jsxs("mesh", { layers: EDITOR_LAYER, position: run.start, children: [_jsx("sphereGeometry", { args: [0.07, 16, 12] }), _jsx("meshBasicMaterial", { color: "#818cf8", depthTest: false })] })), previewPlan?.ducts.map((duct, index) => (_jsx(DuctSegmentGhost, { duct: duct }, index))), previewPlan?.ducts.map((duct, index) => (_jsx(RunHangerPreview, { run: duct, levelId: activeLevelId }, `hanger-${index}`))), ghostFittings.map((fitting) => (_jsx(FittingGhost, { fitting: fitting }, fitting.id)))] }));
|
|
847
561
|
};
|
|
848
|
-
/**
|
|
849
|
-
* Build a horizontal `ShapeGeometry` for a ceiling polygon (with holes) in
|
|
850
|
-
* level-local XZ, laid flat in the XZ plane. Mirrors the ceiling renderer /
|
|
851
|
-
* move-tool convention (Z negated, then rotated onto the floor plane).
|
|
852
|
-
*/
|
|
853
|
-
function buildCeilingShape(polygon, holes) {
|
|
854
|
-
if (polygon.length < 3)
|
|
855
|
-
return null;
|
|
856
|
-
const shape = new Shape();
|
|
857
|
-
const first = polygon[0];
|
|
858
|
-
shape.moveTo(first[0], -first[1]);
|
|
859
|
-
for (let i = 1; i < polygon.length; i++) {
|
|
860
|
-
const pt = polygon[i];
|
|
861
|
-
shape.lineTo(pt[0], -pt[1]);
|
|
862
|
-
}
|
|
863
|
-
shape.closePath();
|
|
864
|
-
for (const holePolygon of holes) {
|
|
865
|
-
if (holePolygon.length < 3)
|
|
866
|
-
continue;
|
|
867
|
-
const hole = new Path();
|
|
868
|
-
const hf = holePolygon[0];
|
|
869
|
-
hole.moveTo(hf[0], -hf[1]);
|
|
870
|
-
for (let i = 1; i < holePolygon.length; i++) {
|
|
871
|
-
const pt = holePolygon[i];
|
|
872
|
-
hole.lineTo(pt[0], -pt[1]);
|
|
873
|
-
}
|
|
874
|
-
hole.closePath();
|
|
875
|
-
shape.holes.push(hole);
|
|
876
|
-
}
|
|
877
|
-
const geometry = new ShapeGeometry(shape);
|
|
878
|
-
geometry.rotateX(-Math.PI / 2);
|
|
879
|
-
return geometry;
|
|
880
|
-
}
|
|
881
|
-
/**
|
|
882
|
-
* Translucent overlay of the ceiling the cursor is under, drawn at the
|
|
883
|
-
* ceiling's own height. Gives the in-flight duct point a real surface to
|
|
884
|
-
* read against, so "hung against the ceiling" is visible from any angle
|
|
885
|
-
* instead of being a dot floating in space.
|
|
886
|
-
*/
|
|
887
|
-
function CeilingHighlight({ ceiling }) {
|
|
888
|
-
const geometry = useMemo(() => buildCeilingShape(ceiling.polygon, ceiling.holes), [ceiling.polygon, ceiling.holes]);
|
|
889
|
-
const outline = useMemo(() => {
|
|
890
|
-
if (ceiling.polygon.length < 2)
|
|
891
|
-
return null;
|
|
892
|
-
const pts = ceiling.polygon.map(([x, z]) => new Vector3(x, 0, z));
|
|
893
|
-
const f = ceiling.polygon[0];
|
|
894
|
-
pts.push(new Vector3(f[0], 0, f[1]));
|
|
895
|
-
return pts;
|
|
896
|
-
}, [ceiling.polygon]);
|
|
897
|
-
if (!geometry)
|
|
898
|
-
return null;
|
|
899
|
-
const y = resolveCeilingHeight(ceiling, useScene.getState().nodes);
|
|
900
|
-
return (_jsxs("group", { position: [0, y, 0], children: [_jsx("mesh", { geometry: geometry, layers: EDITOR_LAYER, renderOrder: 1, children: _jsx("meshBasicMaterial", { color: "#818cf8", depthWrite: false, opacity: 0.15, side: DoubleSide, transparent: true }) }), outline && (_jsxs("line", { children: [_jsx("bufferGeometry", { ref: (g) => {
|
|
901
|
-
if (g)
|
|
902
|
-
g.setFromPoints(outline);
|
|
903
|
-
} }), _jsx("lineBasicMaterial", { color: "#818cf8", opacity: 0.6, transparent: true })] }))] }));
|
|
904
|
-
}
|
|
905
|
-
function PreviewSegment({ a, b, profile, startPort, endPort, }) {
|
|
906
|
-
const start = new Vector3(...a);
|
|
907
|
-
const end = new Vector3(...b);
|
|
908
|
-
const dir = new Vector3().subVectors(end, start);
|
|
909
|
-
const length = dir.length();
|
|
910
|
-
if (length < 1e-4)
|
|
911
|
-
return null;
|
|
912
|
-
dir.normalize();
|
|
913
|
-
const mid = new Vector3().addVectors(start, end).multiplyScalar(0.5);
|
|
914
|
-
// Rect AND oval ghost as a box — close enough for a translucent guide.
|
|
915
|
-
if (profile.shape !== 'round') {
|
|
916
|
-
const w = profile.width * 0.0254;
|
|
917
|
-
const h = profile.height * 0.0254;
|
|
918
|
-
return (_jsxs("mesh", { layers: EDITOR_LAYER, position: mid.toArray(), ref: (m) => {
|
|
919
|
-
if (!m)
|
|
920
|
-
return;
|
|
921
|
-
// Same basis AND roll as the commit will use, so the ghost
|
|
922
|
-
// shows the orientation that actually lands.
|
|
923
|
-
const roll = continuityRollForRun(startPort, endPort, dir);
|
|
924
|
-
const { width: x, height: z } = rectSectionAxes(dir, roll);
|
|
925
|
-
m.quaternion.setFromRotationMatrix(new Matrix4().makeBasis(x, dir, z));
|
|
926
|
-
}, children: [_jsx("boxGeometry", { args: [w, length, h] }), _jsx("meshBasicMaterial", { color: "#818cf8", depthTest: false, opacity: PREVIEW_OPACITY, transparent: true })] }));
|
|
927
|
-
}
|
|
928
|
-
const radius = (profile.diameter * 0.0254) / 2;
|
|
929
|
-
return (_jsxs("mesh", { layers: EDITOR_LAYER, position: mid.toArray(), ref: (m) => {
|
|
930
|
-
if (!m)
|
|
931
|
-
return;
|
|
932
|
-
m.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir);
|
|
933
|
-
}, children: [_jsx("cylinderGeometry", { args: [radius, radius, length, 24, 1, false] }), _jsx("meshBasicMaterial", { color: "#818cf8", depthTest: false, opacity: PREVIEW_OPACITY, transparent: true })] }));
|
|
934
|
-
}
|
|
935
562
|
export default DuctSegmentTool;
|