@pascal-app/core 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/README.md +23 -0
- package/dist/capture/index.d.ts +3 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +2 -0
- package/dist/capture/schema.d.ts +417 -0
- package/dist/capture/schema.d.ts.map +1 -0
- package/dist/capture/schema.js +350 -0
- package/dist/capture/source.d.ts +60 -0
- package/dist/capture/source.d.ts.map +1 -0
- package/dist/capture/source.js +254 -0
- package/dist/events/bus.d.ts +58 -7
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/hidden-wall-pointer-hold.d.ts +28 -0
- package/dist/events/hidden-wall-pointer-hold.d.ts.map +1 -0
- package/dist/events/hidden-wall-pointer-hold.js +37 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +4 -4
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +7 -12
- package/dist/hooks/spatial-grid/floor-placed-footprints.d.ts +4 -0
- package/dist/hooks/spatial-grid/floor-placed-footprints.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-footprints.js +7 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +5 -8
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +23 -106
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +2931 -2
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +207 -30
- package/dist/hooks/spatial-grid/support-host-patch.d.ts +45 -1
- package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/support-host-patch.js +174 -9
- package/dist/hooks/spatial-grid/use-spatial-query.d.ts +1 -1
- package/dist/hooks/spatial-grid/use-spatial-query.js +1 -1
- package/dist/index.d.ts +74 -64
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +74 -61
- package/dist/lib/asset-storage.d.ts.map +1 -1
- package/dist/lib/asset-storage.js +4 -1
- package/dist/lib/door-operation.d.ts +1 -1
- package/dist/lib/item-polygon-overlap.d.ts +6 -0
- package/dist/lib/item-polygon-overlap.d.ts.map +1 -0
- package/dist/lib/item-polygon-overlap.js +67 -0
- package/dist/lib/level-name.d.ts +1 -1
- package/dist/lib/measurement-geometry.d.ts +4 -4
- package/dist/lib/plan-footprint.d.ts +41 -0
- package/dist/lib/plan-footprint.d.ts.map +1 -0
- package/dist/lib/plan-footprint.js +58 -0
- package/dist/lib/polygon-union.d.ts +4 -0
- package/dist/lib/polygon-union.d.ts.map +1 -0
- package/dist/lib/polygon-union.js +306 -0
- package/dist/lib/roof-overlap.d.ts +32 -0
- package/dist/lib/roof-overlap.d.ts.map +1 -0
- package/dist/lib/roof-overlap.js +72 -0
- package/dist/lib/room-topology-index.d.ts +1 -1
- package/dist/lib/room-topology-index.js +1 -1
- package/dist/lib/selection-proxy.d.ts +1 -1
- package/dist/lib/slab-polygon.d.ts +36 -3
- package/dist/lib/slab-polygon.d.ts.map +1 -1
- package/dist/lib/slab-polygon.js +135 -29
- package/dist/lib/space-detection.d.ts +12 -1
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +58 -25
- package/dist/lib/terrain-brush.d.ts +1 -1
- package/dist/lib/terrain-brush.js +1 -1
- package/dist/lib/terrain-codec.d.ts +2 -2
- package/dist/lib/terrain-codec.js +1 -1
- package/dist/lib/terrain-raycast.d.ts +1 -1
- package/dist/lib/terrain-raycast.js +1 -1
- package/dist/lib/terrain-source.d.ts +3 -3
- package/dist/lib/terrain-source.js +3 -3
- package/dist/lib/terrain-support.d.ts +1 -1
- package/dist/lib/terrain-support.js +3 -3
- package/dist/lib/unit-containment.d.ts +19 -0
- package/dist/lib/unit-containment.d.ts.map +1 -0
- package/dist/lib/unit-containment.js +158 -0
- package/dist/lib/unit-report.d.ts +19 -0
- package/dist/lib/unit-report.d.ts.map +1 -0
- package/dist/lib/unit-report.js +36 -0
- package/dist/lib/wall-distance.d.ts +2 -2
- package/dist/lib/wall-distance.js +1 -1
- package/dist/lib/zone-quantities.d.ts +1 -1
- package/dist/lib/zone-quantities.js +7 -7
- package/dist/material-library.d.ts +1 -1
- package/dist/material-library.js +1 -1
- package/dist/procedural-items/fixtures.d.ts +261 -0
- package/dist/procedural-items/fixtures.d.ts.map +1 -0
- package/dist/procedural-items/fixtures.js +349 -0
- package/dist/procedural-items/index.d.ts +10 -0
- package/dist/procedural-items/index.d.ts.map +1 -0
- package/dist/procedural-items/index.js +9 -0
- package/dist/procedural-items/integration.d.ts +92 -0
- package/dist/procedural-items/integration.d.ts.map +1 -0
- package/dist/procedural-items/integration.js +19 -0
- package/dist/procedural-items/library-colors.d.ts +56 -0
- package/dist/procedural-items/library-colors.d.ts.map +1 -0
- package/dist/procedural-items/library-colors.js +84 -0
- package/dist/procedural-items/materials.d.ts +5 -0
- package/dist/procedural-items/materials.d.ts.map +1 -0
- package/dist/procedural-items/materials.js +47 -0
- package/dist/procedural-items/node.d.ts +280 -0
- package/dist/procedural-items/node.d.ts.map +1 -0
- package/dist/procedural-items/node.js +71 -0
- package/dist/procedural-items/placement.d.ts +7 -0
- package/dist/procedural-items/placement.d.ts.map +1 -0
- package/dist/procedural-items/placement.js +48 -0
- package/dist/procedural-items/query.d.ts +50 -0
- package/dist/procedural-items/query.d.ts.map +1 -0
- package/dist/procedural-items/query.js +298 -0
- package/dist/procedural-items/recipe.d.ts +144 -0
- package/dist/procedural-items/recipe.d.ts.map +1 -0
- package/dist/procedural-items/recipe.js +372 -0
- package/dist/procedural-items/spatial.d.ts +19 -0
- package/dist/procedural-items/spatial.d.ts.map +1 -0
- package/dist/procedural-items/spatial.js +39 -0
- package/dist/registry/__bench__/relations-resolver.bench.js +2 -2
- package/dist/registry/handles.d.ts +36 -7
- package/dist/registry/handles.d.ts.map +1 -1
- package/dist/registry/index.d.ts +6 -7
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +4 -5
- package/dist/registry/registry.d.ts +13 -6
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +37 -4
- package/dist/registry/relations-resolver.d.ts +2 -2
- package/dist/registry/relations-resolver.js +1 -1
- package/dist/registry/scene-api.d.ts +18 -2
- package/dist/registry/scene-api.d.ts.map +1 -1
- package/dist/registry/scene-api.js +50 -2
- package/dist/registry/subtree.d.ts +1 -1
- package/dist/registry/subtree.js +2 -2
- package/dist/registry/types.d.ts +246 -21
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry/use-registry-version.js +1 -1
- package/dist/registry/validate-relations.d.ts +4 -0
- package/dist/registry/validate-relations.d.ts.map +1 -0
- package/dist/registry/validate-relations.js +22 -0
- package/dist/schema/__bench__/node-parsers.bench.d.ts +24 -0
- package/dist/schema/__bench__/node-parsers.bench.d.ts.map +1 -0
- package/dist/schema/__bench__/node-parsers.bench.js +314 -0
- package/dist/schema/__fixtures__/node-fixtures.d.ts +28 -0
- package/dist/schema/__fixtures__/node-fixtures.d.ts.map +1 -0
- package/dist/schema/__fixtures__/node-fixtures.js +127 -0
- package/dist/schema/base.d.ts +1 -1
- package/dist/schema/base.d.ts.map +1 -1
- package/dist/schema/base.js +9 -2
- package/dist/schema/collections.d.ts +1 -1
- package/dist/schema/collections.js +1 -1
- package/dist/schema/compiled-node-parsers.d.ts +40 -0
- package/dist/schema/compiled-node-parsers.d.ts.map +1 -0
- package/dist/schema/compiled-node-parsers.js +95 -0
- package/dist/schema/index.d.ts +73 -66
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +65 -57
- package/dist/schema/material.d.ts +1 -0
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +2 -1
- package/dist/schema/nodes/automatic-downspout.d.ts +17 -0
- package/dist/schema/nodes/automatic-downspout.d.ts.map +1 -0
- package/dist/schema/nodes/automatic-downspout.js +237 -0
- package/dist/schema/nodes/block.d.ts +107 -0
- package/dist/schema/nodes/block.d.ts.map +1 -0
- package/dist/schema/nodes/block.js +206 -0
- package/dist/schema/nodes/box-vent.d.ts +8 -2
- package/dist/schema/nodes/box-vent.d.ts.map +1 -1
- package/dist/schema/nodes/box-vent.js +4 -2
- package/dist/schema/nodes/building.d.ts +2 -2
- package/dist/schema/nodes/building.d.ts.map +1 -1
- package/dist/schema/nodes/building.js +8 -5
- package/dist/schema/nodes/cabinet.d.ts +26 -4
- package/dist/schema/nodes/cabinet.d.ts.map +1 -1
- package/dist/schema/nodes/cabinet.js +29 -8
- package/dist/schema/nodes/ceiling.d.ts +2 -2
- package/dist/schema/nodes/ceiling.js +5 -5
- package/dist/schema/nodes/chimney.d.ts +2 -2
- package/dist/schema/nodes/chimney.js +2 -2
- package/dist/schema/nodes/column.d.ts +7 -6
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +3 -2
- package/dist/schema/nodes/construction-dimension.d.ts +1 -1
- package/dist/schema/nodes/construction-dimension.js +2 -2
- package/dist/schema/nodes/cupola.d.ts +9 -1
- package/dist/schema/nodes/cupola.d.ts.map +1 -1
- package/dist/schema/nodes/cupola.js +4 -2
- package/dist/schema/nodes/door.d.ts +1 -1
- package/dist/schema/nodes/door.js +2 -2
- package/dist/schema/nodes/dormer.d.ts +45 -2
- package/dist/schema/nodes/dormer.d.ts.map +1 -1
- package/dist/schema/nodes/dormer.js +133 -9
- package/dist/schema/nodes/downspout.d.ts +11 -1
- package/dist/schema/nodes/downspout.d.ts.map +1 -1
- package/dist/schema/nodes/downspout.js +26 -2
- package/dist/schema/nodes/duct-fitting.d.ts +18 -1
- package/dist/schema/nodes/duct-fitting.d.ts.map +1 -1
- package/dist/schema/nodes/duct-fitting.js +26 -5
- package/dist/schema/nodes/duct-segment.d.ts +23 -1
- package/dist/schema/nodes/duct-segment.d.ts.map +1 -1
- package/dist/schema/nodes/duct-segment.js +21 -1
- package/dist/schema/nodes/duct-terminal.d.ts +1 -1
- package/dist/schema/nodes/duct-terminal.js +1 -1
- package/dist/schema/nodes/elevator.d.ts +1 -1
- package/dist/schema/nodes/elevator.js +2 -2
- package/dist/schema/nodes/eyebrow-vent.d.ts +8 -2
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -1
- package/dist/schema/nodes/eyebrow-vent.js +4 -2
- package/dist/schema/nodes/fence.d.ts +1 -1
- package/dist/schema/nodes/fence.js +2 -2
- package/dist/schema/nodes/guide.d.ts +1 -1
- package/dist/schema/nodes/guide.js +2 -2
- package/dist/schema/nodes/gutter.d.ts +36 -2
- package/dist/schema/nodes/gutter.d.ts.map +1 -1
- package/dist/schema/nodes/gutter.js +310 -3
- package/dist/schema/nodes/hanger-overrides.d.ts +7 -0
- package/dist/schema/nodes/hanger-overrides.d.ts.map +1 -0
- package/dist/schema/nodes/hanger-overrides.js +6 -0
- package/dist/schema/nodes/hvac-equipment.d.ts +1 -1
- package/dist/schema/nodes/hvac-equipment.js +1 -1
- package/dist/schema/nodes/imported-mesh.d.ts +40 -0
- package/dist/schema/nodes/imported-mesh.d.ts.map +1 -0
- package/dist/schema/nodes/imported-mesh.js +27 -0
- package/dist/schema/nodes/item.d.ts +3 -2
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +8 -3
- package/dist/schema/nodes/lean-to-extension.d.ts +212 -0
- package/dist/schema/nodes/lean-to-extension.d.ts.map +1 -0
- package/dist/schema/nodes/lean-to-extension.js +114 -0
- package/dist/schema/nodes/level.d.ts +2 -2
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +1 -1
- package/dist/schema/nodes/lineset.d.ts +1 -1
- package/dist/schema/nodes/lineset.js +1 -1
- package/dist/schema/nodes/liquid-line.d.ts +1 -1
- package/dist/schema/nodes/liquid-line.js +1 -1
- package/dist/schema/nodes/measurement.d.ts +1 -1
- package/dist/schema/nodes/measurement.js +2 -2
- package/dist/schema/nodes/pipe-fitting.d.ts +10 -2
- package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -1
- package/dist/schema/nodes/pipe-fitting.js +7 -3
- package/dist/schema/nodes/pipe-segment.d.ts +24 -2
- package/dist/schema/nodes/pipe-segment.d.ts.map +1 -1
- package/dist/schema/nodes/pipe-segment.js +21 -1
- package/dist/schema/nodes/pipe-trap.d.ts +2 -2
- package/dist/schema/nodes/pipe-trap.js +1 -1
- package/dist/schema/nodes/ridge-vent.d.ts +2 -2
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -1
- package/dist/schema/nodes/ridge-vent.js +7 -4
- package/dist/schema/nodes/roof-segment-shape.d.ts +5 -1
- package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-shape.js +65 -2
- package/dist/schema/nodes/roof-segment-walls.d.ts +4 -2
- package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-walls.js +2 -2
- package/dist/schema/nodes/roof-segment.d.ts +42 -2
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +81 -4
- package/dist/schema/nodes/roof.d.ts +23 -2
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +17 -3
- package/dist/schema/nodes/scan.d.ts +19 -2
- package/dist/schema/nodes/scan.d.ts.map +1 -1
- package/dist/schema/nodes/scan.js +13 -3
- package/dist/schema/nodes/shelf.d.ts +3 -3
- package/dist/schema/nodes/shelf.d.ts.map +1 -1
- package/dist/schema/nodes/shelf.js +3 -4
- package/dist/schema/nodes/site.d.ts +1 -1
- package/dist/schema/nodes/site.js +2 -2
- package/dist/schema/nodes/skylight.d.ts +5 -5
- package/dist/schema/nodes/skylight.js +2 -2
- package/dist/schema/nodes/slab.d.ts +1 -1
- package/dist/schema/nodes/slab.js +3 -3
- package/dist/schema/nodes/solar-panel.d.ts +1 -1
- package/dist/schema/nodes/solar-panel.js +3 -3
- package/dist/schema/nodes/spawn.d.ts +1 -1
- package/dist/schema/nodes/spawn.js +1 -1
- package/dist/schema/nodes/stair-segment.d.ts +1 -1
- package/dist/schema/nodes/stair-segment.js +2 -2
- package/dist/schema/nodes/stair.d.ts +2 -2
- package/dist/schema/nodes/stair.js +3 -3
- package/dist/schema/nodes/structural-grid.d.ts +1 -1
- package/dist/schema/nodes/structural-grid.js +1 -1
- package/dist/schema/nodes/turbine-vent.d.ts +8 -2
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -1
- package/dist/schema/nodes/turbine-vent.js +4 -2
- package/dist/schema/nodes/unit.d.ts +33 -0
- package/dist/schema/nodes/unit.d.ts.map +1 -0
- package/dist/schema/nodes/unit.js +13 -0
- package/dist/schema/nodes/wall.d.ts +5 -5
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +13 -6
- package/dist/schema/nodes/window.d.ts +10 -3
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +6 -2
- package/dist/schema/nodes/zone.d.ts +2 -2
- package/dist/schema/nodes/zone.js +2 -2
- package/dist/schema/scene-material.js +2 -2
- package/dist/schema/types.d.ts +815 -118
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +79 -46
- package/dist/services/alignment-anchors.d.ts +6 -11
- package/dist/services/alignment-anchors.d.ts.map +1 -1
- package/dist/services/alignment-anchors.js +7 -34
- package/dist/services/drag-session.d.ts +4 -4
- package/dist/services/drag-session.js +1 -1
- package/dist/services/hosting.d.ts +4 -10
- package/dist/services/hosting.d.ts.map +1 -1
- package/dist/services/hosting.js +6 -16
- package/dist/services/index.d.ts +16 -13
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +16 -13
- package/dist/services/level-height.d.ts +2 -2
- package/dist/services/level-height.js +3 -3
- package/dist/services/movement.d.ts +3 -3
- package/dist/services/movement.js +2 -2
- package/dist/services/port-connectivity.d.ts +1 -1
- package/dist/services/port-connectivity.js +1 -1
- package/dist/services/riser-diagram.d.ts +1 -1
- package/dist/services/shelf-board.d.ts +5 -0
- package/dist/services/shelf-board.d.ts.map +1 -0
- package/dist/services/shelf-board.js +15 -0
- package/dist/services/storey.d.ts +2 -2
- package/dist/services/storey.js +2 -2
- package/dist/services/surface-hosting-procedural-parity.fixtures.d.ts +13 -0
- package/dist/services/surface-hosting-procedural-parity.fixtures.d.ts.map +1 -0
- package/dist/services/surface-hosting-procedural-parity.fixtures.js +4691 -0
- package/dist/services/surface-hosting-shelf-parity.fixtures.d.ts +1060 -0
- package/dist/services/surface-hosting-shelf-parity.fixtures.d.ts.map +1 -0
- package/dist/services/surface-hosting-shelf-parity.fixtures.js +3127 -0
- package/dist/services/surface-hosting.d.ts +88 -0
- package/dist/services/surface-hosting.d.ts.map +1 -0
- package/dist/services/surface-hosting.js +268 -0
- package/dist/services/surface-region.d.ts +8 -0
- package/dist/services/surface-region.d.ts.map +1 -0
- package/dist/services/surface-region.js +44 -0
- package/dist/services/system-graph.d.ts +12 -1
- package/dist/services/system-graph.d.ts.map +1 -1
- package/dist/services/system-graph.js +36 -6
- package/dist/services/validate-dwv.d.ts +1 -1
- package/dist/services/validate-dwv.js +1 -1
- package/dist/store/actions/node-actions.d.ts +9 -2
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +475 -24
- package/dist/store/history-control.d.ts +3 -3
- package/dist/store/history-invalidation.d.ts +3 -0
- package/dist/store/history-invalidation.d.ts.map +1 -0
- package/dist/store/history-invalidation.js +72 -0
- package/dist/store/scene-hydration.d.ts +5 -0
- package/dist/store/scene-hydration.d.ts.map +1 -0
- package/dist/store/scene-hydration.js +57 -0
- package/dist/store/use-interactive.d.ts +2 -2
- package/dist/store/use-live-terrain.d.ts +1 -1
- package/dist/store/use-scene.d.ts +7 -4
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +361 -87
- package/dist/systems/elevator/elevator-dispatch.d.ts +2 -2
- package/dist/systems/elevator/elevator-dispatch.js +1 -1
- package/dist/systems/elevator/elevator-geometry.d.ts +1 -1
- package/dist/systems/elevator/elevator-opening-sync.d.ts +1 -1
- package/dist/systems/elevator/elevator-opening-sync.js +2 -2
- package/dist/systems/elevator/elevator-opening-system.js +3 -3
- package/dist/systems/elevator/elevator-runtime.d.ts +3 -3
- package/dist/systems/elevator/elevator-runtime.js +3 -3
- package/dist/systems/elevator/elevator-service.d.ts +1 -1
- package/dist/systems/elevator/elevator-service.js +1 -1
- package/dist/systems/fence/fence-centerline.d.ts +2 -2
- package/dist/systems/fence/fence-centerline.js +2 -2
- package/dist/systems/fence/fence-spline.d.ts +2 -2
- package/dist/systems/roof/roof-elevation-system.d.ts +3 -0
- package/dist/systems/roof/roof-elevation-system.d.ts.map +1 -0
- package/dist/systems/roof/roof-elevation-system.js +94 -0
- package/dist/systems/roof/roof-elevation.d.ts +4 -0
- package/dist/systems/roof/roof-elevation.d.ts.map +1 -0
- package/dist/systems/roof/roof-elevation.js +95 -0
- package/dist/systems/roof/roof-footprint.d.ts +17 -0
- package/dist/systems/roof/roof-footprint.d.ts.map +1 -0
- package/dist/systems/roof/roof-footprint.js +83 -0
- package/dist/systems/slab/slab-placement.d.ts +1 -1
- package/dist/systems/slab/slab-support.d.ts +1 -1
- package/dist/systems/slab/slab-support.js +4 -4
- package/dist/systems/stair/stair-flight.d.ts +17 -0
- package/dist/systems/stair/stair-flight.d.ts.map +1 -0
- package/dist/systems/stair/stair-flight.js +27 -0
- package/dist/systems/stair/stair-footprint.d.ts +1 -1
- package/dist/systems/stair/stair-opening-preview.d.ts +1621 -1267
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-preview.js +2 -2
- package/dist/systems/stair/stair-opening-sync.d.ts +1 -1
- package/dist/systems/stair/stair-opening-sync.js +6 -6
- package/dist/systems/stair/stair-opening-system.d.ts +1 -0
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-system.js +100 -103
- package/dist/systems/stair/stair-rise.d.ts +1 -1
- package/dist/systems/stair/stair-rise.d.ts.map +1 -1
- package/dist/systems/stair/stair-rise.js +23 -24
- package/dist/systems/wall/wall-curve.d.ts +3 -2
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +73 -0
- package/dist/systems/wall/wall-footprint.d.ts +3 -2
- package/dist/systems/wall/wall-footprint.d.ts.map +1 -1
- package/dist/systems/wall/wall-footprint.js +3 -2
- package/dist/systems/wall/wall-mitering.d.ts +1 -1
- package/dist/systems/wall/wall-mitering.js +1 -1
- package/dist/systems/wall/wall-move.d.ts +1 -1
- package/dist/systems/wall/wall-top.d.ts +1 -1
- package/dist/systems/wall/wall-topology.d.ts +2 -2
- package/dist/systems/wall/wall-topology.js +4 -4
- package/dist/utils/clone-scene-graph.d.ts +3 -3
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +35 -3
- package/dist/utils/scene-migrations.d.ts +3 -3
- package/dist/utils/scene-migrations.js +3 -3
- package/dist/utils/vertical-scene-migration.js +5 -5
- package/dist/validation/validate-build-json.d.ts +7 -1
- package/dist/validation/validate-build-json.d.ts.map +1 -1
- package/dist/validation/validate-build-json.js +92 -4
- package/package.json +23 -5
- package/dist/systems/elevator/elevator-runtime-system.d.ts +0 -2
- package/dist/systems/elevator/elevator-runtime-system.d.ts.map +0 -1
- package/dist/systems/elevator/elevator-runtime-system.js +0 -8
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { nodeRegistry } from '../../registry/registry';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { nodeRegistry } from '../../registry/registry.js';
|
|
2
|
+
import { validateNodeRelations } from '../../registry/validate-relations.js';
|
|
3
|
+
import { AnyNode as AnyNodeSchema, createDefaultGuttersForSegment, createDefaultRidgeVentsForSegment, DownspoutNode as DownspoutNodeSchema, defaultDownspoutMetadata, generateId, getDefaultGutterSide, getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, isAutoGutterEnabled, isAutoRidgeVentEnabled, isDefaultDownspoutNode, isDefaultGutterNode, isDefaultRidgeVentNode, parseNode, planAutomaticDownspouts, resolveAutomaticDownspoutLength, } from '../../schema/index.js';
|
|
4
|
+
import { constrainWallCurveOffsetToAvoidIntersections } from '../../systems/wall/wall-curve.js';
|
|
5
|
+
import { activeSceneCommitNodeIds, addActiveSceneCommitNodeIds, runWithSceneCommitNodeIds, } from '../history-control.js';
|
|
4
6
|
const DEFAULT_RIDGE_VENT_REFRESH_FIELDS = new Set([
|
|
5
7
|
'roofType',
|
|
6
8
|
'width',
|
|
@@ -15,6 +17,18 @@ const DEFAULT_RIDGE_VENT_REFRESH_FIELDS = new Set([
|
|
|
15
17
|
'dutchWaistLengthRatio',
|
|
16
18
|
'dutchGabletRake',
|
|
17
19
|
]);
|
|
20
|
+
const DEFAULT_GUTTER_REFRESH_FIELDS = new Set([
|
|
21
|
+
'metadata',
|
|
22
|
+
'position',
|
|
23
|
+
'rotation',
|
|
24
|
+
'roofType',
|
|
25
|
+
'width',
|
|
26
|
+
'depth',
|
|
27
|
+
'wallHeight',
|
|
28
|
+
'pitch',
|
|
29
|
+
'overhang',
|
|
30
|
+
'trim',
|
|
31
|
+
]);
|
|
18
32
|
const NUMBER_FORMAT_BOUNDS = {
|
|
19
33
|
safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
|
|
20
34
|
int32: [-2147483648, 2147483647],
|
|
@@ -319,21 +333,46 @@ function formatNumericValue(value) {
|
|
|
319
333
|
return '-Infinity';
|
|
320
334
|
return String(value);
|
|
321
335
|
}
|
|
322
|
-
function numericSanitizeIssuesToMessage(issues) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
336
|
+
export function numericSanitizeIssuesToMessage(issues) {
|
|
337
|
+
if (!Array.isArray(issues))
|
|
338
|
+
return '';
|
|
339
|
+
try {
|
|
340
|
+
return issues
|
|
341
|
+
.map((issue) => {
|
|
342
|
+
const path = Array.isArray(issue?.path)
|
|
343
|
+
? issue.path.map(String).join('.') || '<root>'
|
|
344
|
+
: '<unknown>';
|
|
345
|
+
const to = issue?.to === undefined ? '' : ` -> ${formatNumericValue(issue.to)}`;
|
|
346
|
+
return `${path}: ${formatNumericValue(issue?.from)} ${issue?.action ?? 'sanitized'}${to}`;
|
|
347
|
+
})
|
|
348
|
+
.join('; ');
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
return '<diagnostic unavailable>';
|
|
352
|
+
}
|
|
330
353
|
}
|
|
331
354
|
function warnSanitizedNodeMutation(mutation, nodeId, issues) {
|
|
332
|
-
|
|
355
|
+
let message = '<diagnostic unavailable>';
|
|
356
|
+
try {
|
|
357
|
+
message = numericSanitizeIssuesToMessage(issues);
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
// Reporting must never interrupt a node mutation.
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
console.warn(`[Scene] Sanitized invalid numeric node ${mutation}`, nodeId, message);
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
// A broken diagnostic sink must not interrupt a node mutation either.
|
|
367
|
+
}
|
|
333
368
|
}
|
|
334
369
|
function parseCreatedNode(node, parentId) {
|
|
335
370
|
const candidate = { ...node, parentId };
|
|
336
|
-
const
|
|
371
|
+
const registered = nodeRegistry.get(candidate.type)?.schema;
|
|
372
|
+
// Generated definitions must reject invalid geometry instead of retaining a failed parse.
|
|
373
|
+
if (registered?.meta?.()?.strictMutations === true)
|
|
374
|
+
return registered.parse(candidate);
|
|
375
|
+
const parsed = parseNode(candidate);
|
|
337
376
|
if (parsed.success)
|
|
338
377
|
return parsed.data;
|
|
339
378
|
const schema = getNodeSchemaForType(candidate.type);
|
|
@@ -359,9 +398,19 @@ function mergeNodeUpdate(currentNode, patch) {
|
|
|
359
398
|
}
|
|
360
399
|
function parseUpdatedNode(currentNode, data) {
|
|
361
400
|
const candidate = mergeNodeUpdate(currentNode, data);
|
|
362
|
-
|
|
401
|
+
// Graph links survive schemas that omit children; only an explicit patch may change them.
|
|
402
|
+
const preserveChildren = (updated) => !Object.hasOwn(data, 'children') &&
|
|
403
|
+
'children' in currentNode &&
|
|
404
|
+
Array.isArray(currentNode.children)
|
|
405
|
+
? { ...updated, children: currentNode.children }
|
|
406
|
+
: updated;
|
|
407
|
+
const registered = nodeRegistry.get(currentNode.type)?.schema;
|
|
408
|
+
// Generated definitions must reject invalid geometry instead of retaining a failed parse.
|
|
409
|
+
if (registered?.meta?.()?.strictMutations === true)
|
|
410
|
+
return preserveChildren(registered.parse(candidate));
|
|
411
|
+
const parsed = parseNode(candidate);
|
|
363
412
|
if (parsed.success)
|
|
364
|
-
return parsed.data;
|
|
413
|
+
return preserveChildren(parsed.data);
|
|
365
414
|
const schema = getNodeSchemaForType(candidate.type);
|
|
366
415
|
const sanitized = sanitizeNumericValue(schema, data, currentNode, []);
|
|
367
416
|
if (sanitized.issues.length === 0) {
|
|
@@ -398,6 +447,306 @@ function refreshDefaultRidgeVentsForSegment(nextNodes, segment) {
|
|
|
398
447
|
};
|
|
399
448
|
return nextVents.map((vent) => vent.id);
|
|
400
449
|
}
|
|
450
|
+
function shouldRefreshDefaultGutters(data) {
|
|
451
|
+
return Object.keys(data).some((key) => DEFAULT_GUTTER_REFRESH_FIELDS.has(key));
|
|
452
|
+
}
|
|
453
|
+
function getLeanToGutterExclusions(nodes, segmentId) {
|
|
454
|
+
return Object.values(nodes).flatMap((node) => {
|
|
455
|
+
if (node.type !== 'lean-to-extension' ||
|
|
456
|
+
node.connectionMode !== 'auto' ||
|
|
457
|
+
node.hostRoofSegmentId !== segmentId ||
|
|
458
|
+
!node.hostRoofEdge) {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
const range = node.hostRoofEdgeRange ?? [0, 1];
|
|
462
|
+
return [{ side: node.hostRoofEdge, from: range[0], to: range[1] }];
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
function addLeanToHostRoofId(node, nodes, roofIds) {
|
|
466
|
+
if (node?.type !== 'lean-to-extension' || !node.hostRoofSegmentId)
|
|
467
|
+
return;
|
|
468
|
+
const segment = nodes[node.hostRoofSegmentId];
|
|
469
|
+
const roofId = segment?.type === 'roof-segment'
|
|
470
|
+
? segment.parentId
|
|
471
|
+
: node.hostRoofId;
|
|
472
|
+
if (roofId && nodes[roofId]?.type === 'roof')
|
|
473
|
+
roofIds.add(roofId);
|
|
474
|
+
}
|
|
475
|
+
// When an eave carries several default gutter runs on the same side (e.g. a run
|
|
476
|
+
// split by a lean-to exclusion), reuse the existing node whose plan position is
|
|
477
|
+
// closest to the desired run rather than an arbitrary queue order — otherwise
|
|
478
|
+
// the runs swap positions and their downspouts follow the wrong segment.
|
|
479
|
+
function takeNearestGutterId(candidateIds, desired, nodes) {
|
|
480
|
+
if (candidateIds.length === 0)
|
|
481
|
+
return undefined;
|
|
482
|
+
let bestIndex = 0;
|
|
483
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
484
|
+
for (let i = 0; i < candidateIds.length; i++) {
|
|
485
|
+
const node = nodes[candidateIds[i]];
|
|
486
|
+
const position = node && 'position' in node ? node.position : undefined;
|
|
487
|
+
const dx = (position?.[0] ?? 0) - desired.position[0];
|
|
488
|
+
const dz = (position?.[2] ?? 0) - desired.position[2];
|
|
489
|
+
const distance = dx * dx + dz * dz;
|
|
490
|
+
if (distance < bestDistance) {
|
|
491
|
+
bestDistance = distance;
|
|
492
|
+
bestIndex = i;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return candidateIds.splice(bestIndex, 1)[0];
|
|
496
|
+
}
|
|
497
|
+
function refreshDefaultGuttersForSegment(nextNodes, segment, roofSegments) {
|
|
498
|
+
const childIds = Array.isArray(segment.children) ? segment.children : [];
|
|
499
|
+
const existingIds = childIds.filter((childId) => isDefaultGutterNode(nextNodes[childId], segment.id));
|
|
500
|
+
if (!isAutoGutterEnabled(segment, nextNodes) && existingIds.length === 0) {
|
|
501
|
+
return { dirtyIds: [], deletedIds: [] };
|
|
502
|
+
}
|
|
503
|
+
const existingBySide = new Map();
|
|
504
|
+
for (const id of existingIds) {
|
|
505
|
+
const side = getDefaultGutterSide(nextNodes[id], segment.id);
|
|
506
|
+
if (!side)
|
|
507
|
+
continue;
|
|
508
|
+
const ids = existingBySide.get(side) ?? [];
|
|
509
|
+
ids.push(id);
|
|
510
|
+
existingBySide.set(side, ids);
|
|
511
|
+
}
|
|
512
|
+
const desiredGutters = isAutoGutterEnabled(segment, nextNodes)
|
|
513
|
+
? createDefaultGuttersForSegment(segment, roofSegments, getLeanToGutterExclusions(nextNodes, segment.id))
|
|
514
|
+
: [];
|
|
515
|
+
const desiredChildIds = [];
|
|
516
|
+
const dirtyIds = [];
|
|
517
|
+
for (const desired of desiredGutters) {
|
|
518
|
+
const side = getDefaultGutterSide(desired, segment.id);
|
|
519
|
+
if (!side)
|
|
520
|
+
continue;
|
|
521
|
+
const matchingIds = existingBySide.get(side);
|
|
522
|
+
const existingId = matchingIds
|
|
523
|
+
? takeNearestGutterId(matchingIds, desired, nextNodes)
|
|
524
|
+
: undefined;
|
|
525
|
+
if (existingId) {
|
|
526
|
+
const existing = nextNodes[existingId];
|
|
527
|
+
nextNodes[existingId] = {
|
|
528
|
+
...existing,
|
|
529
|
+
parentId: segment.id,
|
|
530
|
+
roofSegmentId: segment.id,
|
|
531
|
+
position: desired.position,
|
|
532
|
+
rotation: desired.rotation,
|
|
533
|
+
length: desired.length,
|
|
534
|
+
};
|
|
535
|
+
desiredChildIds.push(existingId);
|
|
536
|
+
dirtyIds.push(existingId);
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
const desiredId = desired.id;
|
|
540
|
+
nextNodes[desiredId] = { ...desired, parentId: segment.id };
|
|
541
|
+
desiredChildIds.push(desiredId);
|
|
542
|
+
dirtyIds.push(desiredId);
|
|
543
|
+
}
|
|
544
|
+
const retainedIdSet = new Set(desiredChildIds);
|
|
545
|
+
const deletedIds = existingIds.filter((id) => !retainedIdSet.has(id));
|
|
546
|
+
const deletedGutterIds = new Set(deletedIds);
|
|
547
|
+
for (const [nodeId, node] of Object.entries(nextNodes)) {
|
|
548
|
+
if (node.type === 'downspout' &&
|
|
549
|
+
node.gutterId &&
|
|
550
|
+
deletedGutterIds.has(node.gutterId)) {
|
|
551
|
+
deletedIds.push(nodeId);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
const deletedIdSet = new Set(deletedIds);
|
|
555
|
+
for (const id of deletedIds)
|
|
556
|
+
delete nextNodes[id];
|
|
557
|
+
nextNodes[segment.id] = {
|
|
558
|
+
...segment,
|
|
559
|
+
children: [
|
|
560
|
+
...childIds.filter((childId) => !deletedIdSet.has(childId) && !existingIds.includes(childId)),
|
|
561
|
+
...desiredChildIds,
|
|
562
|
+
],
|
|
563
|
+
};
|
|
564
|
+
return { dirtyIds, deletedIds };
|
|
565
|
+
}
|
|
566
|
+
function getRoofSegments(nextNodes, segment) {
|
|
567
|
+
const roof = segment.parentId ? nextNodes[segment.parentId] : undefined;
|
|
568
|
+
if (!(roof && roof.type === 'roof'))
|
|
569
|
+
return [segment];
|
|
570
|
+
return (roof.children ?? [])
|
|
571
|
+
.map((childId) => nextNodes[childId])
|
|
572
|
+
.filter((node) => node?.type === 'roof-segment');
|
|
573
|
+
}
|
|
574
|
+
function refreshDefaultDownspoutsForRoof(nextNodes, roofSegments) {
|
|
575
|
+
const gutters = roofSegments.flatMap((segment) => {
|
|
576
|
+
const current = nextNodes[segment.id];
|
|
577
|
+
if (current?.type !== 'roof-segment')
|
|
578
|
+
return [];
|
|
579
|
+
return (current.children ?? [])
|
|
580
|
+
.map((childId) => nextNodes[childId])
|
|
581
|
+
.filter((node) => node?.type === 'gutter' && isDefaultGutterNode(node, current.id));
|
|
582
|
+
});
|
|
583
|
+
const gutterById = new Map(gutters.map((gutter) => [gutter.id, gutter]));
|
|
584
|
+
const downspouts = Object.values(nextNodes).filter((node) => node?.type === 'downspout' && Boolean(node.gutterId && gutterById.has(node.gutterId)));
|
|
585
|
+
const generated = downspouts.filter((downspout) => {
|
|
586
|
+
if (!isDefaultDownspoutNode(downspout))
|
|
587
|
+
return false;
|
|
588
|
+
const gutter = downspout.gutterId ? gutterById.get(downspout.gutterId) : undefined;
|
|
589
|
+
return gutter?.outlets.some((outlet) => outlet.id === downspout.outletId && outlet.generatedBy === 'default-downspout');
|
|
590
|
+
});
|
|
591
|
+
const generatedIds = new Set(generated.map((downspout) => downspout.id));
|
|
592
|
+
const manual = downspouts.filter((downspout) => !generatedIds.has(downspout.id));
|
|
593
|
+
const placements = planAutomaticDownspouts({
|
|
594
|
+
segments: roofSegments,
|
|
595
|
+
gutters,
|
|
596
|
+
downspouts: manual,
|
|
597
|
+
});
|
|
598
|
+
const segmentById = new Map(roofSegments.map((segment) => [segment.id, segment]));
|
|
599
|
+
const availableByGutter = new Map();
|
|
600
|
+
for (const downspout of generated) {
|
|
601
|
+
if (!downspout.gutterId)
|
|
602
|
+
continue;
|
|
603
|
+
const available = availableByGutter.get(downspout.gutterId) ?? [];
|
|
604
|
+
available.push(downspout);
|
|
605
|
+
availableByGutter.set(downspout.gutterId, available);
|
|
606
|
+
}
|
|
607
|
+
const retainedIds = new Set();
|
|
608
|
+
const retainedOutletIds = new Set();
|
|
609
|
+
const dirtyIds = new Set();
|
|
610
|
+
const deletedIds = [];
|
|
611
|
+
const outletsByGutter = new Map(gutters.map((gutter) => [gutter.id, [...(gutter.outlets ?? [])]]));
|
|
612
|
+
for (const placement of placements) {
|
|
613
|
+
const gutter = gutterById.get(placement.gutterId);
|
|
614
|
+
if (!gutter?.roofSegmentId)
|
|
615
|
+
continue;
|
|
616
|
+
const segment = segmentById.get(gutter.roofSegmentId);
|
|
617
|
+
if (!segment)
|
|
618
|
+
continue;
|
|
619
|
+
const outlets = outletsByGutter.get(gutter.id) ?? [];
|
|
620
|
+
const available = availableByGutter.get(gutter.id) ?? [];
|
|
621
|
+
let bestIndex = -1;
|
|
622
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
623
|
+
for (let index = 0; index < available.length; index++) {
|
|
624
|
+
const candidate = available[index];
|
|
625
|
+
const outlet = outlets.find((entry) => entry.id === candidate.outletId);
|
|
626
|
+
const distance = outlet ? Math.abs(outlet.offset - placement.offset) : 0;
|
|
627
|
+
if (distance < bestDistance) {
|
|
628
|
+
bestDistance = distance;
|
|
629
|
+
bestIndex = index;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
const existing = bestIndex >= 0 ? available.splice(bestIndex, 1)[0] : undefined;
|
|
633
|
+
const outletId = existing?.outletId ?? generateId('outlet');
|
|
634
|
+
const outletIndex = outlets.findIndex((outlet) => outlet.id === outletId);
|
|
635
|
+
const outlet = {
|
|
636
|
+
id: outletId,
|
|
637
|
+
offset: placement.offset,
|
|
638
|
+
diameter: 0.07,
|
|
639
|
+
generatedBy: 'default-downspout',
|
|
640
|
+
};
|
|
641
|
+
if (outletIndex >= 0)
|
|
642
|
+
outlets[outletIndex] = { ...outlets[outletIndex], ...outlet };
|
|
643
|
+
else
|
|
644
|
+
outlets.push(outlet);
|
|
645
|
+
outletsByGutter.set(gutter.id, outlets);
|
|
646
|
+
retainedOutletIds.add(outletId);
|
|
647
|
+
const length = resolveAutomaticDownspoutLength(nextNodes, segment, gutter, placement.offset);
|
|
648
|
+
const downspout = existing
|
|
649
|
+
? {
|
|
650
|
+
...existing,
|
|
651
|
+
parentId: segment.id,
|
|
652
|
+
gutterId: gutter.id,
|
|
653
|
+
outletId,
|
|
654
|
+
length: existing.lengthMode === 'manual' ? existing.length : length,
|
|
655
|
+
lengthMode: existing.lengthMode === 'manual' ? 'manual' : 'to-ground',
|
|
656
|
+
}
|
|
657
|
+
: DownspoutNodeSchema.parse({
|
|
658
|
+
name: 'Downspout',
|
|
659
|
+
parentId: segment.id,
|
|
660
|
+
gutterId: gutter.id,
|
|
661
|
+
outletId,
|
|
662
|
+
length,
|
|
663
|
+
lengthMode: 'to-ground',
|
|
664
|
+
diameter: outlet.diameter,
|
|
665
|
+
metadata: defaultDownspoutMetadata(),
|
|
666
|
+
});
|
|
667
|
+
nextNodes[downspout.id] = downspout;
|
|
668
|
+
retainedIds.add(downspout.id);
|
|
669
|
+
dirtyIds.add(downspout.id);
|
|
670
|
+
const currentSegment = nextNodes[segment.id];
|
|
671
|
+
if (currentSegment?.type === 'roof-segment') {
|
|
672
|
+
nextNodes[segment.id] = {
|
|
673
|
+
...currentSegment,
|
|
674
|
+
children: Array.from(new Set([...(currentSegment.children ?? []), downspout.id])),
|
|
675
|
+
};
|
|
676
|
+
dirtyIds.add(segment.id);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
for (const [gutterId, outlets] of outletsByGutter) {
|
|
680
|
+
const gutter = nextNodes[gutterId];
|
|
681
|
+
if (gutter?.type !== 'gutter')
|
|
682
|
+
continue;
|
|
683
|
+
nextNodes[gutterId] = {
|
|
684
|
+
...gutter,
|
|
685
|
+
outlets: outlets.filter((outlet) => outlet.generatedBy !== 'default-downspout' || retainedOutletIds.has(outlet.id)),
|
|
686
|
+
};
|
|
687
|
+
dirtyIds.add(gutterId);
|
|
688
|
+
}
|
|
689
|
+
for (const downspout of generated) {
|
|
690
|
+
const downspoutId = downspout.id;
|
|
691
|
+
if (retainedIds.has(downspoutId))
|
|
692
|
+
continue;
|
|
693
|
+
const gutter = downspout.gutterId ? nextNodes[downspout.gutterId] : undefined;
|
|
694
|
+
if (gutter?.type === 'gutter' && downspout.outletId) {
|
|
695
|
+
nextNodes[gutter.id] = {
|
|
696
|
+
...gutter,
|
|
697
|
+
outlets: (gutter.outlets ?? []).filter((outlet) => outlet.id !== downspout.outletId),
|
|
698
|
+
};
|
|
699
|
+
dirtyIds.add(gutter.id);
|
|
700
|
+
}
|
|
701
|
+
const parent = downspout.parentId ? nextNodes[downspout.parentId] : undefined;
|
|
702
|
+
if (parent?.type === 'roof-segment') {
|
|
703
|
+
nextNodes[parent.id] = {
|
|
704
|
+
...parent,
|
|
705
|
+
children: (parent.children ?? []).filter((childId) => childId !== downspout.id),
|
|
706
|
+
};
|
|
707
|
+
dirtyIds.add(parent.id);
|
|
708
|
+
}
|
|
709
|
+
delete nextNodes[downspoutId];
|
|
710
|
+
deletedIds.push(downspoutId);
|
|
711
|
+
}
|
|
712
|
+
return { dirtyIds: [...dirtyIds], deletedIds };
|
|
713
|
+
}
|
|
714
|
+
function refreshDefaultGuttersForRoof(nextNodes, segment) {
|
|
715
|
+
const roofSegments = getRoofSegments(nextNodes, segment);
|
|
716
|
+
const dirtyIds = [];
|
|
717
|
+
const deletedIds = [];
|
|
718
|
+
for (const roofSegment of roofSegments) {
|
|
719
|
+
const current = nextNodes[roofSegment.id];
|
|
720
|
+
if (current?.type !== 'roof-segment')
|
|
721
|
+
continue;
|
|
722
|
+
const result = refreshDefaultGuttersForSegment(nextNodes, current, roofSegments);
|
|
723
|
+
dirtyIds.push(...result.dirtyIds);
|
|
724
|
+
deletedIds.push(...result.deletedIds);
|
|
725
|
+
}
|
|
726
|
+
const downspoutResult = refreshDefaultDownspoutsForRoof(nextNodes, roofSegments);
|
|
727
|
+
dirtyIds.push(...downspoutResult.dirtyIds);
|
|
728
|
+
deletedIds.push(...downspoutResult.deletedIds);
|
|
729
|
+
return { dirtyIds, deletedIds };
|
|
730
|
+
}
|
|
731
|
+
function collectDefaultGutterRefresh(result, dirtyIds, deletedIds) {
|
|
732
|
+
for (const id of result.dirtyIds)
|
|
733
|
+
dirtyIds.add(id);
|
|
734
|
+
for (const id of result.deletedIds)
|
|
735
|
+
deletedIds.add(id);
|
|
736
|
+
}
|
|
737
|
+
function refreshDefaultGuttersForRoofIds(nextNodes, roofIds, dirtyIds, deletedIds) {
|
|
738
|
+
for (const roofId of new Set(roofIds)) {
|
|
739
|
+
const roof = nextNodes[roofId];
|
|
740
|
+
if (roof?.type !== 'roof')
|
|
741
|
+
continue;
|
|
742
|
+
const segment = (roof.children ?? [])
|
|
743
|
+
.map((childId) => nextNodes[childId])
|
|
744
|
+
.find((child) => child?.type === 'roof-segment');
|
|
745
|
+
if (!segment)
|
|
746
|
+
continue;
|
|
747
|
+
collectDefaultGutterRefresh(refreshDefaultGuttersForRoof(nextNodes, segment), dirtyIds, deletedIds);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
401
750
|
// Track pending RAF for updateNodesAction to prevent multiple queued callbacks
|
|
402
751
|
let pendingRafId = null;
|
|
403
752
|
let pendingUpdates = new Set();
|
|
@@ -551,6 +900,8 @@ function buildWallMergePlans(nodes, idsToDelete) {
|
|
|
551
900
|
const createNodesActionImpl = (set, get, ops) => {
|
|
552
901
|
if (get().readOnly)
|
|
553
902
|
return;
|
|
903
|
+
const extraNodesToMarkDirty = new Set();
|
|
904
|
+
const extraNodesToClearDirty = new Set();
|
|
554
905
|
set((state) => {
|
|
555
906
|
const nextNodes = { ...state.nodes };
|
|
556
907
|
const nextRootIds = [...state.rootNodeIds];
|
|
@@ -585,6 +936,16 @@ const createNodesActionImpl = (set, get, ops) => {
|
|
|
585
936
|
}
|
|
586
937
|
}
|
|
587
938
|
}
|
|
939
|
+
const refreshedRoofIds = new Set();
|
|
940
|
+
for (const { node } of ops) {
|
|
941
|
+
const created = nextNodes[node.id];
|
|
942
|
+
if (created?.type === 'roof-segment' && created.parentId) {
|
|
943
|
+
refreshedRoofIds.add(created.parentId);
|
|
944
|
+
}
|
|
945
|
+
addLeanToHostRoofId(created, nextNodes, refreshedRoofIds);
|
|
946
|
+
}
|
|
947
|
+
refreshDefaultGuttersForRoofIds(nextNodes, refreshedRoofIds, extraNodesToMarkDirty, extraNodesToClearDirty);
|
|
948
|
+
addActiveSceneCommitNodeIds([...extraNodesToMarkDirty, ...extraNodesToClearDirty]);
|
|
588
949
|
return { nodes: nextNodes, rootNodeIds: nextRootIds };
|
|
589
950
|
});
|
|
590
951
|
// 4. System Sync
|
|
@@ -595,6 +956,10 @@ const createNodesActionImpl = (set, get, ops) => {
|
|
|
595
956
|
else if (node.parentId)
|
|
596
957
|
get().markDirty(node.parentId);
|
|
597
958
|
});
|
|
959
|
+
for (const id of extraNodesToMarkDirty)
|
|
960
|
+
get().markDirty(id);
|
|
961
|
+
for (const id of extraNodesToClearDirty)
|
|
962
|
+
get().clearDirty(id);
|
|
598
963
|
};
|
|
599
964
|
const applyNodeChangesActionImpl = (set, get, changes) => {
|
|
600
965
|
if (get().readOnly)
|
|
@@ -603,17 +968,21 @@ const applyNodeChangesActionImpl = (set, get, changes) => {
|
|
|
603
968
|
const updateOps = changes.update ?? [];
|
|
604
969
|
const deleteOps = changes.delete ?? [];
|
|
605
970
|
const nodesToMarkDirty = new Set();
|
|
971
|
+
const nodesToClearDirty = new Set();
|
|
606
972
|
const parentsToMarkDirty = new Set();
|
|
607
973
|
set((state) => {
|
|
608
974
|
const nextNodes = { ...state.nodes };
|
|
609
975
|
const nextCollections = { ...state.collections };
|
|
610
976
|
const nextRootIds = [...state.rootNodeIds];
|
|
611
977
|
let resolvedRootIds = nextRootIds;
|
|
978
|
+
const roofsToRefresh = new Set();
|
|
612
979
|
for (const { id, data } of updateOps) {
|
|
613
980
|
const currentNode = nextNodes[id];
|
|
614
981
|
if (!currentNode)
|
|
615
982
|
continue;
|
|
983
|
+
addLeanToHostRoofId(currentNode, nextNodes, roofsToRefresh);
|
|
616
984
|
const updatedNode = parseUpdatedNode(currentNode, data);
|
|
985
|
+
addLeanToHostRoofId(updatedNode, nextNodes, roofsToRefresh);
|
|
617
986
|
if (data.parentId !== undefined && data.parentId !== currentNode.parentId) {
|
|
618
987
|
const oldParentId = currentNode.parentId;
|
|
619
988
|
if (oldParentId && nextNodes[oldParentId]) {
|
|
@@ -640,6 +1009,11 @@ const applyNodeChangesActionImpl = (set, get, changes) => {
|
|
|
640
1009
|
nodesToMarkDirty.add(ventId);
|
|
641
1010
|
}
|
|
642
1011
|
}
|
|
1012
|
+
const currentSegment = nextNodes[id];
|
|
1013
|
+
if (currentSegment?.type === 'roof-segment' && shouldRefreshDefaultGutters(data)) {
|
|
1014
|
+
if (currentSegment.parentId)
|
|
1015
|
+
roofsToRefresh.add(currentSegment.parentId);
|
|
1016
|
+
}
|
|
643
1017
|
nodesToMarkDirty.add(id);
|
|
644
1018
|
}
|
|
645
1019
|
for (const { node, parentId } of createOps) {
|
|
@@ -647,6 +1021,10 @@ const applyNodeChangesActionImpl = (set, get, changes) => {
|
|
|
647
1021
|
const newNode = parseCreatedNode(node, effectiveParentId);
|
|
648
1022
|
nextNodes[newNode.id] = newNode;
|
|
649
1023
|
nodesToMarkDirty.add(newNode.id);
|
|
1024
|
+
if (newNode.type === 'roof-segment' && effectiveParentId) {
|
|
1025
|
+
roofsToRefresh.add(effectiveParentId);
|
|
1026
|
+
}
|
|
1027
|
+
addLeanToHostRoofId(newNode, nextNodes, roofsToRefresh);
|
|
650
1028
|
if (effectiveParentId && nextNodes[effectiveParentId]) {
|
|
651
1029
|
const parent = nextNodes[effectiveParentId];
|
|
652
1030
|
if ('children' in parent && Array.isArray(parent.children)) {
|
|
@@ -676,6 +1054,13 @@ const applyNodeChangesActionImpl = (set, get, changes) => {
|
|
|
676
1054
|
for (const id of deleteOps) {
|
|
677
1055
|
collectDelete(id);
|
|
678
1056
|
}
|
|
1057
|
+
for (const id of allIdsToDelete) {
|
|
1058
|
+
const node = nextNodes[id];
|
|
1059
|
+
addLeanToHostRoofId(node, nextNodes, roofsToRefresh);
|
|
1060
|
+
if (node?.type === 'roof-segment' && node.parentId) {
|
|
1061
|
+
roofsToRefresh.add(node.parentId);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
679
1064
|
for (const id of allIdsToDelete) {
|
|
680
1065
|
const node = nextNodes[id];
|
|
681
1066
|
if (!node)
|
|
@@ -705,12 +1090,21 @@ const applyNodeChangesActionImpl = (set, get, changes) => {
|
|
|
705
1090
|
}
|
|
706
1091
|
delete nextNodes[id];
|
|
707
1092
|
}
|
|
708
|
-
|
|
1093
|
+
refreshDefaultGuttersForRoofIds(nextNodes, roofsToRefresh, nodesToMarkDirty, nodesToClearDirty);
|
|
1094
|
+
addActiveSceneCommitNodeIds([
|
|
1095
|
+
...allIdsToDelete,
|
|
1096
|
+
...nodesToMarkDirty,
|
|
1097
|
+
...nodesToClearDirty,
|
|
1098
|
+
...parentsToMarkDirty,
|
|
1099
|
+
]);
|
|
709
1100
|
return { nodes: nextNodes, rootNodeIds: resolvedRootIds, collections: nextCollections };
|
|
710
1101
|
});
|
|
711
1102
|
for (const id of nodesToMarkDirty) {
|
|
712
1103
|
get().markDirty(id);
|
|
713
1104
|
}
|
|
1105
|
+
for (const id of nodesToClearDirty) {
|
|
1106
|
+
get().clearDirty(id);
|
|
1107
|
+
}
|
|
714
1108
|
for (const id of parentsToMarkDirty) {
|
|
715
1109
|
get().markDirty(id);
|
|
716
1110
|
const parent = get().nodes[id];
|
|
@@ -726,13 +1120,24 @@ const updateNodesActionImpl = (set, get, updates) => {
|
|
|
726
1120
|
return;
|
|
727
1121
|
const parentsToUpdate = new Set();
|
|
728
1122
|
const extraNodesToUpdate = new Set();
|
|
1123
|
+
const extraNodesToDelete = new Set();
|
|
1124
|
+
const roofsToRefresh = new Set();
|
|
729
1125
|
set((state) => {
|
|
730
1126
|
const nextNodes = { ...state.nodes };
|
|
731
1127
|
for (const { id, data } of updates) {
|
|
732
1128
|
const currentNode = nextNodes[id];
|
|
733
1129
|
if (!currentNode)
|
|
734
1130
|
continue;
|
|
735
|
-
|
|
1131
|
+
addLeanToHostRoofId(currentNode, nextNodes, roofsToRefresh);
|
|
1132
|
+
const curveOffset = currentNode.type === 'wall' ? data.curveOffset : undefined;
|
|
1133
|
+
const constrainedData = currentNode.type === 'wall' && typeof curveOffset === 'number'
|
|
1134
|
+
? {
|
|
1135
|
+
...data,
|
|
1136
|
+
curveOffset: constrainWallCurveOffsetToAvoidIntersections(currentNode, curveOffset, Object.values(nextNodes).filter((node) => node.type === 'wall' && node.parentId === currentNode.parentId)),
|
|
1137
|
+
}
|
|
1138
|
+
: data;
|
|
1139
|
+
const updatedNode = parseUpdatedNode(currentNode, constrainedData);
|
|
1140
|
+
addLeanToHostRoofId(updatedNode, nextNodes, roofsToRefresh);
|
|
736
1141
|
// Handle Reparenting Logic
|
|
737
1142
|
if (data.parentId !== undefined && data.parentId !== currentNode.parentId) {
|
|
738
1143
|
// 1. Remove from old parent
|
|
@@ -774,11 +1179,18 @@ const updateNodesActionImpl = (set, get, updates) => {
|
|
|
774
1179
|
extraNodesToUpdate.add(ventId);
|
|
775
1180
|
}
|
|
776
1181
|
}
|
|
1182
|
+
const currentSegment = nextNodes[id];
|
|
1183
|
+
if (currentSegment?.type === 'roof-segment' && shouldRefreshDefaultGutters(data)) {
|
|
1184
|
+
if (currentSegment.parentId)
|
|
1185
|
+
roofsToRefresh.add(currentSegment.parentId);
|
|
1186
|
+
}
|
|
777
1187
|
}
|
|
1188
|
+
refreshDefaultGuttersForRoofIds(nextNodes, roofsToRefresh, extraNodesToUpdate, extraNodesToDelete);
|
|
778
1189
|
addActiveSceneCommitNodeIds([
|
|
779
1190
|
...updates.map(({ id }) => id),
|
|
780
1191
|
...parentsToUpdate,
|
|
781
1192
|
...extraNodesToUpdate,
|
|
1193
|
+
...extraNodesToDelete,
|
|
782
1194
|
]);
|
|
783
1195
|
return { nodes: nextNodes };
|
|
784
1196
|
});
|
|
@@ -797,6 +1209,9 @@ const updateNodesActionImpl = (set, get, updates) => {
|
|
|
797
1209
|
for (const id of extraNodesToUpdate) {
|
|
798
1210
|
pendingUpdates.add(id);
|
|
799
1211
|
}
|
|
1212
|
+
for (const id of extraNodesToDelete) {
|
|
1213
|
+
get().clearDirty(id);
|
|
1214
|
+
}
|
|
800
1215
|
if (pendingRafId !== null) {
|
|
801
1216
|
cancelAnimationFrame(pendingRafId);
|
|
802
1217
|
}
|
|
@@ -815,6 +1230,7 @@ const deleteNodesActionImpl = (set, get, ids) => {
|
|
|
815
1230
|
const nodesToMarkDirty = new Set();
|
|
816
1231
|
const deletedIds = new Set();
|
|
817
1232
|
const mergePlans = buildWallMergePlans(get().nodes, ids);
|
|
1233
|
+
const requestedDeleteIds = new Set(ids);
|
|
818
1234
|
set((state) => {
|
|
819
1235
|
const nextNodes = { ...state.nodes };
|
|
820
1236
|
const nextCollections = { ...state.collections };
|
|
@@ -828,7 +1244,9 @@ const deleteNodesActionImpl = (set, get, ids) => {
|
|
|
828
1244
|
allIds.add(id);
|
|
829
1245
|
const node = nextNodes[id];
|
|
830
1246
|
const cascadeDeletes = node
|
|
831
|
-
? nodeRegistry
|
|
1247
|
+
? nodeRegistry
|
|
1248
|
+
.get(node.type)
|
|
1249
|
+
?.parametrics?.onDeleteCascade?.(node, nextNodes, allIds, requestedDeleteIds)
|
|
832
1250
|
: null;
|
|
833
1251
|
if (cascadeDeletes) {
|
|
834
1252
|
for (const companionId of cascadeDeletes)
|
|
@@ -844,6 +1262,14 @@ const deleteNodesActionImpl = (set, get, ids) => {
|
|
|
844
1262
|
for (const plan of mergePlans) {
|
|
845
1263
|
allIds.add(plan.secondaryWallId);
|
|
846
1264
|
}
|
|
1265
|
+
const affectedRoofIds = new Set();
|
|
1266
|
+
for (const id of allIds) {
|
|
1267
|
+
const node = nextNodes[id];
|
|
1268
|
+
addLeanToHostRoofId(node, nextNodes, affectedRoofIds);
|
|
1269
|
+
if (node?.type === 'roof-segment' && node.parentId) {
|
|
1270
|
+
affectedRoofIds.add(node.parentId);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
847
1273
|
for (const id of allIds)
|
|
848
1274
|
deletedIds.add(id);
|
|
849
1275
|
// Let each deleted kind undo what it imposed on its neighbours (e.g. an
|
|
@@ -857,7 +1283,7 @@ const deleteNodesActionImpl = (set, get, ids) => {
|
|
|
857
1283
|
const onDelete = nodeRegistry.get(node.type)?.parametrics?.onDelete;
|
|
858
1284
|
if (!onDelete)
|
|
859
1285
|
continue;
|
|
860
|
-
for (const { id: targetId, data } of onDelete(node, nextNodes)) {
|
|
1286
|
+
for (const { id: targetId, data } of onDelete(node, nextNodes, allIds, requestedDeleteIds)) {
|
|
861
1287
|
if (allIds.has(targetId))
|
|
862
1288
|
continue;
|
|
863
1289
|
const target = nextNodes[targetId];
|
|
@@ -917,6 +1343,22 @@ const deleteNodesActionImpl = (set, get, ids) => {
|
|
|
917
1343
|
}
|
|
918
1344
|
}
|
|
919
1345
|
}
|
|
1346
|
+
const deletedZoneIds = new Set();
|
|
1347
|
+
for (const id of allIds) {
|
|
1348
|
+
if (nextNodes[id]?.type === 'zone')
|
|
1349
|
+
deletedZoneIds.add(id);
|
|
1350
|
+
}
|
|
1351
|
+
if (deletedZoneIds.size > 0) {
|
|
1352
|
+
for (const node of Object.values(nextNodes)) {
|
|
1353
|
+
if (node.type !== 'unit' || allIds.has(node.id))
|
|
1354
|
+
continue;
|
|
1355
|
+
const members = node.members.filter((id) => !deletedZoneIds.has(id));
|
|
1356
|
+
if (members.length === node.members.length)
|
|
1357
|
+
continue;
|
|
1358
|
+
nextNodes[node.id] = { ...node, members };
|
|
1359
|
+
nodesToMarkDirty.add(node.id);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
920
1362
|
for (const id of allIds) {
|
|
921
1363
|
const node = nextNodes[id];
|
|
922
1364
|
if (!node)
|
|
@@ -947,7 +1389,8 @@ const deleteNodesActionImpl = (set, get, ids) => {
|
|
|
947
1389
|
// 4. Delete the node itself
|
|
948
1390
|
delete nextNodes[id];
|
|
949
1391
|
}
|
|
950
|
-
|
|
1392
|
+
refreshDefaultGuttersForRoofIds(nextNodes, affectedRoofIds, nodesToMarkDirty, deletedIds);
|
|
1393
|
+
addActiveSceneCommitNodeIds([...deletedIds, ...parentsToMarkDirty, ...nodesToMarkDirty]);
|
|
951
1394
|
return { nodes: nextNodes, rootNodeIds: nextRootIds, collections: nextCollections };
|
|
952
1395
|
});
|
|
953
1396
|
// Deleted ids must leave the dirty set: every consumer skips missing
|
|
@@ -970,10 +1413,18 @@ const deleteNodesActionImpl = (set, get, ids) => {
|
|
|
970
1413
|
get().markDirty(id);
|
|
971
1414
|
});
|
|
972
1415
|
};
|
|
1416
|
+
function validatedSet(set) {
|
|
1417
|
+
return (change) => set((state) => {
|
|
1418
|
+
const patch = change(state);
|
|
1419
|
+
if (patch.nodes)
|
|
1420
|
+
validateNodeRelations(state.nodes, patch.nodes, activeSceneCommitNodeIds() ?? []);
|
|
1421
|
+
return patch;
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
973
1424
|
export const createNodesAction = (set, get, ops) => runWithSceneCommitNodeIds(ops.flatMap(({ node, parentId }) => {
|
|
974
1425
|
const effectiveParentId = parentId ?? node.parentId;
|
|
975
1426
|
return effectiveParentId ? [node.id, effectiveParentId] : [node.id];
|
|
976
|
-
}), () => createNodesActionImpl(set, get, ops));
|
|
1427
|
+
}), () => createNodesActionImpl(validatedSet(set), get, ops));
|
|
977
1428
|
export const applyNodeChangesAction = (set, get, changes) => runWithSceneCommitNodeIds([
|
|
978
1429
|
...(changes.create ?? []).flatMap(({ node, parentId }) => {
|
|
979
1430
|
const effectiveParentId = parentId ?? node.parentId;
|
|
@@ -981,6 +1432,6 @@ export const applyNodeChangesAction = (set, get, changes) => runWithSceneCommitN
|
|
|
981
1432
|
}),
|
|
982
1433
|
...(changes.update ?? []).map(({ id }) => id),
|
|
983
1434
|
...(changes.delete ?? []),
|
|
984
|
-
], () => applyNodeChangesActionImpl(set, get, changes));
|
|
985
|
-
export const updateNodesAction = (set, get, updates) => runWithSceneCommitNodeIds(updates.map(({ id }) => id), () => updateNodesActionImpl(set, get, updates));
|
|
986
|
-
export const deleteNodesAction = (set, get, ids) => runWithSceneCommitNodeIds(ids, () => deleteNodesActionImpl(set, get, ids));
|
|
1435
|
+
], () => applyNodeChangesActionImpl(validatedSet(set), get, changes));
|
|
1436
|
+
export const updateNodesAction = (set, get, updates) => runWithSceneCommitNodeIds(updates.map(({ id }) => id), () => updateNodesActionImpl(validatedSet(set), get, updates));
|
|
1437
|
+
export const deleteNodesAction = (set, get, ids) => runWithSceneCommitNodeIds(ids, () => deleteNodesActionImpl(validatedSet(set), get, ids));
|