@pascal-app/nodes 1.0.0-beta.5 → 1.0.1
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 +4 -4
- 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/appliance-layout.d.ts +26 -0
- package/dist/cabinet/appliance-layout.d.ts.map +1 -0
- package/dist/cabinet/appliance-layout.js +52 -0
- 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/countertop-layout.d.ts +173 -0
- package/dist/cabinet/countertop-layout.d.ts.map +1 -0
- package/dist/cabinet/countertop-layout.js +80 -0
- package/dist/cabinet/definition.d.ts.map +1 -1
- package/dist/cabinet/definition.js +414 -61
- package/dist/cabinet/floorplan-move.d.ts.map +1 -1
- package/dist/cabinet/floorplan-move.js +91 -22
- package/dist/cabinet/geometry/cooktop.d.ts.map +1 -1
- package/dist/cabinet/geometry/cooktop.js +2 -2
- 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 +28 -71
- package/dist/cabinet/geometry/sink.d.ts +2 -12
- package/dist/cabinet/geometry/sink.d.ts.map +1 -1
- package/dist/cabinet/geometry/sink.js +8 -39
- package/dist/cabinet/geometry.d.ts.map +1 -1
- package/dist/cabinet/geometry.js +63 -1
- package/dist/cabinet/hosted-resize.d.ts +8 -0
- package/dist/cabinet/hosted-resize.d.ts.map +1 -0
- package/dist/cabinet/hosted-resize.js +158 -0
- 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 +98 -5
- package/dist/cabinet/run-layout.d.ts.map +1 -1
- package/dist/cabinet/run-layout.js +430 -37
- package/dist/cabinet/run-ops.d.ts +204 -12
- package/dist/cabinet/run-ops.d.ts.map +1 -1
- package/dist/cabinet/run-ops.js +523 -36
- 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 +294 -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/surfaces.d.ts +4 -0
- package/dist/cabinet/surfaces.d.ts.map +1 -0
- package/dist/cabinet/surfaces.js +101 -0
- package/dist/cabinet/tool.d.ts.map +1 -1
- package/dist/cabinet/tool.js +615 -91
- package/dist/cabinet/tree-structure.d.ts.map +1 -1
- package/dist/cabinet/tree-structure.js +10 -9
- 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 +8 -2
- package/dist/ceiling/floorplan-affordances.d.ts +12 -12
- package/dist/ceiling/materials.d.ts.map +1 -1
- package/dist/ceiling/materials.js +0 -1
- package/dist/ceiling/panel.js +1 -1
- package/dist/ceiling/parametrics.js +1 -1
- package/dist/chimney/definition.js +1 -1
- package/dist/chimney/panel.js +9 -9
- 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 +12 -12
- 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-position-section.js +2 -2
- 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/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 +49 -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 +38 -0
- package/dist/duct-segment/tool.d.ts.map +1 -1
- package/dist/duct-segment/tool.js +306 -677
- 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/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/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/inspector-editors.js +1 -1
- package/dist/fence/parametrics.js +3 -3
- package/dist/fence/tool.d.ts.map +1 -1
- package/dist/fence/tool.js +47 -20
- 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/imported-mesh/definition.d.ts +5 -0
- package/dist/imported-mesh/definition.d.ts.map +1 -0
- package/dist/imported-mesh/definition.js +38 -0
- package/dist/imported-mesh/floorplan.d.ts +4 -0
- package/dist/imported-mesh/floorplan.d.ts.map +1 -0
- package/dist/imported-mesh/floorplan.js +34 -0
- package/dist/imported-mesh/geometry.d.ts +5 -0
- package/dist/imported-mesh/geometry.d.ts.map +1 -0
- package/dist/imported-mesh/geometry.js +34 -0
- package/dist/imported-mesh/index.d.ts +4 -0
- package/dist/imported-mesh/index.d.ts.map +1 -0
- package/dist/imported-mesh/index.js +3 -0
- package/dist/imported-mesh/schema.d.ts +2 -0
- package/dist/imported-mesh/schema.d.ts.map +1 -0
- package/dist/imported-mesh/schema.js +1 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -2
- package/dist/item/definition.d.ts.map +1 -1
- package/dist/item/definition.js +10 -2
- package/dist/item/floorplan-move.d.ts +9 -1
- package/dist/item/floorplan-move.d.ts.map +1 -1
- package/dist/item/floorplan-move.js +74 -2
- package/dist/item/floorplan.d.ts.map +1 -1
- package/dist/item/floorplan.js +33 -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 +15 -3
- package/dist/item/panel.js +1 -1
- 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/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 +49 -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/procedural-item/definition.d.ts +4 -0
- package/dist/procedural-item/definition.d.ts.map +1 -0
- package/dist/procedural-item/definition.js +254 -0
- package/dist/procedural-item/geometry.d.ts +31 -0
- package/dist/procedural-item/geometry.d.ts.map +1 -0
- package/dist/procedural-item/geometry.js +88 -0
- package/dist/procedural-item/index.d.ts +3 -0
- package/dist/procedural-item/index.d.ts.map +1 -0
- package/dist/procedural-item/index.js +2 -0
- package/dist/procedural-item/move-session.d.ts +15 -0
- package/dist/procedural-item/move-session.d.ts.map +1 -0
- package/dist/procedural-item/move-session.js +197 -0
- package/dist/procedural-item/move-tool.d.ts +5 -0
- package/dist/procedural-item/move-tool.d.ts.map +1 -0
- package/dist/procedural-item/move-tool.js +193 -0
- package/dist/procedural-item/renderer.d.ts +5 -0
- package/dist/procedural-item/renderer.d.ts.map +1 -0
- package/dist/procedural-item/renderer.js +66 -0
- package/dist/ridge-vent/definition.js +1 -1
- package/dist/ridge-vent/panel.d.ts.map +1 -1
- package/dist/ridge-vent/panel.js +4 -4
- package/dist/ridge-vent/parametrics.js +1 -1
- 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 +26 -25
- 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 +12 -0
- package/dist/shared/automatic-run-end-cap.d.ts.map +1 -0
- package/dist/shared/automatic-run-end-cap.js +169 -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 +117 -0
- package/dist/shared/distribution-run-tool.d.ts.map +1 -0
- package/dist/shared/distribution-run-tool.js +986 -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-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/panel-fields.d.ts +15 -0
- package/dist/shared/panel-fields.d.ts.map +1 -0
- package/dist/shared/panel-fields.js +31 -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/resting-surface-plan.d.ts +3 -0
- package/dist/shared/resting-surface-plan.d.ts.map +1 -0
- package/dist/shared/resting-surface-plan.js +28 -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/definition.d.ts.map +1 -1
- package/dist/shelf/definition.js +2 -1
- package/dist/shelf/floorplan-affordances.d.ts.map +1 -1
- package/dist/shelf/floorplan-affordances.js +5 -3
- package/dist/shelf/floorplan-move.d.ts.map +1 -1
- package/dist/shelf/floorplan-move.js +3 -2
- package/dist/shelf/geometry.d.ts.map +1 -1
- package/dist/shelf/geometry.js +2 -10
- 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 +6 -6
- 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.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 +2 -2
- package/dist/solar-panel/parametrics.js +2 -2
- package/dist/spawn/panel.js +1 -1
- package/dist/stair/panel.d.ts.map +1 -1
- package/dist/stair/panel.js +23 -16
- 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 +45 -6
- 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/unit/definition.d.ts +10 -0
- package/dist/unit/definition.d.ts.map +1 -0
- package/dist/unit/definition.js +38 -0
- package/dist/unit/index.d.ts +2 -0
- package/dist/unit/index.d.ts.map +1 -0
- package/dist/unit/index.js +1 -0
- package/dist/unit/parametrics.d.ts +3 -0
- package/dist/unit/parametrics.d.ts.map +1 -0
- package/dist/unit/parametrics.js +4 -0
- package/dist/unit/schema.d.ts +2 -0
- package/dist/unit/schema.d.ts.map +1 -0
- package/dist/unit/schema.js +1 -0
- package/dist/unit/unit-panel.d.ts +5 -0
- package/dist/unit/unit-panel.d.ts.map +1 -0
- package/dist/unit/unit-panel.js +61 -0
- 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.map +1 -1
- package/dist/wall/curve-tool.js +8 -11
- 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 +10 -17
- 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 +2 -0
- package/dist/wall/system.d.ts.map +1 -1
- package/dist/wall/system.js +66 -25
- package/dist/wall/tool.d.ts.map +1 -1
- package/dist/wall/tool.js +5 -0
- 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 +38 -6
- package/dist/wall/wall-batch-system.d.ts.map +1 -1
- package/dist/wall/wall-batch-system.js +122 -25
- package/dist/wall/wall-batch.js +2 -2
- 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 +8 -6
- 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/definition.d.ts.map +1 -1
- package/dist/zone/definition.js +7 -1
- package/dist/zone/floorplan-affordances.d.ts +12 -12
- package/dist/zone/floorplan.d.ts +1 -3
- package/dist/zone/floorplan.d.ts.map +1 -1
- package/dist/zone/floorplan.js +43 -28
- package/dist/zone/quantities-panel.d.ts.map +1 -1
- package/dist/zone/quantities-panel.js +33 -29
- package/dist/zone/renderer.d.ts.map +1 -1
- package/dist/zone/renderer.js +20 -6
- package/dist/zone/unit-membership.d.ts +12 -0
- package/dist/zone/unit-membership.d.ts.map +1 -0
- package/dist/zone/unit-membership.js +22 -0
- package/package.json +16 -8
- 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,16 @@ 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;
|
|
59
|
+
const DUCT_WALL_STANDOFF_M = 0.01;
|
|
63
60
|
/** 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
61
|
/**
|
|
86
62
|
* Cross-section roll for a new rect run leaving `port` along `newDir`,
|
|
87
63
|
* so its profile stays continuous with whatever it joined: a turn
|
|
@@ -92,11 +68,15 @@ function dist2(a, b) {
|
|
|
92
68
|
* there). Null when the port doesn't carry a rect orientation. Shared
|
|
93
69
|
* by the ghost preview and the commit so what you see is what lands.
|
|
94
70
|
*/
|
|
95
|
-
function continuityRollFrom(port, newDir) {
|
|
71
|
+
function continuityRollFrom(port, newDir, nodes) {
|
|
96
72
|
if (!port)
|
|
97
73
|
return null;
|
|
98
|
-
const nodes = useScene.getState().nodes;
|
|
99
74
|
const owner = nodes[port.nodeId];
|
|
75
|
+
if (owner?.type === 'duct-fitting' && ['reducer', 'transition'].includes(owner.fittingType)) {
|
|
76
|
+
const width = new Vector3(0, 0, 1).applyEuler(new Euler(...owner.rotation));
|
|
77
|
+
const basis = rectSectionAxes(newDir);
|
|
78
|
+
return Math.atan2(width.dot(basis.height), width.dot(basis.width));
|
|
79
|
+
}
|
|
100
80
|
let srcDir = null;
|
|
101
81
|
let srcRoll = 0;
|
|
102
82
|
if ((owner?.type === 'hvac-equipment' || owner?.type === 'duct-terminal') &&
|
|
@@ -134,26 +114,33 @@ function continuityRollFrom(port, newDir) {
|
|
|
134
114
|
if (!srcDir)
|
|
135
115
|
return null;
|
|
136
116
|
const cross = new Vector3().crossVectors(srcDir, newDir);
|
|
137
|
-
if (cross.lengthSq() < 1e-8)
|
|
117
|
+
if (cross.lengthSq() < 1e-8) {
|
|
118
|
+
if (owner?.type === 'duct-segment') {
|
|
119
|
+
const i = port.id === 'start' ? 0 : owner.path.length - 2;
|
|
120
|
+
const direction = new Vector3(...owner.path[i + 1]).sub(new Vector3(...owner.path[i]));
|
|
121
|
+
const width = rectSectionAxes(direction, owner.roll).width;
|
|
122
|
+
const basis = rectSectionAxes(newDir);
|
|
123
|
+
return Math.atan2(width.dot(basis.height), width.dot(basis.width));
|
|
124
|
+
}
|
|
138
125
|
return srcRoll;
|
|
126
|
+
}
|
|
139
127
|
return rollToContinueAcrossElbow(srcDir, srcRoll, srcDir, newDir);
|
|
140
128
|
}
|
|
141
|
-
function continuityRollForRun(startPort, endPort, dir) {
|
|
142
|
-
return continuityRollFrom(startPort, dir) ?? continuityRollFrom(endPort, dir) ?? 0;
|
|
129
|
+
function continuityRollForRun(startPort, endPort, dir, nodes) {
|
|
130
|
+
return continuityRollFrom(startPort, dir, nodes) ?? continuityRollFrom(endPort, dir, nodes) ?? 0;
|
|
143
131
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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);
|
|
132
|
+
function getConnectionPorts(levelId, nodes) {
|
|
133
|
+
return collectScenePorts({
|
|
134
|
+
systems: DUCT_PORT_SYSTEMS,
|
|
135
|
+
levelId: levelId ?? undefined,
|
|
136
|
+
}).filter((port) => !isRunEndCapPort(port, nodes));
|
|
154
137
|
}
|
|
155
|
-
function
|
|
156
|
-
return
|
|
138
|
+
export function ductSurfaceClearanceM(profile, wall = false) {
|
|
139
|
+
return (runSectionHalfSizeM(profile.shape === 'round'
|
|
140
|
+
? profile.diameter
|
|
141
|
+
: wall
|
|
142
|
+
? Math.max(profile.width, profile.height)
|
|
143
|
+
: profile.height) + (wall && profile.shape !== 'round' ? DUCT_WALL_STANDOFF_M : 0));
|
|
157
144
|
}
|
|
158
145
|
/**
|
|
159
146
|
* Profile to inherit when the segment start snaps onto `port` — joining
|
|
@@ -161,16 +148,16 @@ function portPoint(port) {
|
|
|
161
148
|
* run / fitting collar keeps its diameter. Equipment and terminal
|
|
162
149
|
* collars are round at the port's advertised size.
|
|
163
150
|
*/
|
|
164
|
-
function inheritProfile(port) {
|
|
165
|
-
const owner =
|
|
151
|
+
function inheritProfile(port, nodes) {
|
|
152
|
+
const owner = nodes[port.nodeId];
|
|
166
153
|
if (!owner)
|
|
167
154
|
return null;
|
|
168
155
|
if (owner.type === 'duct-segment' || owner.type === 'duct-fitting') {
|
|
169
156
|
return {
|
|
170
|
-
shape: owner.shape,
|
|
157
|
+
shape: port.shape ?? owner.shape,
|
|
171
158
|
diameter: Math.min(48, Math.max(2, owner.type === 'duct-segment' ? owner.diameter : port.diameter)),
|
|
172
|
-
width: owner.width,
|
|
173
|
-
height: owner.height,
|
|
159
|
+
width: port.width ?? owner.width,
|
|
160
|
+
height: port.height ?? owner.height,
|
|
174
161
|
};
|
|
175
162
|
}
|
|
176
163
|
if (owner.type === 'hvac-equipment' || owner.type === 'duct-terminal') {
|
|
@@ -195,52 +182,38 @@ function inheritProfile(port) {
|
|
|
195
182
|
}
|
|
196
183
|
return null;
|
|
197
184
|
}
|
|
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) => {
|
|
185
|
+
const elbowPlanFor = (port, awayDir, profile, nodes) => {
|
|
219
186
|
if (!port)
|
|
220
187
|
return null;
|
|
221
|
-
const owner =
|
|
188
|
+
const owner = nodes[port.nodeId];
|
|
222
189
|
if (owner?.type !== 'duct-segment')
|
|
223
190
|
return null;
|
|
224
|
-
const
|
|
191
|
+
const source = inheritProfile(port, nodes) ?? profile;
|
|
192
|
+
const plan = planElbowAtPort(port, awayDir, source);
|
|
225
193
|
if (!plan)
|
|
226
194
|
return null;
|
|
227
195
|
// Trim the run's snapped endpoint back to the elbow's inlet collar.
|
|
228
196
|
const path = owner.path.map((p) => [...p]);
|
|
229
197
|
const index = port.id === 'start' ? 0 : path.length - 1;
|
|
230
198
|
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
199
|
const original = path[index];
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
200
|
+
const direction = [
|
|
201
|
+
original[0] - neighbor[0],
|
|
202
|
+
original[1] - neighbor[1],
|
|
203
|
+
original[2] - neighbor[2],
|
|
204
|
+
];
|
|
205
|
+
const hasClearance = hasFittingClearance(neighbor, plan.trimmedPortPoint, direction, 0.08);
|
|
237
206
|
path[index] = plan.trimmedPortPoint;
|
|
238
|
-
return {
|
|
207
|
+
return {
|
|
208
|
+
...plan,
|
|
209
|
+
hasClearance,
|
|
210
|
+
trim: { id: port.nodeId, data: { path } },
|
|
211
|
+
};
|
|
239
212
|
};
|
|
240
|
-
const realignPlanFor = (port, awayDir) => {
|
|
213
|
+
const realignPlanFor = (port, awayDir, nodes) => {
|
|
241
214
|
if (!port)
|
|
242
215
|
return null;
|
|
243
|
-
const owner =
|
|
216
|
+
const owner = nodes[port.nodeId];
|
|
244
217
|
if (owner?.type !== 'duct-fitting')
|
|
245
218
|
return null;
|
|
246
219
|
return planElbowRealign(owner, port.id, awayDir);
|
|
@@ -251,10 +224,10 @@ const realignPlanFor = (port, awayDir) => {
|
|
|
251
224
|
* tap), decide every node the commit creates / updates — auto-inserted
|
|
252
225
|
* elbows / tees / crosses, the drawn run (split in two when it crosses a
|
|
253
226
|
* 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.
|
|
227
|
+
* graph snapshot but mutates nothing, so the live preview can call it each
|
|
228
|
+
* frame to ghost the fittings before the commit applies the identical plan.
|
|
256
229
|
*/
|
|
257
|
-
function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profile) {
|
|
230
|
+
export function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profile, nodes, surface, autoHangers = false, toolDefaults = {}, hangerStyle = 'single') {
|
|
258
231
|
const length = Math.hypot(end[0] - start[0], end[1] - start[1], end[2] - start[2]);
|
|
259
232
|
if (length < 1e-4)
|
|
260
233
|
return null;
|
|
@@ -263,56 +236,107 @@ function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profil
|
|
|
263
236
|
(end[1] - start[1]) / length,
|
|
264
237
|
(end[2] - start[2]) / length,
|
|
265
238
|
];
|
|
266
|
-
const startPlan = elbowPlanFor(startPort, dir, profile);
|
|
267
|
-
const endPlan = elbowPlanFor(endPort, [-dir[0], -dir[1], -dir[2]], profile);
|
|
268
|
-
const
|
|
269
|
-
|
|
239
|
+
const startPlan = elbowPlanFor(startPort, dir, profile, nodes);
|
|
240
|
+
const endPlan = elbowPlanFor(endPort, [-dir[0], -dir[1], -dir[2]], profile, nodes);
|
|
241
|
+
const invalidPlan = () => ({
|
|
242
|
+
validationMessage: FITTING_CLEARANCE_MESSAGE,
|
|
243
|
+
fittings: [],
|
|
244
|
+
ducts: [],
|
|
245
|
+
tails: [],
|
|
246
|
+
updates: [],
|
|
247
|
+
delete: [],
|
|
248
|
+
});
|
|
249
|
+
if (startPlan?.hasClearance === false || endPlan?.hasClearance === false)
|
|
250
|
+
return invalidPlan();
|
|
251
|
+
const startRealign = startPlan ? null : realignPlanFor(startPort, dir, nodes);
|
|
252
|
+
const endRealign = endPlan ? null : realignPlanFor(endPort, [-dir[0], -dir[1], -dir[2]], nodes);
|
|
270
253
|
const trunkBody = startPlan ? null : startBody;
|
|
271
|
-
const trunkOwner = trunkBody ?
|
|
254
|
+
const trunkOwner = trunkBody ? nodes[trunkBody.nodeId] : null;
|
|
272
255
|
const teePlan = trunkBody && trunkOwner?.type === 'duct-segment'
|
|
273
256
|
? planTeeAtRunBody(trunkOwner, trunkBody, dir, profile)
|
|
274
257
|
: null;
|
|
275
258
|
const endTrunkBody = endPlan || endRealign ? null : endBody;
|
|
276
|
-
const endTrunkOwner = endTrunkBody ?
|
|
259
|
+
const endTrunkOwner = endTrunkBody ? nodes[endTrunkBody.nodeId] : null;
|
|
277
260
|
const endTeePlan = endTrunkBody && endTrunkOwner?.type === 'duct-segment'
|
|
278
261
|
? planTeeAtRunBody(endTrunkOwner, endTrunkBody, [-dir[0], -dir[1], -dir[2]], profile)
|
|
279
262
|
: null;
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
263
|
+
const adapterFor = (port, corner, away) => {
|
|
264
|
+
if (!port)
|
|
265
|
+
return null;
|
|
266
|
+
const source = inheritProfile(port, nodes);
|
|
267
|
+
if (!source)
|
|
268
|
+
return null;
|
|
269
|
+
const axis = new Vector3(...away);
|
|
270
|
+
if (!corner && axis.dot(new Vector3(...port.direction).normalize()) < 0.9999)
|
|
271
|
+
return null;
|
|
272
|
+
const owner = nodes[port.nodeId];
|
|
273
|
+
const roll = continuityRollFrom(port, axis, nodes) ?? 0;
|
|
274
|
+
const width = rectSectionAxes(axis, roll).width;
|
|
275
|
+
if (owner?.type === 'duct-fitting' && !corner) {
|
|
276
|
+
width.set(0, 0, 1).applyEuler(new Euler(...owner.rotation));
|
|
277
|
+
}
|
|
278
|
+
return planDuctAdapter({ ...port, position: corner?.collarPoint ?? port.position, direction: away }, source, profile, width);
|
|
279
|
+
};
|
|
280
|
+
const startAdapter = adapterFor(startPort, startPlan, dir);
|
|
281
|
+
const endAdapter = adapterFor(endPort, endPlan, [-dir[0], -dir[1], -dir[2]]);
|
|
282
|
+
for (const [port, adapter] of [
|
|
283
|
+
[startPort, startAdapter],
|
|
284
|
+
[endPort, endAdapter],
|
|
285
|
+
]) {
|
|
286
|
+
const source = port ? inheritProfile(port, nodes) : null;
|
|
287
|
+
if (source && !ductProfilesMatch(source, profile) && !adapter) {
|
|
288
|
+
return {
|
|
289
|
+
...invalidPlan(),
|
|
290
|
+
validationMessage: 'Align the connection or draw a 15–90° bend to fit the profile change.',
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const ductStart = startAdapter?.collarPoint ??
|
|
295
|
+
startPlan?.collarPoint ??
|
|
296
|
+
teePlan?.branchCollar ??
|
|
297
|
+
startRealign?.collarPoint ??
|
|
298
|
+
start;
|
|
299
|
+
let ductEnd = endAdapter?.collarPoint ??
|
|
300
|
+
endPlan?.collarPoint ??
|
|
301
|
+
endTeePlan?.branchCollar ??
|
|
302
|
+
endRealign?.collarPoint ??
|
|
303
|
+
end;
|
|
304
|
+
const plans = [startPlan, endPlan].filter((p) => p !== null);
|
|
305
|
+
const tee = teePlan;
|
|
285
306
|
let endTee = endTeePlan && endTrunkBody?.nodeId === trunkBody?.nodeId ? null : endTeePlan;
|
|
286
307
|
if (!endTee && endTeePlan)
|
|
287
308
|
ductEnd = endRealign?.collarPoint ?? end;
|
|
288
|
-
|
|
289
|
-
const crossHit =
|
|
290
|
-
|
|
309
|
+
const realigns = [startRealign, endRealign].filter((p) => p !== null);
|
|
310
|
+
const crossHit = surface
|
|
311
|
+
? findRunBodyCrossingSurface(start, end, BODY_SNAP_RADIUS_M, surface)
|
|
312
|
+
: null;
|
|
313
|
+
const crossOwner = crossHit ? nodes[crossHit.nodeId] : null;
|
|
291
314
|
const crossTappedElsewhere = crossHit?.nodeId === trunkBody?.nodeId || crossHit?.nodeId === endTrunkBody?.nodeId;
|
|
292
|
-
|
|
315
|
+
const cross = crossHit && !crossTappedElsewhere && crossOwner?.type === 'duct-segment'
|
|
293
316
|
? planCrossAtRunBody(crossOwner, crossHit, dir, profile)
|
|
294
317
|
: null;
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
ductStart
|
|
302
|
-
|
|
303
|
-
|
|
318
|
+
if (!hasFittingClearance(ductStart, ductEnd, dir, 0.08) ||
|
|
319
|
+
(trunkBody && !teePlan) ||
|
|
320
|
+
(endTrunkBody && !endTeePlan) ||
|
|
321
|
+
(crossHit && !crossTappedElsewhere && !cross))
|
|
322
|
+
return invalidPlan();
|
|
323
|
+
if (cross &&
|
|
324
|
+
(!hasFittingClearance(ductStart, cross.branchCollarNear, dir, 0.08) ||
|
|
325
|
+
!hasFittingClearance(cross.branchCollarFar, ductEnd, dir, 0.08)))
|
|
326
|
+
return invalidPlan();
|
|
304
327
|
// Rect / oval continuity: roll the new run's cross-section so its
|
|
305
328
|
// profile stays continuous with whatever either end joined.
|
|
306
329
|
let roll = 0;
|
|
307
330
|
if (profile.shape !== 'round') {
|
|
308
331
|
const newDir = new Vector3(...dir);
|
|
309
|
-
roll = continuityRollForRun(startPort, endPort, newDir);
|
|
332
|
+
roll = continuityRollForRun(startPort, endPort, newDir, nodes);
|
|
310
333
|
}
|
|
311
334
|
const defaults = ductSegmentDefinition.defaults();
|
|
312
|
-
const toolDefaults = useEditor.getState().toolDefaults['duct-segment'] ?? {};
|
|
313
335
|
const makeDuct = (from, to) => DuctSegmentNode.parse({
|
|
314
336
|
...defaults,
|
|
315
337
|
...toolDefaults,
|
|
338
|
+
autoHangers,
|
|
339
|
+
hangerStyle,
|
|
316
340
|
name: profile.shape === 'rect' ? 'Trunk' : 'Duct run',
|
|
317
341
|
path: [from, to],
|
|
318
342
|
shape: profile.shape,
|
|
@@ -333,6 +357,7 @@ function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profil
|
|
|
333
357
|
: [makeDuct(ductStart, ductEnd)];
|
|
334
358
|
const fittings = [
|
|
335
359
|
...plans.map((p) => p.fitting),
|
|
360
|
+
...[startAdapter, endAdapter].flatMap((p) => (p ? [p.fitting] : [])),
|
|
336
361
|
...(tee ? [tee.fitting] : []),
|
|
337
362
|
...(endTee ? [endTee.fitting] : []),
|
|
338
363
|
...(cross ? [cross.fitting] : []),
|
|
@@ -349,587 +374,191 @@ function planDuctDraw(start, end, startPort, startBody, endPort, endBody, profil
|
|
|
349
374
|
...(cross ? [cross.trunkUpdate] : []),
|
|
350
375
|
...realigns.map((p) => p.update),
|
|
351
376
|
];
|
|
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
|
-
};
|
|
377
|
+
const deleteIds = Array.from(new Set([
|
|
378
|
+
...findMatedRunEndCapIds(startPort, nodes, 'duct-fitting'),
|
|
379
|
+
...findMatedRunEndCapIds(endPort, nodes, 'duct-fitting'),
|
|
380
|
+
]));
|
|
381
|
+
const firstDuct = ducts[0];
|
|
382
|
+
const nextDuct = ducts.at(-1);
|
|
383
|
+
const startEndCap = !startPort && !startBody && firstDuct ? createDuctRunEndCap(firstDuct, 'start') : null;
|
|
384
|
+
const nextEndCap = !endPort && !endBody && nextDuct ? createDuctRunEndCap(nextDuct) : null;
|
|
385
|
+
if (startEndCap)
|
|
386
|
+
fittings.push(startEndCap);
|
|
387
|
+
if (nextEndCap)
|
|
388
|
+
fittings.push(nextEndCap);
|
|
389
|
+
return { validationMessage: null, fittings, ducts, tails, updates, delete: deleteIds };
|
|
374
390
|
}
|
|
375
391
|
const DuctSegmentTool = () => {
|
|
376
|
-
const activeLevelId =
|
|
377
|
-
const unit = useViewer((s) => s.unit);
|
|
392
|
+
const { activeLevelId, sceneApi, unit } = useRegistryToolContext();
|
|
378
393
|
const cursorRef = useRef(null);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
394
|
+
const continuationSeedRef = useRef(currentDuctContinuationSeed());
|
|
395
|
+
const continuationSeed = continuationSeedRef.current;
|
|
396
|
+
const hangerDefaults = useEditor((state) => state.toolDefaults['duct-segment']);
|
|
397
|
+
const initialAutoHangersRef = useRef(Boolean(hangerDefaults?.autoHangers ?? continuationSeed?.duct.autoHangers ?? false));
|
|
398
|
+
const autoHangers = useRunHangerMode((state) => state.enabled['duct-segment']);
|
|
399
|
+
const hangerStyle = (hangerDefaults?.hangerStyle ?? continuationSeed?.duct.hangerStyle) === 'double'
|
|
400
|
+
? 'double'
|
|
401
|
+
: 'single';
|
|
402
|
+
const hangerStyleRef = useRef(hangerStyle);
|
|
403
|
+
hangerStyleRef.current = hangerStyle;
|
|
404
|
+
const autoHangersRef = useRef(autoHangers);
|
|
405
|
+
autoHangersRef.current = autoHangers;
|
|
406
|
+
const pendingPromotionRef = useRef(continuationSeed?.promotedFitting ?? null);
|
|
384
407
|
const [profile, setProfile] = useState(() => {
|
|
385
408
|
const defaults = ductSegmentDefinition.defaults();
|
|
386
409
|
const seeded = useEditor.getState().toolDefaults['duct-segment'];
|
|
387
410
|
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,
|
|
411
|
+
shape: continuationSeed?.duct.shape ?? seeded?.shape ?? defaults.shape,
|
|
412
|
+
diameter: continuationSeed?.duct.diameter ?? seeded?.diameter ?? defaults.diameter,
|
|
413
|
+
width: continuationSeed?.duct.width ?? seeded?.width ?? defaults.width,
|
|
414
|
+
height: continuationSeed?.duct.height ?? seeded?.height ?? defaults.height,
|
|
392
415
|
};
|
|
393
416
|
});
|
|
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
417
|
const profileRef = useRef(profile);
|
|
424
418
|
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
|
-
useEffect(() => {
|
|
451
|
-
usePathDraftPreview
|
|
452
|
-
.getState()
|
|
453
|
-
.setDraft('duct-segment', draftPoints, cursorPos, profile, ghostFittings);
|
|
454
|
-
}, [cursorPos, draftPoints, ghostFittings, profile]);
|
|
455
|
-
useEffect(() => () => usePathDraftPreview.getState().clear('duct-segment'), []);
|
|
456
419
|
useEffect(() => {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
420
|
+
const mode = useRunHangerMode.getState();
|
|
421
|
+
mode.setEnabled('duct-segment', initialAutoHangersRef.current);
|
|
422
|
+
return () => mode.setEnabled('duct-segment', false);
|
|
423
|
+
}, []);
|
|
424
|
+
const run = useDistributionRunTool({
|
|
425
|
+
active: !!activeLevelId,
|
|
426
|
+
levelId: activeLevelId,
|
|
427
|
+
toolName: 'duct-segment',
|
|
428
|
+
initialStart: continuationSeed
|
|
429
|
+
? [...(continuationSeed.port?.position ?? continuationSeed.body?.point ?? [0, 0, 0])]
|
|
430
|
+
: null,
|
|
431
|
+
initialConnection: continuationSeed
|
|
432
|
+
? { port: continuationSeed.port, body: continuationSeed.body }
|
|
433
|
+
: null,
|
|
434
|
+
getPorts: () => getConnectionPorts(activeLevelId, sceneApi.nodes()),
|
|
435
|
+
findBody: (point) => findNearestRunBody3D(point, BODY_SNAP_RADIUS_M, { levelId: activeLevelId ?? undefined }),
|
|
436
|
+
surfaceClearance: (surface) => surface ? ductSurfaceClearanceM(profileRef.current, surface.kind === 'wall') : 0,
|
|
437
|
+
minimumSegmentLength: 0.08,
|
|
438
|
+
inheritFromConnection: ({ port }) => {
|
|
439
|
+
if (!port)
|
|
469
440
|
return;
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
441
|
+
const inherited = inheritProfile(port, sceneApi.nodes());
|
|
442
|
+
if (inherited)
|
|
443
|
+
setProfile(inherited);
|
|
444
|
+
},
|
|
445
|
+
commit: ({ start, end, startConnection, endConnection, surfaceTarget }) => {
|
|
446
|
+
if (!activeLevelId)
|
|
447
|
+
return null;
|
|
448
|
+
const promotedFitting = pendingPromotionRef.current;
|
|
449
|
+
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);
|
|
450
|
+
if (!plan || plan.validationMessage)
|
|
451
|
+
return null;
|
|
452
|
+
const attachDuct = (node) => {
|
|
453
|
+
const wallAttachment = surfaceTarget?.kind === 'wall'
|
|
454
|
+
? createRunWallAttachment(surfaceTarget.hostId, surfaceTarget.side, node.path[0], node.path.at(-1), surfaceTarget, ductSurfaceClearanceM(profileRef.current, true))
|
|
455
|
+
: undefined;
|
|
456
|
+
return { ...node, wallAttachment };
|
|
457
|
+
};
|
|
458
|
+
const ducts = plan.ducts.map(attachDuct);
|
|
459
|
+
const tails = plan.tails;
|
|
460
|
+
if (!sceneApi.applyChanges)
|
|
461
|
+
throw new Error('Registry SceneApi must support atomic changes');
|
|
462
|
+
sceneApi.applyChanges({
|
|
473
463
|
create: [
|
|
474
464
|
...plan.fittings.map((node) => ({ node, parentId: activeLevelId })),
|
|
475
|
-
...
|
|
476
|
-
...
|
|
465
|
+
...tails.map((node) => ({ node, parentId: activeLevelId })),
|
|
466
|
+
...ducts.map((node) => ({ node, parentId: activeLevelId })),
|
|
467
|
+
],
|
|
468
|
+
update: [
|
|
469
|
+
...(promotedFitting
|
|
470
|
+
? [
|
|
471
|
+
{
|
|
472
|
+
id: promotedFitting.id,
|
|
473
|
+
data: {
|
|
474
|
+
name: promotedFitting.name,
|
|
475
|
+
fittingType: promotedFitting.fittingType,
|
|
476
|
+
rotation: promotedFitting.rotation,
|
|
477
|
+
branchAngle: promotedFitting.branchAngle,
|
|
478
|
+
shape2: promotedFitting.shape2,
|
|
479
|
+
width2: promotedFitting.width2,
|
|
480
|
+
height2: promotedFitting.height2,
|
|
481
|
+
diameter2: promotedFitting.diameter2,
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
]
|
|
485
|
+
: []),
|
|
486
|
+
...plan.updates,
|
|
477
487
|
],
|
|
478
|
-
|
|
488
|
+
delete: plan.delete,
|
|
479
489
|
});
|
|
490
|
+
pendingPromotionRef.current = null;
|
|
480
491
|
const nextDuct = plan.ducts.at(-1);
|
|
481
492
|
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];
|
|
493
|
+
const nextPort = nextDuct ? ductEndpointPort(nextDuct, 'end') : endConnection.port;
|
|
589
494
|
return {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
495
|
+
nextStart,
|
|
496
|
+
nextConnection: {
|
|
497
|
+
port: nextPort,
|
|
498
|
+
body: nextPort ? null : endConnection.body,
|
|
499
|
+
},
|
|
594
500
|
};
|
|
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;
|
|
501
|
+
},
|
|
502
|
+
onShortcut: (event) => {
|
|
503
|
+
if (event.key === '[' || event.key === ']') {
|
|
504
|
+
event.preventDefault();
|
|
505
|
+
const next = stepNominalRunSize(DUCT_DIAMETERS_IN, profileRef.current.diameter, event.key === ']' ? 1 : -1);
|
|
506
|
+
if (next !== profileRef.current.diameter) {
|
|
507
|
+
setProfile((current) => ({ ...current, diameter: next }));
|
|
508
|
+
triggerSFX('sfx:grid-snap');
|
|
653
509
|
}
|
|
654
510
|
}
|
|
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' }));
|
|
511
|
+
else if (event.key === 'q' || event.key === 'Q') {
|
|
512
|
+
event.preventDefault();
|
|
513
|
+
setProfile((current) => ({
|
|
514
|
+
...current,
|
|
515
|
+
shape: current.shape === 'round' ? 'rect' : current.shape === 'rect' ? 'oval' : 'round',
|
|
516
|
+
}));
|
|
748
517
|
triggerSFX('sfx:grid-snap');
|
|
749
518
|
}
|
|
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);
|
|
519
|
+
else if (event.key === 'h' || event.key === 'H') {
|
|
520
|
+
event.preventDefault();
|
|
521
|
+
useRunHangerMode.getState().toggle('duct-segment');
|
|
760
522
|
triggerSFX('sfx:grid-snap');
|
|
761
523
|
}
|
|
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]);
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
const previewPlan = useMemo(() => {
|
|
527
|
+
if (!(activeLevelId && run.start && run.cursor))
|
|
528
|
+
return null;
|
|
529
|
+
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);
|
|
530
|
+
}, [
|
|
531
|
+
activeLevelId,
|
|
532
|
+
autoHangers,
|
|
533
|
+
hangerDefaults,
|
|
534
|
+
hangerStyle,
|
|
535
|
+
profile,
|
|
536
|
+
run.start,
|
|
537
|
+
run.cursor,
|
|
538
|
+
run.startConnection,
|
|
539
|
+
run.endConnection,
|
|
540
|
+
run.surfaceTarget,
|
|
541
|
+
sceneApi,
|
|
542
|
+
]);
|
|
543
|
+
const ghostFittings = useMemo(() => previewPlan?.fittings ?? [], [previewPlan]);
|
|
544
|
+
useEffect(() => {
|
|
545
|
+
usePathDraftPreview
|
|
546
|
+
.getState()
|
|
547
|
+
.setDraft('duct-segment', run.start ? [run.start] : [], run.cursor, { ...profile, autoHangers, hangerStyle }, ghostFittings);
|
|
548
|
+
}, [autoHangers, hangerStyle, ghostFittings, profile, run.cursor, run.start]);
|
|
549
|
+
useEffect(() => () => usePathDraftPreview.getState().clear('duct-segment'), []);
|
|
550
|
+
useEffect(() => () => useEditor.getState().setToolDefaults('duct-segment', null), []);
|
|
797
551
|
if (!activeLevelId)
|
|
798
552
|
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)))] }));
|
|
553
|
+
const extraParts = profile.shape === 'round'
|
|
554
|
+
? [{ key: 'diameter', prefix: 'Ø', value: profile.diameter * 0.0254 }]
|
|
555
|
+
: [
|
|
556
|
+
{ key: 'trunk-w', prefix: 'W', value: profile.width * 0.0254 },
|
|
557
|
+
{ key: 'trunk-h', prefix: 'H', value: profile.height * 0.0254 },
|
|
558
|
+
];
|
|
559
|
+
return (_jsxs(LevelOffsetGroup, { children: [_jsx(ConnectionFeedback, { point: run.cursor, target: run.endConnection.port, levelId: activeLevelId, profile: {
|
|
560
|
+
...profile,
|
|
561
|
+
system: String(hangerDefaults?.system ?? ductSegmentDefinition.defaults().system),
|
|
562
|
+
} }), _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
563
|
};
|
|
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
564
|
export default DuctSegmentTool;
|