@pascal-app/core 0.8.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/events/bus.d.ts +67 -13
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/scene-registry/item-clip-registry.d.ts +18 -0
- package/dist/hooks/scene-registry/item-clip-registry.d.ts.map +1 -0
- package/dist/hooks/scene-registry/item-clip-registry.js +10 -0
- package/dist/hooks/scene-registry/scene-registry.d.ts +14 -23
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +59 -26
- package/dist/hooks/scene-registry/scene-registry.test.d.ts +2 -0
- package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +1 -0
- package/dist/hooks/scene-registry/scene-registry.test.js +26 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +366 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +79 -11
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +508 -141
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +109 -18
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +363 -0
- package/dist/index.d.ts +36 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -6
- package/dist/lib/level-name.d.ts +4 -0
- package/dist/lib/level-name.d.ts.map +1 -0
- package/dist/lib/level-name.js +10 -0
- package/dist/lib/measurement-geometry.d.ts +19 -0
- package/dist/lib/measurement-geometry.d.ts.map +1 -0
- package/dist/lib/measurement-geometry.js +232 -0
- package/dist/lib/measurement-geometry.test.d.ts +2 -0
- package/dist/lib/measurement-geometry.test.d.ts.map +1 -0
- package/dist/lib/measurement-geometry.test.js +102 -0
- package/dist/lib/polygon-geometry.d.ts +0 -1
- package/dist/lib/polygon-geometry.d.ts.map +1 -1
- package/dist/lib/polygon-geometry.js +0 -18
- package/dist/lib/polygon-relations.d.ts +10 -0
- package/dist/lib/polygon-relations.d.ts.map +1 -0
- package/dist/lib/polygon-relations.js +73 -0
- package/dist/lib/selection-proxy.d.ts +4 -0
- package/dist/lib/selection-proxy.d.ts.map +1 -0
- package/dist/lib/selection-proxy.js +12 -0
- package/dist/lib/slab-polygon.d.ts +31 -2
- package/dist/lib/slab-polygon.d.ts.map +1 -1
- package/dist/lib/slab-polygon.js +525 -31
- package/dist/lib/slab-polygon.test.d.ts +2 -0
- package/dist/lib/slab-polygon.test.d.ts.map +1 -0
- package/dist/lib/slab-polygon.test.js +635 -0
- package/dist/lib/slots.d.ts +14 -0
- package/dist/lib/slots.d.ts.map +1 -0
- package/dist/lib/slots.js +26 -0
- package/dist/lib/space-detection-pause.test.d.ts +2 -0
- package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
- package/dist/lib/space-detection-pause.test.js +42 -0
- package/dist/lib/space-detection.d.ts +54 -2
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +599 -124
- package/dist/lib/space-detection.test.d.ts +2 -0
- package/dist/lib/space-detection.test.d.ts.map +1 -0
- package/dist/lib/space-detection.test.js +376 -0
- package/dist/lib/wall-distance.d.ts +60 -0
- package/dist/lib/wall-distance.d.ts.map +1 -0
- package/dist/lib/wall-distance.js +85 -0
- package/dist/lib/zone-quantities.d.ts +21 -0
- package/dist/lib/zone-quantities.d.ts.map +1 -0
- package/dist/lib/zone-quantities.js +183 -0
- package/dist/lib/zone-quantities.test.d.ts +2 -0
- package/dist/lib/zone-quantities.test.d.ts.map +1 -0
- package/dist/lib/zone-quantities.test.js +82 -0
- package/dist/material-library.d.ts +20 -1
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +3737 -200
- package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
- package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
- package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
- package/dist/registry/handles.d.ts +394 -0
- package/dist/registry/handles.d.ts.map +1 -0
- package/dist/registry/handles.js +24 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/registry.d.ts +136 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +282 -0
- package/dist/registry/registry.test.d.ts +2 -0
- package/dist/registry/registry.test.d.ts.map +1 -0
- package/dist/registry/registry.test.js +196 -0
- package/dist/registry/relations-resolver.d.ts +42 -0
- package/dist/registry/relations-resolver.d.ts.map +1 -0
- package/dist/registry/relations-resolver.js +77 -0
- package/dist/registry/relations-resolver.test.d.ts +2 -0
- package/dist/registry/relations-resolver.test.d.ts.map +1 -0
- package/dist/registry/relations-resolver.test.js +183 -0
- package/dist/registry/scene-api.d.ts +44 -0
- package/dist/registry/scene-api.d.ts.map +1 -0
- package/dist/registry/scene-api.js +111 -0
- package/dist/registry/scene-api.test.d.ts +2 -0
- package/dist/registry/scene-api.test.d.ts.map +1 -0
- package/dist/registry/scene-api.test.js +183 -0
- package/dist/registry/subtree.d.ts +67 -0
- package/dist/registry/subtree.d.ts.map +1 -0
- package/dist/registry/subtree.js +123 -0
- package/dist/registry/subtree.test.d.ts +2 -0
- package/dist/registry/subtree.test.d.ts.map +1 -0
- package/dist/registry/subtree.test.js +163 -0
- package/dist/registry/types.d.ts +2099 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +1 -0
- package/dist/schema/index.d.ts +35 -4
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +32 -4
- package/dist/schema/material.d.ts +13 -2
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +17 -5
- package/dist/schema/material.test.d.ts +2 -0
- package/dist/schema/material.test.d.ts.map +1 -0
- package/dist/schema/material.test.js +43 -0
- package/dist/schema/nodes/box-vent.d.ts +74 -0
- package/dist/schema/nodes/box-vent.d.ts.map +1 -0
- package/dist/schema/nodes/box-vent.js +63 -0
- package/dist/schema/nodes/building.d.ts +1 -1
- package/dist/schema/nodes/building.d.ts.map +1 -1
- package/dist/schema/nodes/building.js +3 -2
- package/dist/schema/nodes/cabinet.d.ts +534 -0
- package/dist/schema/nodes/cabinet.d.ts.map +1 -0
- package/dist/schema/nodes/cabinet.js +137 -0
- package/dist/schema/nodes/ceiling.d.ts +5 -2
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +5 -1
- package/dist/schema/nodes/chimney.d.ts +158 -0
- package/dist/schema/nodes/chimney.d.ts.map +1 -0
- package/dist/schema/nodes/chimney.js +67 -0
- package/dist/schema/nodes/column.d.ts +600 -7
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +589 -6
- package/dist/schema/nodes/cupola.d.ts +66 -0
- package/dist/schema/nodes/cupola.d.ts.map +1 -0
- package/dist/schema/nodes/cupola.js +28 -0
- package/dist/schema/nodes/door.d.ts +13 -5
- package/dist/schema/nodes/door.d.ts.map +1 -1
- package/dist/schema/nodes/door.js +12 -0
- package/dist/schema/nodes/dormer.d.ts +228 -0
- package/dist/schema/nodes/dormer.d.ts.map +1 -0
- package/dist/schema/nodes/dormer.js +119 -0
- package/dist/schema/nodes/downspout.d.ts +75 -0
- package/dist/schema/nodes/downspout.d.ts.map +1 -0
- package/dist/schema/nodes/downspout.js +63 -0
- package/dist/schema/nodes/duct-fitting.d.ts +91 -0
- package/dist/schema/nodes/duct-fitting.d.ts.map +1 -0
- package/dist/schema/nodes/duct-fitting.js +92 -0
- package/dist/schema/nodes/duct-segment.d.ts +61 -0
- package/dist/schema/nodes/duct-segment.d.ts.map +1 -0
- package/dist/schema/nodes/duct-segment.js +73 -0
- package/dist/schema/nodes/duct-terminal.d.ts +57 -0
- package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
- package/dist/schema/nodes/duct-terminal.js +51 -0
- package/dist/schema/nodes/elevator.d.ts +106 -0
- package/dist/schema/nodes/elevator.d.ts.map +1 -0
- package/dist/schema/nodes/elevator.js +57 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
- package/dist/schema/nodes/eyebrow-vent.js +40 -0
- package/dist/schema/nodes/fence.d.ts +19 -2
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +27 -2
- package/dist/schema/nodes/gutter.d.ts +84 -0
- package/dist/schema/nodes/gutter.d.ts.map +1 -0
- package/dist/schema/nodes/gutter.js +76 -0
- package/dist/schema/nodes/hvac-equipment.d.ts +63 -0
- package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
- package/dist/schema/nodes/hvac-equipment.js +55 -0
- package/dist/schema/nodes/item.d.ts +12 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +20 -0
- package/dist/schema/nodes/level.d.ts +1 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +4 -0
- package/dist/schema/nodes/lineset.d.ts +42 -0
- package/dist/schema/nodes/lineset.d.ts.map +1 -0
- package/dist/schema/nodes/lineset.js +38 -0
- package/dist/schema/nodes/liquid-line.d.ts +34 -0
- package/dist/schema/nodes/liquid-line.d.ts.map +1 -0
- package/dist/schema/nodes/liquid-line.js +24 -0
- package/dist/schema/nodes/measurement.d.ts +306 -0
- package/dist/schema/nodes/measurement.d.ts.map +1 -0
- package/dist/schema/nodes/measurement.js +87 -0
- package/dist/schema/nodes/measurement.test.d.ts +2 -0
- package/dist/schema/nodes/measurement.test.d.ts.map +1 -0
- package/dist/schema/nodes/measurement.test.js +139 -0
- package/dist/schema/nodes/pipe-fitting.d.ts +56 -0
- package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-fitting.js +45 -0
- package/dist/schema/nodes/pipe-segment.d.ts +48 -0
- package/dist/schema/nodes/pipe-segment.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-segment.js +36 -0
- package/dist/schema/nodes/pipe-trap.d.ts +43 -0
- package/dist/schema/nodes/pipe-trap.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-trap.js +36 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +2 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.js +205 -0
- package/dist/schema/nodes/ridge-vent.d.ts +82 -0
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent.js +248 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts +90 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-shape.js +318 -0
- package/dist/schema/nodes/roof-segment-shape.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-shape.test.js +175 -0
- package/dist/schema/nodes/roof-segment-surface.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-surface.test.js +48 -0
- package/dist/schema/nodes/roof-segment-trim.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-trim.test.js +107 -0
- package/dist/schema/nodes/roof-segment-walls.d.ts +101 -0
- package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-walls.js +330 -0
- package/dist/schema/nodes/roof-segment-walls.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-walls.test.js +92 -0
- package/dist/schema/nodes/roof-segment.d.ts +273 -3
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +539 -3
- package/dist/schema/nodes/roof.d.ts +8 -8
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +4 -16
- package/dist/schema/nodes/shelf.d.ts +106 -0
- package/dist/schema/nodes/shelf.d.ts.map +1 -0
- package/dist/schema/nodes/shelf.js +90 -0
- package/dist/schema/nodes/site.d.ts +1 -122
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/site.js +2 -6
- package/dist/schema/nodes/skylight.d.ts +279 -0
- package/dist/schema/nodes/skylight.d.ts.map +1 -0
- package/dist/schema/nodes/skylight.js +152 -0
- package/dist/schema/nodes/slab.d.ts +5 -2
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +5 -1
- package/dist/schema/nodes/solar-panel.d.ts +117 -0
- package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
- package/dist/schema/nodes/solar-panel.js +47 -0
- package/dist/schema/nodes/stair-segment.d.ts +4 -4
- package/dist/schema/nodes/stair.d.ts +13 -12
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +3 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
- package/dist/schema/nodes/surface-hole-metadata.js +3 -1
- package/dist/schema/nodes/turbine-vent.d.ts +68 -0
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
- package/dist/schema/nodes/turbine-vent.js +41 -0
- package/dist/schema/nodes/wall.d.ts +245 -6
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +257 -0
- package/dist/schema/nodes/wall.test.d.ts +2 -0
- package/dist/schema/nodes/wall.test.d.ts.map +1 -0
- package/dist/schema/nodes/wall.test.js +208 -0
- package/dist/schema/nodes/window.d.ts +11 -3
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +12 -0
- package/dist/schema/nodes/zone.d.ts +2 -0
- package/dist/schema/nodes/zone.d.ts.map +1 -1
- package/dist/schema/nodes/zone.js +6 -0
- package/dist/schema/scene-material.d.ts +41 -0
- package/dist/schema/scene-material.d.ts.map +1 -0
- package/dist/schema/scene-material.js +9 -0
- package/dist/schema/types.d.ts +2850 -655
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +49 -0
- package/dist/services/alignment-anchors.d.ts +100 -0
- package/dist/services/alignment-anchors.d.ts.map +1 -0
- package/dist/services/alignment-anchors.js +312 -0
- package/dist/services/alignment-anchors.test.d.ts +2 -0
- package/dist/services/alignment-anchors.test.d.ts.map +1 -0
- package/dist/services/alignment-anchors.test.js +383 -0
- package/dist/services/alignment.d.ts +131 -0
- package/dist/services/alignment.d.ts.map +1 -0
- package/dist/services/alignment.js +171 -0
- package/dist/services/alignment.test.d.ts +2 -0
- package/dist/services/alignment.test.d.ts.map +1 -0
- package/dist/services/alignment.test.js +92 -0
- package/dist/services/drag-session.d.ts +47 -0
- package/dist/services/drag-session.d.ts.map +1 -0
- package/dist/services/drag-session.js +100 -0
- package/dist/services/drag-session.test.d.ts +2 -0
- package/dist/services/drag-session.test.d.ts.map +1 -0
- package/dist/services/drag-session.test.js +219 -0
- package/dist/services/hosting.d.ts +88 -0
- package/dist/services/hosting.d.ts.map +1 -0
- package/dist/services/hosting.js +131 -0
- package/dist/services/hosting.test.d.ts +2 -0
- package/dist/services/hosting.test.d.ts.map +1 -0
- package/dist/services/hosting.test.js +230 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +12 -0
- package/dist/services/level-height.d.ts +28 -0
- package/dist/services/level-height.d.ts.map +1 -0
- package/dist/services/level-height.js +62 -0
- package/dist/services/movement.d.ts +48 -0
- package/dist/services/movement.d.ts.map +1 -0
- package/dist/services/movement.js +65 -0
- package/dist/services/movement.test.d.ts +2 -0
- package/dist/services/movement.test.d.ts.map +1 -0
- package/dist/services/movement.test.js +133 -0
- package/dist/services/opening-guides.d.ts +141 -0
- package/dist/services/opening-guides.d.ts.map +1 -0
- package/dist/services/opening-guides.js +267 -0
- package/dist/services/opening-guides.test.d.ts +2 -0
- package/dist/services/opening-guides.test.d.ts.map +1 -0
- package/dist/services/opening-guides.test.js +208 -0
- package/dist/services/port-connectivity.d.ts +115 -0
- package/dist/services/port-connectivity.d.ts.map +1 -0
- package/dist/services/port-connectivity.js +300 -0
- package/dist/services/port-connectivity.test.d.ts +2 -0
- package/dist/services/port-connectivity.test.d.ts.map +1 -0
- package/dist/services/port-connectivity.test.js +297 -0
- package/dist/services/riser-diagram.d.ts +40 -0
- package/dist/services/riser-diagram.d.ts.map +1 -0
- package/dist/services/riser-diagram.js +110 -0
- package/dist/services/riser-diagram.test.d.ts +2 -0
- package/dist/services/riser-diagram.test.d.ts.map +1 -0
- package/dist/services/riser-diagram.test.js +66 -0
- package/dist/services/single-undo-dance.test.d.ts +2 -0
- package/dist/services/single-undo-dance.test.d.ts.map +1 -0
- package/dist/services/single-undo-dance.test.js +210 -0
- package/dist/services/snap.d.ts +80 -0
- package/dist/services/snap.d.ts.map +1 -0
- package/dist/services/snap.js +140 -0
- package/dist/services/snap.test.d.ts +2 -0
- package/dist/services/snap.test.d.ts.map +1 -0
- package/dist/services/snap.test.js +137 -0
- package/dist/services/system-graph.d.ts +30 -0
- package/dist/services/system-graph.d.ts.map +1 -0
- package/dist/services/system-graph.js +164 -0
- package/dist/services/system-graph.test.d.ts +2 -0
- package/dist/services/system-graph.test.d.ts.map +1 -0
- package/dist/services/system-graph.test.js +138 -0
- package/dist/services/validate-dwv.d.ts +32 -0
- package/dist/services/validate-dwv.d.ts.map +1 -0
- package/dist/services/validate-dwv.js +118 -0
- package/dist/services/validate-dwv.test.d.ts +2 -0
- package/dist/services/validate-dwv.test.d.ts.map +1 -0
- package/dist/services/validate-dwv.test.js +109 -0
- package/dist/solar-panel-presets.d.ts +17 -0
- package/dist/solar-panel-presets.d.ts.map +1 -0
- package/dist/solar-panel-presets.js +43 -0
- package/dist/store/actions/node-actions.d.ts +14 -2
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +585 -15
- package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
- package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
- package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
- package/dist/store/actions/reparent.test.d.ts +2 -0
- package/dist/store/actions/reparent.test.d.ts.map +1 -0
- package/dist/store/actions/reparent.test.js +212 -0
- package/dist/store/actions/ridge-vent-update.test.d.ts +2 -0
- package/dist/store/actions/ridge-vent-update.test.d.ts.map +1 -0
- package/dist/store/actions/ridge-vent-update.test.js +264 -0
- package/dist/store/history-control.d.ts +11 -0
- package/dist/store/history-control.d.ts.map +1 -1
- package/dist/store/history-control.js +33 -0
- package/dist/store/use-interactive.d.ts +39 -0
- package/dist/store/use-interactive.d.ts.map +1 -1
- package/dist/store/use-interactive.js +75 -0
- package/dist/store/use-live-node-overrides.d.ts +22 -0
- package/dist/store/use-live-node-overrides.d.ts.map +1 -0
- package/dist/store/use-live-node-overrides.js +59 -0
- package/dist/store/use-scene-dirty-tracking.test.d.ts +2 -0
- package/dist/store/use-scene-dirty-tracking.test.d.ts.map +1 -0
- package/dist/store/use-scene-dirty-tracking.test.js +68 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-elevator-migration.test.js +109 -0
- package/dist/store/use-scene-measurement-roundtrip.test.d.ts +2 -0
- package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +1 -0
- package/dist/store/use-scene-measurement-roundtrip.test.js +97 -0
- package/dist/store/use-scene-plugins.test.d.ts +2 -0
- package/dist/store/use-scene-plugins.test.d.ts.map +1 -0
- package/dist/store/use-scene-plugins.test.js +51 -0
- package/dist/store/use-scene-wall-slot-migration.test.d.ts +2 -0
- package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-wall-slot-migration.test.js +210 -0
- package/dist/store/use-scene-window-migration.test.d.ts +2 -0
- package/dist/store/use-scene-window-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-window-migration.test.js +85 -0
- package/dist/store/use-scene.d.ts +28 -2
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +546 -49
- package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
- package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-dispatch.js +57 -0
- package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
- package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-geometry.js +50 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.js +218 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-system.js +48 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime-system.js +8 -0
- package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
- package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.js +225 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.test.js +64 -0
- package/dist/systems/elevator/elevator-service.d.ts +19 -0
- package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-service.js +111 -0
- package/dist/systems/fence/fence-centerline.d.ts +12 -0
- package/dist/systems/fence/fence-centerline.d.ts.map +1 -0
- package/dist/systems/fence/fence-centerline.js +37 -0
- package/dist/systems/fence/fence-spline.d.ts +39 -0
- package/dist/systems/fence/fence-spline.d.ts.map +1 -0
- package/dist/systems/fence/fence-spline.js +196 -0
- package/dist/systems/fence/fence-spline.test.d.ts +2 -0
- package/dist/systems/fence/fence-spline.test.d.ts.map +1 -0
- package/dist/systems/fence/fence-spline.test.js +136 -0
- package/dist/systems/stair/stair-footprint.d.ts +44 -0
- package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
- package/dist/systems/stair/stair-footprint.js +178 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +2447 -0
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.js +93 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.test.js +80 -0
- package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-sync.js +155 -147
- package/dist/systems/stair/stair-opening-sync.test.js +424 -2
- package/dist/systems/stair/stair-opening-system.d.ts +2 -0
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-system.js +104 -0
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +38 -2
- package/dist/systems/wall/wall-mitering.test.d.ts +2 -0
- package/dist/systems/wall/wall-mitering.test.d.ts.map +1 -0
- package/dist/systems/wall/wall-mitering.test.js +63 -0
- package/dist/systems/wall/wall-move.d.ts +48 -0
- package/dist/systems/wall/wall-move.d.ts.map +1 -0
- package/dist/systems/wall/wall-move.js +193 -0
- package/dist/utils/clone-scene-graph.d.ts +7 -4
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +29 -7
- package/dist/utils/clone-scene-graph.test.d.ts +2 -0
- package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/clone-scene-graph.test.js +62 -0
- package/dist/utils/heal-scene-graph.d.ts +18 -0
- package/dist/utils/heal-scene-graph.d.ts.map +1 -0
- package/dist/utils/heal-scene-graph.js +90 -0
- package/dist/utils/heal-scene-graph.test.d.ts +2 -0
- package/dist/utils/heal-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/heal-scene-graph.test.js +88 -0
- package/dist/validation/validate-build-json.d.ts +44 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -0
- package/dist/validation/validate-build-json.js +256 -0
- package/package.json +11 -4
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { computeEdgeGaps, computeOpeningGuides, detectAlongWallAlignment, detectEqualSpacing, detectVerticalAlignment, } from './opening-guides';
|
|
3
|
+
function span(id, centerS, width, centerY = 1, height = 1) {
|
|
4
|
+
return { id, centerS, width, centerY, height };
|
|
5
|
+
}
|
|
6
|
+
const WALL = { length: 10, height: 2.5 };
|
|
7
|
+
describe('detectEqualSpacing', () => {
|
|
8
|
+
test('returns null for fewer than three openings', () => {
|
|
9
|
+
const a = span('a', 0.5, 1);
|
|
10
|
+
const b = span('b', 2.5, 1);
|
|
11
|
+
expect(detectEqualSpacing([a, b], 'b', 0.03, 0.02)).toBeNull();
|
|
12
|
+
});
|
|
13
|
+
test('detects a run of equal gaps across three openings', () => {
|
|
14
|
+
// width 1 each: a[0,1] b[2,3] c[4,5] → two gaps of 1m.
|
|
15
|
+
const a = span('a', 0.5, 1);
|
|
16
|
+
const b = span('b', 2.5, 1);
|
|
17
|
+
const c = span('c', 4.5, 1);
|
|
18
|
+
const run = detectEqualSpacing([a, b, c], 'b', 0.03, 0.02);
|
|
19
|
+
expect(run).not.toBeNull();
|
|
20
|
+
expect(run?.gap).toBeCloseTo(1);
|
|
21
|
+
expect(run?.segments).toHaveLength(2);
|
|
22
|
+
expect(run?.openingIds).toEqual(['a', 'b', 'c']);
|
|
23
|
+
expect(run?.segments[0]).toEqual({ fromS: 1, toS: 2 });
|
|
24
|
+
expect(run?.segments[1]).toEqual({ fromS: 3, toS: 4 });
|
|
25
|
+
});
|
|
26
|
+
test('extends a run across four openings (three gaps)', () => {
|
|
27
|
+
const openings = [span('a', 0.5, 1), span('b', 2.5, 1), span('c', 4.5, 1), span('d', 6.5, 1)];
|
|
28
|
+
const run = detectEqualSpacing(openings, 'c', 0.03, 0.02);
|
|
29
|
+
expect(run?.segments).toHaveLength(3);
|
|
30
|
+
expect(run?.openingIds).toEqual(['a', 'b', 'c', 'd']);
|
|
31
|
+
});
|
|
32
|
+
test('returns null when gaps differ beyond tolerance', () => {
|
|
33
|
+
const a = span('a', 0.5, 1); // [0,1]
|
|
34
|
+
const b = span('b', 2.5, 1); // [2,3] → gap 1
|
|
35
|
+
const c = span('c', 5, 1); // [4.5,5.5] → gap 1.5
|
|
36
|
+
expect(detectEqualSpacing([a, b, c], 'b', 0.03, 0.02)).toBeNull();
|
|
37
|
+
});
|
|
38
|
+
test('returns null when the moving opening is not part of the equal run', () => {
|
|
39
|
+
const a = span('a', 0.5, 1);
|
|
40
|
+
const b = span('b', 2.5, 1);
|
|
41
|
+
const c = span('c', 4.5, 1); // a,b,c form equal gaps of 1
|
|
42
|
+
const d = span('d', 10, 1); // far right, breaks the run
|
|
43
|
+
expect(detectEqualSpacing([a, b, c, d], 'd', 0.03, 0.02)).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
test('a near-zero (touching) gap breaks a run', () => {
|
|
46
|
+
const a = span('a', 0.5, 1); // [0,1]
|
|
47
|
+
const b = span('b', 1.505, 1); // [1.005,2.005] → gap 0.005 < minGap
|
|
48
|
+
const c = span('c', 3.005, 1); // [2.505,3.505] → gap 0.5
|
|
49
|
+
expect(detectEqualSpacing([a, b, c], 'b', 0.03, 0.02)).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
test('honours the equal-spacing tolerance', () => {
|
|
52
|
+
const a = span('a', 0.5, 1); // [0,1]
|
|
53
|
+
const b = span('b', 2.5, 1); // [2,3] → gap 1.0
|
|
54
|
+
const c = span('c', 4.52, 1); // [4.02,5.02] → gap 1.02
|
|
55
|
+
expect(detectEqualSpacing([a, b, c], 'b', 0.03, 0.02)?.segments).toHaveLength(2);
|
|
56
|
+
expect(detectEqualSpacing([a, b, c], 'b', 0.01, 0.02)).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('computeEdgeGaps', () => {
|
|
60
|
+
test('measures clearance to the nearest neighbour on each side', () => {
|
|
61
|
+
const moving = span('m', 5, 1); // [4.5,5.5]
|
|
62
|
+
const left = span('l', 2, 1); // [1.5,2.5]
|
|
63
|
+
const right = span('r', 8, 1); // [7.5,8.5]
|
|
64
|
+
const gaps = computeEdgeGaps(moving, [left, right], WALL, 0.02);
|
|
65
|
+
const byside = Object.fromEntries(gaps.map((g) => [g.side, g]));
|
|
66
|
+
expect(byside.left?.distance).toBeCloseTo(2);
|
|
67
|
+
expect(byside.left?.target).toBe('opening');
|
|
68
|
+
expect(byside.left?.targetId).toBe('l');
|
|
69
|
+
expect(byside.right?.distance).toBeCloseTo(2);
|
|
70
|
+
expect(byside.right?.targetId).toBe('r');
|
|
71
|
+
});
|
|
72
|
+
test('falls back to wall ends with no neighbour', () => {
|
|
73
|
+
const moving = span('m', 5, 1); // [4.5,5.5]
|
|
74
|
+
const gaps = computeEdgeGaps(moving, [], WALL, 0.02);
|
|
75
|
+
const byside = Object.fromEntries(gaps.map((g) => [g.side, g]));
|
|
76
|
+
expect(byside.left?.target).toBe('wall-start');
|
|
77
|
+
expect(byside.left?.distance).toBeCloseTo(4.5);
|
|
78
|
+
expect(byside.right?.target).toBe('wall-end');
|
|
79
|
+
expect(byside.right?.distance).toBeCloseTo(4.5);
|
|
80
|
+
});
|
|
81
|
+
test('omits a side that is flush / overlapping (below minGap)', () => {
|
|
82
|
+
const moving = span('m', 5, 1); // [4.5,5.5]
|
|
83
|
+
const flush = span('l', 4, 1); // [3.5,4.5] right edge touches moving left
|
|
84
|
+
const gaps = computeEdgeGaps(moving, [flush], WALL, 0.02);
|
|
85
|
+
expect(gaps.find((g) => g.side === 'left')).toBeUndefined();
|
|
86
|
+
expect(gaps.find((g) => g.side === 'right')?.target).toBe('wall-end');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('detectAlongWallAlignment', () => {
|
|
90
|
+
test('detects edge-to-edge alignment within tolerance', () => {
|
|
91
|
+
const moving = span('m', 5, 2); // [4,6]
|
|
92
|
+
const sib = span('s', 7.05, 2); // left edge 6.05
|
|
93
|
+
const a = detectAlongWallAlignment(moving, [sib], 0.08);
|
|
94
|
+
expect(a?.movingFeature).toBe('right');
|
|
95
|
+
expect(a?.targetFeature).toBe('left');
|
|
96
|
+
expect(a?.snap).toBeCloseTo(0.05);
|
|
97
|
+
expect(a?.s).toBeCloseTo(6.05);
|
|
98
|
+
});
|
|
99
|
+
test('detects centre alignment', () => {
|
|
100
|
+
const moving = span('m', 5, 2);
|
|
101
|
+
const sib = span('s', 5.03, 0.5); // centre 5.03, edges far from moving edges
|
|
102
|
+
const a = detectAlongWallAlignment(moving, [sib], 0.08);
|
|
103
|
+
expect(a?.movingFeature).toBe('center');
|
|
104
|
+
expect(a?.targetFeature).toBe('center');
|
|
105
|
+
expect(a?.snap).toBeCloseTo(0.03);
|
|
106
|
+
});
|
|
107
|
+
test('returns null when nothing is within tolerance', () => {
|
|
108
|
+
const moving = span('m', 5, 2);
|
|
109
|
+
const sib = span('s', 9, 2);
|
|
110
|
+
expect(detectAlongWallAlignment(moving, [sib], 0.08)).toBeNull();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('detectVerticalAlignment', () => {
|
|
114
|
+
test('detects a shared sill within tolerance', () => {
|
|
115
|
+
const moving = span('m', 5, 1, 1.5, 1); // sill 1.0
|
|
116
|
+
const sib = span('s', 8, 1, 2.04, 2); // sill 1.04
|
|
117
|
+
const a = detectVerticalAlignment(moving, [sib], 0.08);
|
|
118
|
+
expect(a?.movingFeature).toBe('sill');
|
|
119
|
+
expect(a?.targetFeature).toBe('sill');
|
|
120
|
+
expect(a?.snap).toBeCloseTo(0.04);
|
|
121
|
+
expect(a?.y).toBeCloseTo(1.04);
|
|
122
|
+
});
|
|
123
|
+
test('returns null when sills/tops differ beyond tolerance', () => {
|
|
124
|
+
const moving = span('m', 5, 1, 1.5, 1); // sill 1, top 2, centre 1.5
|
|
125
|
+
const sib = span('s', 8, 1, 0.4, 0.4); // sill 0.2, top 0.6, centre 0.4
|
|
126
|
+
expect(detectVerticalAlignment(moving, [sib], 0.08)).toBeNull();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe('computeOpeningGuides', () => {
|
|
130
|
+
test('includes sill/head for windows', () => {
|
|
131
|
+
const moving = span('m', 5, 1, 1.5, 1); // bottom 1, top 2
|
|
132
|
+
const guides = computeOpeningGuides({
|
|
133
|
+
moving,
|
|
134
|
+
siblings: [],
|
|
135
|
+
wall: WALL,
|
|
136
|
+
includeVertical: true,
|
|
137
|
+
});
|
|
138
|
+
expect(guides.sillHead?.sill).toBeCloseTo(1);
|
|
139
|
+
expect(guides.sillHead?.head).toBeCloseTo(0.5); // 2.5 - 2
|
|
140
|
+
expect(guides.sillHead?.bottomY).toBeCloseTo(1);
|
|
141
|
+
expect(guides.sillHead?.topY).toBeCloseTo(2);
|
|
142
|
+
});
|
|
143
|
+
test('omits vertical guides for doors (sit on the floor)', () => {
|
|
144
|
+
const moving = span('m', 5, 1, 1, 2);
|
|
145
|
+
const sib = span('s', 8, 1, 1, 2);
|
|
146
|
+
const guides = computeOpeningGuides({
|
|
147
|
+
moving,
|
|
148
|
+
siblings: [sib],
|
|
149
|
+
wall: WALL,
|
|
150
|
+
includeVertical: false,
|
|
151
|
+
});
|
|
152
|
+
expect(guides.sillHead).toBeNull();
|
|
153
|
+
expect(guides.vertical).toBeNull();
|
|
154
|
+
// along-wall + proximity still computed for doors
|
|
155
|
+
expect(guides.gaps.length).toBeGreaterThan(0);
|
|
156
|
+
});
|
|
157
|
+
test('combines proximity and equal-spacing in one pass', () => {
|
|
158
|
+
const moving = span('b', 2.5, 1);
|
|
159
|
+
const guides = computeOpeningGuides({
|
|
160
|
+
moving,
|
|
161
|
+
siblings: [span('a', 0.5, 1), span('c', 4.5, 1)],
|
|
162
|
+
wall: WALL,
|
|
163
|
+
includeVertical: true,
|
|
164
|
+
});
|
|
165
|
+
expect(guides.gaps).toHaveLength(2);
|
|
166
|
+
expect(guides.equalSpacing?.gap).toBeCloseTo(1);
|
|
167
|
+
expect(guides.equalSpacing?.openingIds).toEqual(['a', 'b', 'c']);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
describe('opening-guides — review regressions', () => {
|
|
171
|
+
test('detectEqualSpacing finds a run that starts partway through a drifting sequence', () => {
|
|
172
|
+
// gaps 1.00, 1.02, 1.04 — only [b,c,d] is equal within 0.03 and includes the
|
|
173
|
+
// moving opening; a first-gap-anchored greedy scan used to drop it.
|
|
174
|
+
const openings = [span('a', 0.5, 1), span('b', 2.5, 1), span('c', 4.52, 1), span('d', 6.56, 1)];
|
|
175
|
+
const run = detectEqualSpacing(openings, 'd', 0.03, 0.02);
|
|
176
|
+
expect(run?.openingIds).toEqual(['b', 'c', 'd']);
|
|
177
|
+
expect(run?.gap).toBeCloseTo(1.03);
|
|
178
|
+
expect(run?.segments).toHaveLength(2);
|
|
179
|
+
});
|
|
180
|
+
test('detectEqualSpacing prefers the leftmost run on a length tie', () => {
|
|
181
|
+
// gaps 1,1,2,2 with the moving opening in the middle — two equal-length runs.
|
|
182
|
+
const openings = [
|
|
183
|
+
span('a', 0.5, 1),
|
|
184
|
+
span('b', 2.5, 1),
|
|
185
|
+
span('c', 4.5, 1),
|
|
186
|
+
span('d', 7.5, 1),
|
|
187
|
+
span('e', 10.5, 1),
|
|
188
|
+
];
|
|
189
|
+
expect(detectEqualSpacing(openings, 'c', 0.03, 0.02)?.openingIds).toEqual(['a', 'b', 'c']);
|
|
190
|
+
});
|
|
191
|
+
test('computeEdgeGaps suppresses both sides when a sibling overlaps', () => {
|
|
192
|
+
const moving = span('m', 5, 1); // [4.5,5.5]
|
|
193
|
+
const containing = span('s', 5, 2); // [4,6] straddles both edges
|
|
194
|
+
expect(computeEdgeGaps(moving, [containing], WALL, 0.02)).toEqual([]);
|
|
195
|
+
});
|
|
196
|
+
test('alignment detectors ignore the moving opening if present in siblings', () => {
|
|
197
|
+
const moving = span('m', 5, 2, 1.5, 1);
|
|
198
|
+
expect(detectAlongWallAlignment(moving, [moving], 0.08)).toBeNull();
|
|
199
|
+
expect(detectVerticalAlignment(moving, [moving], 0.08)).toBeNull();
|
|
200
|
+
});
|
|
201
|
+
test('detectAlongWallAlignment reports a negative snap when the feature is past the target', () => {
|
|
202
|
+
const moving = span('m', 5, 2); // centre 5
|
|
203
|
+
const sib = span('s', 4.96, 0.5); // centre 4.96
|
|
204
|
+
const a = detectAlongWallAlignment(moving, [sib], 0.08);
|
|
205
|
+
expect(a?.movingFeature).toBe('center');
|
|
206
|
+
expect(a?.snap).toBeCloseTo(-0.04);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { AnyNode, AnyNodeId } from '../schema';
|
|
2
|
+
/**
|
|
3
|
+
* Connectivity-aware editing for port-bearing distribution kinds
|
|
4
|
+
* (HVAC ductwork AND DWV plumbing).
|
|
5
|
+
*
|
|
6
|
+
* Two nodes are "connected" when a port of one coincides in space with a
|
|
7
|
+
* port of the other — exactly how the placement tools mate a fitting onto
|
|
8
|
+
* a duct end (they snap the fitting's collar onto the run's open port).
|
|
9
|
+
* This service reads that relationship back out so an edit to one node can
|
|
10
|
+
* carry its neighbours along.
|
|
11
|
+
*
|
|
12
|
+
* Pure logic: it asks each node for its ports via `def.ports` (level-local
|
|
13
|
+
* meters) and does arithmetic. No Three.js, no rendering — it lives in
|
|
14
|
+
* core and is consumed by the editor's move tool and the duct/pipe
|
|
15
|
+
* selection affordances alike.
|
|
16
|
+
*
|
|
17
|
+
* ## Propagation model
|
|
18
|
+
*
|
|
19
|
+
* The joint graph is snapshotted once at drag start (`analyzePortConnectivity`)
|
|
20
|
+
* and walked every frame (`resolveConnectivityUpdates`) given the moved node's
|
|
21
|
+
* live transform. Deltas flow outward from the moved node through coincident
|
|
22
|
+
* ports:
|
|
23
|
+
*
|
|
24
|
+
* - **Fitting** (rigid): a collar pushed by delta `d` translates the whole
|
|
25
|
+
* fitting by `d`; every other collar carries that same `d` onward.
|
|
26
|
+
* - **Run** (stretch + slide, never skew): an endpoint pushed by delta `d` is
|
|
27
|
+
* split against the run's own axis. The *parallel* part slides only that
|
|
28
|
+
* endpoint (the run lengthens / shortens); the *perpendicular* part
|
|
29
|
+
* translates the entire run (so its direction is preserved). The far
|
|
30
|
+
* endpoint therefore moves by just the perpendicular part, and that part
|
|
31
|
+
* propagates onward to whatever is mated to the far endpoint.
|
|
32
|
+
*
|
|
33
|
+
* Propagation walks the whole connected component so a joint stays welded all
|
|
34
|
+
* the way down the chain, with a visited guard so cycles (looped runs) and
|
|
35
|
+
* shared joints terminate. First-reached (shortest path) wins on a node
|
|
36
|
+
* reachable two ways.
|
|
37
|
+
*/
|
|
38
|
+
type Point = readonly [number, number, number];
|
|
39
|
+
/** A node carried by the edit, plus the snapshot needed to revert it. Kept
|
|
40
|
+
* deliberately small: the move tools read only `kind` + `nodeId` and the
|
|
41
|
+
* matching start snapshot to revert before the single tracked commit. */
|
|
42
|
+
export type PortConnection = {
|
|
43
|
+
/** A fitting mated collar-to-collar: it translates rigidly. */
|
|
44
|
+
kind: 'rigid-node';
|
|
45
|
+
nodeId: AnyNodeId;
|
|
46
|
+
/** Node's `position` at edit-start. */
|
|
47
|
+
startPosition: Point;
|
|
48
|
+
} | {
|
|
49
|
+
/** A run whose endpoint(s) ride the edit: it stretches and/or
|
|
50
|
+
* translates, never skews. */
|
|
51
|
+
kind: 'run';
|
|
52
|
+
nodeId: AnyNodeId;
|
|
53
|
+
/** The run's full `path` at edit-start. */
|
|
54
|
+
startPath: Point[];
|
|
55
|
+
};
|
|
56
|
+
/** One node in the snapshotted joint graph (everything reachable from the
|
|
57
|
+
* moved node, excluding the moved node itself). */
|
|
58
|
+
type GraphNode = {
|
|
59
|
+
id: AnyNodeId;
|
|
60
|
+
role: 'run' | 'fitting';
|
|
61
|
+
ports: ReadonlyArray<{
|
|
62
|
+
id: string;
|
|
63
|
+
position: Point;
|
|
64
|
+
system?: string;
|
|
65
|
+
}>;
|
|
66
|
+
startPath?: Point[];
|
|
67
|
+
startPosition?: Point;
|
|
68
|
+
};
|
|
69
|
+
/** Who else sits on a given node's port, keyed `nodeId` → `portId` → mates. */
|
|
70
|
+
type Adjacency = Record<string, Record<string, Array<{
|
|
71
|
+
nodeId: AnyNodeId;
|
|
72
|
+
portId: string;
|
|
73
|
+
}>>>;
|
|
74
|
+
export type PortConnectivity = {
|
|
75
|
+
movedNodeId: AnyNodeId;
|
|
76
|
+
/** The moved node's port world positions at edit-start, keyed by port id —
|
|
77
|
+
* the reference each frame's delta is measured from. */
|
|
78
|
+
startMovedPorts: Record<string, Point>;
|
|
79
|
+
/** Reachable run/fitting nodes (excludes the moved node), keyed by id. */
|
|
80
|
+
graph: Record<string, GraphNode>;
|
|
81
|
+
/** Port coincidence edges across the moved node + every graph node. */
|
|
82
|
+
adjacency: Adjacency;
|
|
83
|
+
/** Flat list of carried nodes for the move tools' revert + "anything to
|
|
84
|
+
* follow?" check. Derived from `graph`. */
|
|
85
|
+
connections: PortConnection[];
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Snapshot the joint graph reachable from `movedNode`'s ports, taken at the
|
|
89
|
+
* start of a move/resize. Call once before the drag; feed the result to
|
|
90
|
+
* `resolveConnectivityUpdates` on every frame.
|
|
91
|
+
*
|
|
92
|
+
* Only `run`-role partners (segments) and `fitting`-role partners are walked —
|
|
93
|
+
* terminals and equipment usually mount to a surface and shouldn't be yanked
|
|
94
|
+
* off it when an adjacent fitting nudges. Fittings that declare
|
|
95
|
+
* `portConnectivityFollow: false` are anchored fixtures (e.g. pipe-trap) and
|
|
96
|
+
* are skipped, so a connected run stretches against them instead.
|
|
97
|
+
*/
|
|
98
|
+
export declare function analyzePortConnectivity(movedNode: AnyNode, nodes: Record<string, AnyNode>): PortConnectivity;
|
|
99
|
+
/**
|
|
100
|
+
* Given the moved node in its live (in-drag) transform, produce the patches
|
|
101
|
+
* that keep every connected node attached. `previewNode` is the moved node
|
|
102
|
+
* with its current drag position/rotation applied so its ports recompute.
|
|
103
|
+
*
|
|
104
|
+
* Walks the snapshotted graph, propagating each port delta outward: fittings
|
|
105
|
+
* translate rigidly, runs stretch along their axis and translate across it
|
|
106
|
+
* (never skew when driven from one end), and effective port movement carries on
|
|
107
|
+
* to neighbouring joints. Port-level output guards bound cycles while still
|
|
108
|
+
* allowing a looped/shared run to accept constraints at both endpoints.
|
|
109
|
+
*/
|
|
110
|
+
export declare function resolveConnectivityUpdates(connectivity: PortConnectivity, previewNode: AnyNode): {
|
|
111
|
+
id: AnyNodeId;
|
|
112
|
+
data: Partial<AnyNode>;
|
|
113
|
+
}[];
|
|
114
|
+
export {};
|
|
115
|
+
//# sourceMappingURL=port-connectivity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-connectivity.d.ts","sourceRoot":"","sources":["../../src/services/port-connectivity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,KAAK,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAY9C;;0EAE0E;AAC1E,MAAM,MAAM,cAAc,GACtB;IACE,+DAA+D;IAC/D,IAAI,EAAE,YAAY,CAAA;IAClB,MAAM,EAAE,SAAS,CAAA;IACjB,uCAAuC;IACvC,aAAa,EAAE,KAAK,CAAA;CACrB,GACD;IACE;mCAC+B;IAC/B,IAAI,EAAE,KAAK,CAAA;IACX,MAAM,EAAE,SAAS,CAAA;IACjB,2CAA2C;IAC3C,SAAS,EAAE,KAAK,EAAE,CAAA;CACnB,CAAA;AAEL;oDACoD;AACpD,KAAK,SAAS,GAAG;IACf,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,KAAK,GAAG,SAAS,CAAA;IACvB,KAAK,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,KAAK,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACtE,SAAS,CAAC,EAAE,KAAK,EAAE,CAAA;IACnB,aAAa,CAAC,EAAE,KAAK,CAAA;CACtB,CAAA;AAED,+EAA+E;AAC/E,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAC,CAAA;AAE7F,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,SAAS,CAAA;IACtB;6DACyD;IACzD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IACtC,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IAChC,uEAAuE;IACvE,SAAS,EAAE,SAAS,CAAA;IACpB;gDAC4C;IAC5C,WAAW,EAAE,cAAc,EAAE,CAAA;CAC9B,CAAA;AAkCD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,OAAO,EAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,gBAAgB,CAuFlB;AA4GD;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EAAE,OAAO,GACnB;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,EAAE,CA0F7C"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { nodeRegistry } from '../registry';
|
|
2
|
+
/** Distance (meters) under which two ports count as the same joint. Joints
|
|
3
|
+
* formed by placement snapping coincide to sub-millimeter; 5 cm leaves
|
|
4
|
+
* generous slack for grid-snapped hand placement without false matches. */
|
|
5
|
+
const COINCIDENT_EPS_M = 0.05;
|
|
6
|
+
/** Below this (meters) a propagated delta is treated as zero — stops the
|
|
7
|
+
* walk from chasing sub-millimeter perpendicular residue. */
|
|
8
|
+
const DELTA_EPS_M = 1e-4;
|
|
9
|
+
const PROPAGATION_EPS_M = 1e-9;
|
|
10
|
+
function portsOf(node) {
|
|
11
|
+
return nodeRegistry.get(node.type)?.ports?.(node);
|
|
12
|
+
}
|
|
13
|
+
/** A node's distribution role from the registry (run / fitting / …). */
|
|
14
|
+
function roleOf(node) {
|
|
15
|
+
return nodeRegistry.get(node.type)?.distributionRole;
|
|
16
|
+
}
|
|
17
|
+
function distSq(a, b) {
|
|
18
|
+
const dx = a[0] - b[0];
|
|
19
|
+
const dy = a[1] - b[1];
|
|
20
|
+
const dz = a[2] - b[2];
|
|
21
|
+
return dx * dx + dy * dy + dz * dz;
|
|
22
|
+
}
|
|
23
|
+
/** Two ports mate when they coincide AND don't cross incompatible systems
|
|
24
|
+
* (a supply duct and a waste pipe that merely touch must not fuse). */
|
|
25
|
+
function portsMate(a, b, epsSq) {
|
|
26
|
+
if (distSq(a.position, b.position) > epsSq)
|
|
27
|
+
return false;
|
|
28
|
+
if (a.system && b.system && a.system !== b.system)
|
|
29
|
+
return false;
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Snapshot the joint graph reachable from `movedNode`'s ports, taken at the
|
|
34
|
+
* start of a move/resize. Call once before the drag; feed the result to
|
|
35
|
+
* `resolveConnectivityUpdates` on every frame.
|
|
36
|
+
*
|
|
37
|
+
* Only `run`-role partners (segments) and `fitting`-role partners are walked —
|
|
38
|
+
* terminals and equipment usually mount to a surface and shouldn't be yanked
|
|
39
|
+
* off it when an adjacent fitting nudges. Fittings that declare
|
|
40
|
+
* `portConnectivityFollow: false` are anchored fixtures (e.g. pipe-trap) and
|
|
41
|
+
* are skipped, so a connected run stretches against them instead.
|
|
42
|
+
*/
|
|
43
|
+
export function analyzePortConnectivity(movedNode, nodes) {
|
|
44
|
+
const epsSq = COINCIDENT_EPS_M * COINCIDENT_EPS_M;
|
|
45
|
+
const movedPorts = portsOf(movedNode) ?? [];
|
|
46
|
+
const startMovedPorts = {};
|
|
47
|
+
for (const p of movedPorts)
|
|
48
|
+
startMovedPorts[p.id] = p.position;
|
|
49
|
+
// Candidate partners: every run + every following fitting in the scene.
|
|
50
|
+
const candidates = [];
|
|
51
|
+
for (const other of Object.values(nodes)) {
|
|
52
|
+
if (!other || other.id === movedNode.id)
|
|
53
|
+
continue;
|
|
54
|
+
const role = roleOf(other);
|
|
55
|
+
if (role !== 'run' && role !== 'fitting')
|
|
56
|
+
continue;
|
|
57
|
+
if (role === 'fitting' && nodeRegistry.get(other.type)?.portConnectivityFollow === false) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const ports = portsOf(other);
|
|
61
|
+
if (!ports)
|
|
62
|
+
continue;
|
|
63
|
+
const startPath = role === 'run'
|
|
64
|
+
? other.path?.map((p) => [...p])
|
|
65
|
+
: undefined;
|
|
66
|
+
if (role === 'run' && (!startPath || startPath.length < 2))
|
|
67
|
+
continue;
|
|
68
|
+
const startPosition = role === 'fitting'
|
|
69
|
+
? (() => {
|
|
70
|
+
const pos = other.position;
|
|
71
|
+
return pos ? [pos[0], pos[1], pos[2]] : undefined;
|
|
72
|
+
})()
|
|
73
|
+
: undefined;
|
|
74
|
+
if (role === 'fitting' && !startPosition)
|
|
75
|
+
continue;
|
|
76
|
+
candidates.push({ id: other.id, role, ports, startPath, startPosition });
|
|
77
|
+
}
|
|
78
|
+
// Walk outward from the moved node, collecting every node reachable through
|
|
79
|
+
// coincident ports. The adjacency records each port's mates so the resolver
|
|
80
|
+
// can replay the same edges with live deltas.
|
|
81
|
+
const adjacency = {};
|
|
82
|
+
const addEdge = (nodeId, portId, mate) => {
|
|
83
|
+
const byPort = adjacency[nodeId] ?? {};
|
|
84
|
+
adjacency[nodeId] = byPort;
|
|
85
|
+
const mates = byPort[portId] ?? [];
|
|
86
|
+
byPort[portId] = mates;
|
|
87
|
+
mates.push(mate);
|
|
88
|
+
};
|
|
89
|
+
const graph = {};
|
|
90
|
+
const visited = new Set([movedNode.id]);
|
|
91
|
+
// Seed: the moved node's own ports.
|
|
92
|
+
const queue = [{ id: movedNode.id, ports: movedPorts }];
|
|
93
|
+
while (queue.length > 0) {
|
|
94
|
+
const { id, ports } = queue.shift();
|
|
95
|
+
for (const port of ports) {
|
|
96
|
+
for (const cand of candidates) {
|
|
97
|
+
if (cand.id === id)
|
|
98
|
+
continue;
|
|
99
|
+
for (const cp of cand.ports) {
|
|
100
|
+
if (!portsMate(port, cp, epsSq))
|
|
101
|
+
continue;
|
|
102
|
+
addEdge(id, port.id, { nodeId: cand.id, portId: cp.id });
|
|
103
|
+
addEdge(cand.id, cp.id, { nodeId: id, portId: port.id });
|
|
104
|
+
if (!visited.has(cand.id)) {
|
|
105
|
+
visited.add(cand.id);
|
|
106
|
+
graph[cand.id] = cand;
|
|
107
|
+
queue.push({ id: cand.id, ports: cand.ports });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const connections = Object.values(graph).map((g) => g.role === 'fitting'
|
|
114
|
+
? { kind: 'rigid-node', nodeId: g.id, startPosition: g.startPosition }
|
|
115
|
+
: { kind: 'run', nodeId: g.id, startPath: g.startPath });
|
|
116
|
+
return {
|
|
117
|
+
movedNodeId: movedNode.id,
|
|
118
|
+
startMovedPorts,
|
|
119
|
+
graph,
|
|
120
|
+
adjacency,
|
|
121
|
+
connections,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function add(a, b) {
|
|
125
|
+
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
126
|
+
}
|
|
127
|
+
function sub(a, b) {
|
|
128
|
+
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
129
|
+
}
|
|
130
|
+
function lenSq(v) {
|
|
131
|
+
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
|
132
|
+
}
|
|
133
|
+
/** Split `delta` into the component along unit `axis` and the remainder. */
|
|
134
|
+
function decompose(delta, axis) {
|
|
135
|
+
const dot = delta[0] * axis[0] + delta[1] * axis[1] + delta[2] * axis[2];
|
|
136
|
+
const parallel = [axis[0] * dot, axis[1] * dot, axis[2] * dot];
|
|
137
|
+
return { parallel, perp: sub(delta, parallel) };
|
|
138
|
+
}
|
|
139
|
+
function scale(v, scalar) {
|
|
140
|
+
return [v[0] * scalar, v[1] * scalar, v[2] * scalar];
|
|
141
|
+
}
|
|
142
|
+
function average(deltas) {
|
|
143
|
+
const sum = deltas.reduce((acc, delta) => add(acc, delta), [0, 0, 0]);
|
|
144
|
+
return scale(sum, 1 / deltas.length);
|
|
145
|
+
}
|
|
146
|
+
function nearlyEqual(a, b) {
|
|
147
|
+
return lenSq(sub(a, b)) <= DELTA_EPS_M * DELTA_EPS_M;
|
|
148
|
+
}
|
|
149
|
+
function propagationEqual(a, b) {
|
|
150
|
+
return lenSq(sub(a, b)) <= PROPAGATION_EPS_M * PROPAGATION_EPS_M;
|
|
151
|
+
}
|
|
152
|
+
function effectivePortDeltas(constraints) {
|
|
153
|
+
return Object.fromEntries(Object.entries(constraints).map(([portId, bySource]) => [
|
|
154
|
+
portId,
|
|
155
|
+
average(Object.values(bySource)),
|
|
156
|
+
]));
|
|
157
|
+
}
|
|
158
|
+
/** Unit direction of the run's segment adjacent to its `start` / `end` tip. */
|
|
159
|
+
function endpointAxis(path, portId) {
|
|
160
|
+
const n = path.length;
|
|
161
|
+
const [a, b] = portId === 'start' ? [path[1], path[0]] : [path[n - 2], path[n - 1]];
|
|
162
|
+
const dir = sub(b, a);
|
|
163
|
+
const l2 = lenSq(dir);
|
|
164
|
+
if (l2 < 1e-12)
|
|
165
|
+
return [0, 0, 0];
|
|
166
|
+
const l = Math.sqrt(l2);
|
|
167
|
+
return [dir[0] / l, dir[1] / l, dir[2] / l];
|
|
168
|
+
}
|
|
169
|
+
function runPathFromSinglePortDelta(startPath, portId, delta) {
|
|
170
|
+
const nearIdx = portId === 'start' ? 0 : startPath.length - 1;
|
|
171
|
+
const axis = endpointAxis(startPath, portId);
|
|
172
|
+
const { parallel, perp } = decompose(delta, axis);
|
|
173
|
+
const path = startPath.map((p) => add(p, perp));
|
|
174
|
+
path[nearIdx] = add(path[nearIdx], parallel);
|
|
175
|
+
return path;
|
|
176
|
+
}
|
|
177
|
+
function runEndpointDeltas(startPath, path) {
|
|
178
|
+
return {
|
|
179
|
+
start: sub(path[0], startPath[0]),
|
|
180
|
+
end: sub(path[path.length - 1], startPath[startPath.length - 1]),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function runPathFromPortDeltas(startPath, portDeltas) {
|
|
184
|
+
const startDelta = portDeltas.start;
|
|
185
|
+
const endDelta = portDeltas.end;
|
|
186
|
+
if (startDelta && endDelta) {
|
|
187
|
+
if (startPath.length === 2) {
|
|
188
|
+
return [add(startPath[0], startDelta), add(startPath[1], endDelta)];
|
|
189
|
+
}
|
|
190
|
+
if (nearlyEqual(startDelta, endDelta)) {
|
|
191
|
+
return startPath.map((p) => add(p, startDelta));
|
|
192
|
+
}
|
|
193
|
+
const startParts = decompose(startDelta, endpointAxis(startPath, 'start'));
|
|
194
|
+
const endParts = decompose(endDelta, endpointAxis(startPath, 'end'));
|
|
195
|
+
const commonPerp = average([startParts.perp, endParts.perp]);
|
|
196
|
+
const path = startPath.map((p) => add(p, commonPerp));
|
|
197
|
+
path[0] = add(path[0], startParts.parallel);
|
|
198
|
+
path[path.length - 1] = add(path[path.length - 1], endParts.parallel);
|
|
199
|
+
return path;
|
|
200
|
+
}
|
|
201
|
+
return runPathFromSinglePortDelta(startPath, startDelta ? 'start' : 'end', (startDelta ?? endDelta));
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Given the moved node in its live (in-drag) transform, produce the patches
|
|
205
|
+
* that keep every connected node attached. `previewNode` is the moved node
|
|
206
|
+
* with its current drag position/rotation applied so its ports recompute.
|
|
207
|
+
*
|
|
208
|
+
* Walks the snapshotted graph, propagating each port delta outward: fittings
|
|
209
|
+
* translate rigidly, runs stretch along their axis and translate across it
|
|
210
|
+
* (never skew when driven from one end), and effective port movement carries on
|
|
211
|
+
* to neighbouring joints. Port-level output guards bound cycles while still
|
|
212
|
+
* allowing a looped/shared run to accept constraints at both endpoints.
|
|
213
|
+
*/
|
|
214
|
+
export function resolveConnectivityUpdates(connectivity, previewNode) {
|
|
215
|
+
const { graph, adjacency, startMovedPorts, movedNodeId } = connectivity;
|
|
216
|
+
if (Object.keys(graph).length === 0)
|
|
217
|
+
return [];
|
|
218
|
+
const newPorts = portsOf(previewNode) ?? [];
|
|
219
|
+
const newMovedPos = {};
|
|
220
|
+
for (const p of newPorts)
|
|
221
|
+
newMovedPos[p.id] = p.position;
|
|
222
|
+
// Each queue item drives a node's port by a delta ("this collar / endpoint
|
|
223
|
+
// must move by this much").
|
|
224
|
+
const queue = [];
|
|
225
|
+
const results = {};
|
|
226
|
+
const constrainedPorts = {};
|
|
227
|
+
const propagatedPorts = {};
|
|
228
|
+
const enqueueMates = (nodeId, portId, delta) => {
|
|
229
|
+
const byPort = propagatedPorts[nodeId] ?? {};
|
|
230
|
+
propagatedPorts[nodeId] = byPort;
|
|
231
|
+
const previous = byPort[portId];
|
|
232
|
+
if (previous && propagationEqual(previous, delta))
|
|
233
|
+
return;
|
|
234
|
+
byPort[portId] = delta;
|
|
235
|
+
for (const mate of adjacency[nodeId]?.[portId] ?? []) {
|
|
236
|
+
if (mate.nodeId === movedNodeId)
|
|
237
|
+
continue;
|
|
238
|
+
queue.push({
|
|
239
|
+
nodeId: mate.nodeId,
|
|
240
|
+
portId: mate.portId,
|
|
241
|
+
delta,
|
|
242
|
+
sourceKey: `${nodeId}:${portId}`,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const acceptPortDelta = (nodeId, portId, sourceKey, delta) => {
|
|
247
|
+
const byPort = constrainedPorts[nodeId] ?? {};
|
|
248
|
+
constrainedPorts[nodeId] = byPort;
|
|
249
|
+
const bySource = byPort[portId] ?? {};
|
|
250
|
+
byPort[portId] = bySource;
|
|
251
|
+
const existing = bySource[sourceKey];
|
|
252
|
+
if (existing && propagationEqual(existing, delta)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
bySource[sourceKey] = delta;
|
|
256
|
+
return true;
|
|
257
|
+
};
|
|
258
|
+
// Seed from the moved node's live port deltas.
|
|
259
|
+
for (const [portId, start] of Object.entries(startMovedPorts)) {
|
|
260
|
+
const now = newMovedPos[portId];
|
|
261
|
+
if (!now)
|
|
262
|
+
continue;
|
|
263
|
+
const delta = sub(now, start);
|
|
264
|
+
if (lenSq(delta) <= DELTA_EPS_M * DELTA_EPS_M)
|
|
265
|
+
continue;
|
|
266
|
+
enqueueMates(movedNodeId, portId, delta);
|
|
267
|
+
}
|
|
268
|
+
while (queue.length > 0) {
|
|
269
|
+
const { nodeId, portId, delta, sourceKey } = queue.shift();
|
|
270
|
+
const node = graph[nodeId];
|
|
271
|
+
if (!node)
|
|
272
|
+
continue;
|
|
273
|
+
if (!acceptPortDelta(nodeId, portId, sourceKey, delta))
|
|
274
|
+
continue;
|
|
275
|
+
const portDeltas = effectivePortDeltas(constrainedPorts[nodeId]);
|
|
276
|
+
if (node.role === 'fitting') {
|
|
277
|
+
const start = node.startPosition;
|
|
278
|
+
const effectiveDelta = average(Object.values(portDeltas));
|
|
279
|
+
results[nodeId] = {
|
|
280
|
+
id: nodeId,
|
|
281
|
+
data: { position: add(start, effectiveDelta) },
|
|
282
|
+
};
|
|
283
|
+
// Rigid: every collar carries the effective body translation onward.
|
|
284
|
+
for (const p of node.ports) {
|
|
285
|
+
enqueueMates(nodeId, p.id, effectiveDelta);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
const startPath = node.startPath;
|
|
290
|
+
const path = runPathFromPortDeltas(startPath, portDeltas);
|
|
291
|
+
results[nodeId] = { id: nodeId, data: { path } };
|
|
292
|
+
for (const [nextPortId, nextDelta] of Object.entries(runEndpointDeltas(startPath, path))) {
|
|
293
|
+
if (lenSq(nextDelta) <= DELTA_EPS_M * DELTA_EPS_M)
|
|
294
|
+
continue;
|
|
295
|
+
enqueueMates(nodeId, nextPortId, nextDelta);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return Object.values(results);
|
|
300
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-connectivity.test.d.ts","sourceRoot":"","sources":["../../src/services/port-connectivity.test.ts"],"names":[],"mappings":""}
|