@pascal-app/core 0.9.1 → 1.0.0-beta.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/README.md +17 -10
- package/dist/events/bus.d.ts +49 -2
- 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 +8 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +27 -1
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +17 -1
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +61 -8
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +135 -28
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +427 -271
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +46 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +208 -9
- package/dist/hooks/spatial-grid/support-host-id.d.ts +3 -0
- package/dist/hooks/spatial-grid/support-host-id.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/support-host-id.js +2 -0
- package/dist/hooks/spatial-grid/support-host-patch.d.ts +51 -0
- package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/support-host-patch.js +164 -0
- package/dist/index.d.ts +33 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -10
- 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 +23 -0
- package/dist/lib/measurement-geometry.d.ts.map +1 -0
- package/dist/lib/measurement-geometry.js +249 -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/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 +557 -31
- 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.d.ts +40 -6
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +574 -192
- package/dist/lib/terrain-brush.d.ts +163 -0
- package/dist/lib/terrain-brush.d.ts.map +1 -0
- package/dist/lib/terrain-brush.js +359 -0
- package/dist/lib/terrain-codec.d.ts +51 -0
- package/dist/lib/terrain-codec.d.ts.map +1 -0
- package/dist/lib/terrain-codec.js +167 -0
- package/dist/lib/terrain-field.d.ts +150 -0
- package/dist/lib/terrain-field.d.ts.map +1 -0
- package/dist/lib/terrain-field.js +248 -0
- package/dist/lib/terrain-raycast.d.ts +44 -0
- package/dist/lib/terrain-raycast.d.ts.map +1 -0
- package/dist/lib/terrain-raycast.js +188 -0
- package/dist/lib/terrain-source.d.ts +60 -0
- package/dist/lib/terrain-source.d.ts.map +1 -0
- package/dist/lib/terrain-source.js +90 -0
- package/dist/lib/terrain-support.d.ts +60 -0
- package/dist/lib/terrain-support.d.ts.map +1 -0
- package/dist/lib/terrain-support.js +150 -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 +428 -0
- package/dist/material-library.d.ts +28 -1
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +2233 -441
- package/dist/registry/handles.d.ts +91 -3
- package/dist/registry/handles.d.ts.map +1 -1
- package/dist/registry/index.d.ts +3 -3
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +1 -1
- package/dist/registry/registry.d.ts +46 -3
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +82 -1
- package/dist/registry/subtree.d.ts.map +1 -1
- package/dist/registry/subtree.js +8 -1
- package/dist/registry/types.d.ts +781 -39
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/schema/index.d.ts +27 -8
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +23 -6
- package/dist/schema/material.d.ts +3 -2
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +7 -5
- package/dist/schema/nodes/box-vent.d.ts +2 -2
- package/dist/schema/nodes/cabinet.d.ts +536 -0
- package/dist/schema/nodes/cabinet.d.ts.map +1 -0
- package/dist/schema/nodes/cabinet.js +139 -0
- package/dist/schema/nodes/ceiling.d.ts +4 -3
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +11 -1
- package/dist/schema/nodes/chimney.d.ts +4 -4
- package/dist/schema/nodes/column.d.ts +4 -2
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +5 -0
- package/dist/schema/nodes/construction-dimension.d.ts +195 -0
- package/dist/schema/nodes/construction-dimension.d.ts.map +1 -0
- package/dist/schema/nodes/construction-dimension.js +151 -0
- package/dist/schema/nodes/cupola.d.ts +2 -2
- package/dist/schema/nodes/door.d.ts +42 -5
- package/dist/schema/nodes/door.d.ts.map +1 -1
- package/dist/schema/nodes/door.js +32 -0
- package/dist/schema/nodes/dormer.d.ts +8 -8
- package/dist/schema/nodes/dormer.js +1 -1
- package/dist/schema/nodes/downspout.d.ts +2 -2
- 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 +58 -0
- package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
- package/dist/schema/nodes/duct-terminal.js +53 -0
- package/dist/schema/nodes/elevator.d.ts +3 -2
- package/dist/schema/nodes/elevator.d.ts.map +1 -1
- package/dist/schema/nodes/elevator.js +3 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts +2 -2
- package/dist/schema/nodes/fence.d.ts +20 -2
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +33 -2
- package/dist/schema/nodes/gutter.d.ts +2 -2
- package/dist/schema/nodes/hvac-equipment.d.ts +64 -0
- package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
- package/dist/schema/nodes/hvac-equipment.js +57 -0
- package/dist/schema/nodes/item.d.ts +9 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +24 -0
- package/dist/schema/nodes/level.d.ts +2 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +12 -31
- 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/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.d.ts +18 -3
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -1
- package/dist/schema/nodes/ridge-vent.js +221 -3
- 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-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.d.ts +84 -8
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +290 -5
- package/dist/schema/nodes/roof.d.ts +8 -8
- package/dist/schema/nodes/shelf.d.ts +4 -2
- package/dist/schema/nodes/shelf.d.ts.map +1 -1
- package/dist/schema/nodes/shelf.js +8 -0
- package/dist/schema/nodes/site.d.ts +9 -0
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/site.js +8 -6
- package/dist/schema/nodes/skylight.d.ts +10 -10
- package/dist/schema/nodes/slab.d.ts +8 -2
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +18 -2
- package/dist/schema/nodes/solar-panel.d.ts +4 -4
- package/dist/schema/nodes/spawn.d.ts +1 -0
- package/dist/schema/nodes/spawn.d.ts.map +1 -1
- package/dist/schema/nodes/spawn.js +2 -0
- package/dist/schema/nodes/stair-segment.d.ts +4 -4
- package/dist/schema/nodes/stair.d.ts +16 -13
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +11 -1
- package/dist/schema/nodes/structural-grid.d.ts +27 -0
- package/dist/schema/nodes/structural-grid.d.ts.map +1 -0
- package/dist/schema/nodes/structural-grid.js +17 -0
- package/dist/schema/nodes/turbine-vent.d.ts +2 -2
- package/dist/schema/nodes/wall.d.ts +248 -6
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +266 -0
- package/dist/schema/nodes/window.d.ts +40 -3
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +30 -0
- package/dist/schema/nodes/zone.d.ts +22 -0
- package/dist/schema/nodes/zone.d.ts.map +1 -1
- package/dist/schema/nodes/zone.js +21 -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/terrain.d.ts +28 -0
- package/dist/schema/terrain.d.ts.map +1 -0
- package/dist/schema/terrain.js +30 -0
- package/dist/schema/types.d.ts +1300 -97
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +27 -0
- package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts +80 -0
- package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts.map +1 -0
- package/dist/services/__fixtures__/wall-plane-top-boundary-repro.js +89 -0
- package/dist/services/alignment-anchors.d.ts.map +1 -1
- package/dist/services/alignment-anchors.js +30 -1
- package/dist/services/alignment.d.ts +5 -0
- package/dist/services/alignment.d.ts.map +1 -1
- package/dist/services/alignment.js +2 -0
- package/dist/services/hosting.d.ts +8 -0
- package/dist/services/hosting.d.ts.map +1 -1
- package/dist/services/hosting.js +13 -4
- package/dist/services/index.d.ts +9 -2
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +9 -2
- package/dist/services/level-height.d.ts +27 -0
- package/dist/services/level-height.d.ts.map +1 -0
- package/dist/services/level-height.js +94 -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/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/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/snap.d.ts +9 -1
- package/dist/services/snap.d.ts.map +1 -1
- package/dist/services/snap.js +21 -1
- package/dist/services/storey.d.ts +103 -0
- package/dist/services/storey.d.ts.map +1 -0
- package/dist/services/storey.js +296 -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/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/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +141 -4
- package/dist/store/history-control.d.ts +31 -0
- package/dist/store/history-control.d.ts.map +1 -1
- package/dist/store/history-control.js +130 -0
- package/dist/store/use-live-terrain.d.ts +65 -0
- package/dist/store/use-live-terrain.d.ts.map +1 -0
- package/dist/store/use-live-terrain.js +31 -0
- package/dist/store/use-live-transforms.d.ts +8 -0
- package/dist/store/use-live-transforms.d.ts.map +1 -1
- package/dist/store/use-scene.d.ts +47 -3
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +992 -55
- package/dist/systems/elevator/elevator-opening-system.d.ts +1 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-opening-system.js +27 -21
- package/dist/systems/elevator/elevator-service.d.ts +0 -2
- package/dist/systems/elevator/elevator-service.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-service.js +2 -24
- 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/slab/slab-placement.d.ts +11 -0
- package/dist/systems/slab/slab-placement.d.ts.map +1 -0
- package/dist/systems/slab/slab-placement.js +14 -0
- package/dist/systems/slab/slab-support.d.ts +137 -0
- package/dist/systems/slab/slab-support.d.ts.map +1 -0
- package/dist/systems/slab/slab-support.js +557 -0
- package/dist/systems/stair/stair-footprint.d.ts.map +1 -1
- package/dist/systems/stair/stair-footprint.js +21 -11
- package/dist/systems/stair/stair-opening-preview.d.ts +1300 -505
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-sync.js +19 -33
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-system.js +32 -4
- package/dist/systems/stair/stair-rise.d.ts +22 -0
- package/dist/systems/stair/stair-rise.d.ts.map +1 -0
- package/dist/systems/stair/stair-rise.js +79 -0
- package/dist/systems/wall/wall-curve.d.ts +10 -0
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +1 -1
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +44 -8
- package/dist/systems/wall/wall-top.d.ts +40 -0
- package/dist/systems/wall/wall-top.d.ts.map +1 -0
- package/dist/systems/wall/wall-top.js +46 -0
- package/dist/utils/clone-scene-graph.d.ts +1 -0
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +56 -5
- 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 +106 -0
- package/dist/validation/validate-build-json.d.ts +3 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -1
- package/dist/validation/validate-build-json.js +90 -5
- package/package.json +4 -4
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +0 -2
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +0 -1
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +0 -273
- package/dist/lib/space-detection-pause.test.d.ts +0 -2
- package/dist/lib/space-detection-pause.test.d.ts.map +0 -1
- package/dist/lib/space-detection-pause.test.js +0 -42
- package/dist/lib/space-detection.test.d.ts +0 -2
- package/dist/lib/space-detection.test.d.ts.map +0 -1
- package/dist/lib/space-detection.test.js +0 -77
- package/dist/registry/registry.test.d.ts +0 -2
- package/dist/registry/registry.test.d.ts.map +0 -1
- package/dist/registry/registry.test.js +0 -183
- package/dist/registry/relations-resolver.test.d.ts +0 -2
- package/dist/registry/relations-resolver.test.d.ts.map +0 -1
- package/dist/registry/relations-resolver.test.js +0 -183
- package/dist/registry/scene-api.test.d.ts +0 -2
- package/dist/registry/scene-api.test.d.ts.map +0 -1
- package/dist/registry/scene-api.test.js +0 -183
- package/dist/registry/subtree.test.d.ts +0 -2
- package/dist/registry/subtree.test.d.ts.map +0 -1
- package/dist/registry/subtree.test.js +0 -133
- package/dist/schema/asset-url.test.d.ts +0 -2
- package/dist/schema/asset-url.test.d.ts.map +0 -1
- package/dist/schema/asset-url.test.js +0 -134
- package/dist/services/alignment-anchors.test.d.ts +0 -2
- package/dist/services/alignment-anchors.test.d.ts.map +0 -1
- package/dist/services/alignment-anchors.test.js +0 -383
- package/dist/services/alignment.test.d.ts +0 -2
- package/dist/services/alignment.test.d.ts.map +0 -1
- package/dist/services/alignment.test.js +0 -92
- package/dist/services/drag-session.test.d.ts +0 -2
- package/dist/services/drag-session.test.d.ts.map +0 -1
- package/dist/services/drag-session.test.js +0 -219
- package/dist/services/hosting.test.d.ts +0 -2
- package/dist/services/hosting.test.d.ts.map +0 -1
- package/dist/services/hosting.test.js +0 -199
- package/dist/services/movement.test.d.ts +0 -2
- package/dist/services/movement.test.d.ts.map +0 -1
- package/dist/services/movement.test.js +0 -133
- package/dist/services/single-undo-dance.test.d.ts +0 -2
- package/dist/services/single-undo-dance.test.d.ts.map +0 -1
- package/dist/services/single-undo-dance.test.js +0 -210
- package/dist/services/snap.test.d.ts +0 -2
- package/dist/services/snap.test.d.ts.map +0 -1
- package/dist/services/snap.test.js +0 -95
- package/dist/store/actions/node-mutation-sanitize.test.d.ts +0 -2
- package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +0 -1
- package/dist/store/actions/node-mutation-sanitize.test.js +0 -147
- package/dist/store/actions/reparent.test.d.ts +0 -2
- package/dist/store/actions/reparent.test.d.ts.map +0 -1
- package/dist/store/actions/reparent.test.js +0 -212
- package/dist/store/use-scene-elevator-migration.test.d.ts +0 -2
- package/dist/store/use-scene-elevator-migration.test.d.ts.map +0 -1
- package/dist/store/use-scene-elevator-migration.test.js +0 -109
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts +0 -2
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +0 -1
- package/dist/systems/elevator/elevator-opening-sync.test.js +0 -146
- package/dist/systems/elevator/elevator-runtime.test.d.ts +0 -2
- package/dist/systems/elevator/elevator-runtime.test.d.ts.map +0 -1
- package/dist/systems/elevator/elevator-runtime.test.js +0 -64
- package/dist/systems/stair/stair-opening-preview.test.d.ts +0 -2
- package/dist/systems/stair/stair-opening-preview.test.d.ts.map +0 -1
- package/dist/systems/stair/stair-opening-preview.test.js +0 -80
- package/dist/systems/stair/stair-opening-sync.test.d.ts +0 -2
- package/dist/systems/stair/stair-opening-sync.test.d.ts.map +0 -1
- package/dist/systems/stair/stair-opening-sync.test.js +0 -446
- package/dist/utils/clone-scene-graph.test.d.ts +0 -2
- package/dist/utils/clone-scene-graph.test.d.ts.map +0 -1
- package/dist/utils/clone-scene-graph.test.js +0 -59
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyNode, AnyNodeId } from '../../schema';
|
|
1
|
+
import type { AnyNode, AnyNodeId, LevelNode, SlabNode } from '../../schema';
|
|
2
2
|
export declare function resolveLevelId(node: AnyNode, nodes: Record<string, AnyNode>): string;
|
|
3
3
|
/**
|
|
4
4
|
* Walks the parent chain of `nodeId` and returns the id of the first ancestor
|
|
@@ -28,4 +28,49 @@ export declare function findLevelAncestorId(nodeId: AnyNodeId, nodes: Record<str
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function resolveBuildingForLevel(levelId: AnyNodeId, nodes: Record<AnyNodeId, AnyNode>): AnyNodeId | null;
|
|
30
30
|
export declare function initSpatialGridSync(): () => void;
|
|
31
|
+
/**
|
|
32
|
+
* A level's stored height moved: plane-bound walls follow the new plane,
|
|
33
|
+
* stair rise re-derives, and ceilings/fences re-resolve their clamp — mark
|
|
34
|
+
* them all so their systems rebuild. Restacking the level containers alone
|
|
35
|
+
* leaves their geometry stale.
|
|
36
|
+
*/
|
|
37
|
+
export declare function markLevelHeightDependents(level: LevelNode, nodes: Record<string, AnyNode>, markDirty: (id: AnyNodeId) => void): void;
|
|
38
|
+
/**
|
|
39
|
+
* A deck slab's walking surface moved: stairs attached to it via
|
|
40
|
+
* `deckSlabId` derive their rise from that elevation, so their geometry
|
|
41
|
+
* (and rise-derived affordances) must rebuild.
|
|
42
|
+
*/
|
|
43
|
+
export declare function markDeckAttachedStairs(slabId: string, nodes: Record<string, AnyNode>, markDirty: (id: AnyNodeId) => void): void;
|
|
44
|
+
/**
|
|
45
|
+
* Dirty every consumer of a slab's top or underside. Kept pure so committed
|
|
46
|
+
* scene writes and live handle previews use the same dependency boundary.
|
|
47
|
+
*/
|
|
48
|
+
export declare function markSlabChangeDependents(previous: SlabNode, next: SlabNode, nodes: Record<string, AnyNode>, markDirty: (id: AnyNodeId) => void): void;
|
|
49
|
+
/**
|
|
50
|
+
* The sculpted ground moved: every node the terrain *supports* must re-elevate.
|
|
51
|
+
*
|
|
52
|
+
* The other rules in this file gate on a footprint overlapping the changed
|
|
53
|
+
* surface. Terrain has no such gate — a stroke rewrites a field that spans the
|
|
54
|
+
* whole lot, and the resolver samples it at each node's own XZ — so the sweep is
|
|
55
|
+
* every floor-placed node on a storey at grade, plus every wall whose explicit
|
|
56
|
+
* terrain infill samples that field. During a live stroke it fires per dab, but
|
|
57
|
+
* dirty ids coalesce in a Set until the frame systems consume them; the scene
|
|
58
|
+
* graph itself is still written only once on commit.
|
|
59
|
+
*
|
|
60
|
+
* Without this a sculpt silently desyncs the scene from its own ground. Nothing
|
|
61
|
+
* re-runs `getFloorPlacedElevation`, so the React commit that rebinds a node
|
|
62
|
+
* group's base Y leaves it there: a column that was resting on a hillside drops
|
|
63
|
+
* to the datum and stays buried under the terrain it used to stand on.
|
|
64
|
+
*
|
|
65
|
+
* Gated on `isLevelAtSiteDatum` — the same predicate `terrainSupportLift` uses to
|
|
66
|
+
* decide whether it drapes at all, so the two cannot disagree about which storey
|
|
67
|
+
* is on the ground.
|
|
68
|
+
*/
|
|
69
|
+
export declare function markTerrainSupportDependents(nodes: Record<string, AnyNode>, markDirty: (id: AnyNodeId) => void): void;
|
|
70
|
+
/**
|
|
71
|
+
* A slab on `slabLevelId` was created/deleted or changed shape/placement:
|
|
72
|
+
* the covering bound (slab underside) over the level BELOW moved, so that
|
|
73
|
+
* level's plane-bound walls and clamped ceilings must rebuild.
|
|
74
|
+
*/
|
|
75
|
+
export declare function markCoveringDependentsBelow(slabLevelId: string, nodes: Record<string, AnyNode>, markDirty: (id: AnyNodeId) => void): void;
|
|
31
76
|
//# sourceMappingURL=spatial-grid-sync.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spatial-grid-sync.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/spatial-grid-sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"spatial-grid-sync.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/spatial-grid-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAY,QAAQ,EAAY,MAAM,cAAc,CAAA;AAY/F,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAmBpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,GAAG,IAAI,CASf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,EAClB,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAChC,SAAS,GAAG,IAAI,CAgBlB;AAKD,wBAAgB,mBAAmB,IAAI,MAAM,IAAI,CAgIhD;AAMD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,QAcnC;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,QAOnC;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,QAqBnC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,QAoCnC;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,QAUnC"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { getRenderableSlabPolygon } from '../../lib/slab-polygon';
|
|
2
|
+
import { isLevelAtSiteDatum } from '../../lib/terrain-support';
|
|
1
3
|
import { nodeRegistry } from '../../registry';
|
|
4
|
+
import { getLevelBelow } from '../../services/storey';
|
|
5
|
+
import useLiveTerrain from '../../store/use-live-terrain';
|
|
2
6
|
import useScene from '../../store/use-scene';
|
|
3
7
|
import { getFloorPlacedFootprints } from './floor-placed-elevation';
|
|
4
8
|
import { itemOverlapsPolygon, spatialGridManager, wallOverlapsPolygon, } from './spatial-grid-manager';
|
|
9
|
+
import { GROUND_SUPPORT_ID } from './support-host-id';
|
|
5
10
|
export function resolveLevelId(node, nodes) {
|
|
6
11
|
// If the node itself is a level
|
|
7
12
|
if (node.type === 'level')
|
|
@@ -92,7 +97,7 @@ export function initSpatialGridSync() {
|
|
|
92
97
|
// 2. Then subscribe to future changes
|
|
93
98
|
const markDirty = (id) => store.getState().markDirty(id);
|
|
94
99
|
// Subscribe to all changes
|
|
95
|
-
const
|
|
100
|
+
const unsubscribeScene = store.subscribe((state, prevState) => {
|
|
96
101
|
// Detect added nodes
|
|
97
102
|
for (const [id, node] of Object.entries(state.nodes)) {
|
|
98
103
|
if (!prevState.nodes[id]) {
|
|
@@ -101,6 +106,13 @@ export function initSpatialGridSync() {
|
|
|
101
106
|
// When a slab is added, mark overlapping items/walls dirty
|
|
102
107
|
if (node.type === 'slab') {
|
|
103
108
|
markNodesOverlappingSlab(node, state.nodes, markDirty);
|
|
109
|
+
markCoveringDependentsBelow(levelId, state.nodes, markDirty);
|
|
110
|
+
}
|
|
111
|
+
// A site arriving with terrain already on it (scene load, paste,
|
|
112
|
+
// imported elevation data) is the same event as a stroke: ground exists
|
|
113
|
+
// where flat ground was assumed.
|
|
114
|
+
if (node.type === 'site' && node.terrain) {
|
|
115
|
+
markTerrainSupportDependents(state.nodes, markDirty);
|
|
104
116
|
}
|
|
105
117
|
}
|
|
106
118
|
}
|
|
@@ -112,6 +124,12 @@ export function initSpatialGridSync() {
|
|
|
112
124
|
// When a slab is removed, mark items/walls that were on it dirty (using current state)
|
|
113
125
|
if (node.type === 'slab') {
|
|
114
126
|
markNodesOverlappingSlab(node, state.nodes, markDirty);
|
|
127
|
+
markCoveringDependentsBelow(levelId, state.nodes, markDirty);
|
|
128
|
+
}
|
|
129
|
+
// Deleting a sculpted site drops the ground back to the datum, so its
|
|
130
|
+
// contents have to come down with it.
|
|
131
|
+
if (node.type === 'site' && node.terrain) {
|
|
132
|
+
markTerrainSupportDependents(state.nodes, markDirty);
|
|
115
133
|
}
|
|
116
134
|
}
|
|
117
135
|
}
|
|
@@ -135,23 +153,186 @@ export function initSpatialGridSync() {
|
|
|
135
153
|
}
|
|
136
154
|
}
|
|
137
155
|
else if (node.type === 'slab' && prev.type === 'slab') {
|
|
138
|
-
|
|
156
|
+
const supportChanged = node.polygon !== prev.polygon ||
|
|
139
157
|
node.elevation !== prev.elevation ||
|
|
140
|
-
node.holes !== prev.holes
|
|
158
|
+
node.holes !== prev.holes;
|
|
159
|
+
if (supportChanged) {
|
|
141
160
|
const levelId = resolveLevelId(node, state.nodes);
|
|
142
161
|
spatialGridManager.handleNodeUpdated(node, levelId);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
}
|
|
163
|
+
markSlabChangeDependents(prev, node, state.nodes, markDirty);
|
|
164
|
+
}
|
|
165
|
+
else if (node.type === 'level' && prev.type === 'level') {
|
|
166
|
+
if (node.height !== prev.height) {
|
|
167
|
+
markLevelHeightDependents(node, state.nodes, markDirty);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (node.type === 'site' && prev.type === 'site') {
|
|
171
|
+
// Object identity, not deep equality: the store is
|
|
172
|
+
// immutable-by-convention, so a sculpt commit necessarily produces a new
|
|
173
|
+
// `terrain` object and an unrelated site edit (polygon, name) keeps the
|
|
174
|
+
// old one. The same reasoning `terrain-source`'s field cache is keyed on.
|
|
175
|
+
if (node.terrain !== prev.terrain) {
|
|
176
|
+
markTerrainSupportDependents(state.nodes, markDirty);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else if (node.type === 'wall' && prev.type === 'wall') {
|
|
180
|
+
if (node.start !== prev.start ||
|
|
181
|
+
node.end !== prev.end ||
|
|
182
|
+
node.curveOffset !== prev.curveOffset ||
|
|
183
|
+
node.thickness !== prev.thickness) {
|
|
184
|
+
// Rendered slab polygons adopt wall bands, so a wall reshape
|
|
185
|
+
// must reach the manager to refresh its wall map and drop the
|
|
186
|
+
// level's rendered-polygon cache.
|
|
187
|
+
spatialGridManager.handleNodeUpdated(node, resolveLevelId(node, state.nodes));
|
|
146
188
|
}
|
|
147
189
|
}
|
|
148
190
|
}
|
|
149
191
|
});
|
|
150
|
-
|
|
192
|
+
// Live terrain is deliberately not written into `useScene` per dab: doing so
|
|
193
|
+
// would encode the whole field, flood history, and wake every scene subscriber.
|
|
194
|
+
// Reuse the committed-terrain dependency sweep against the transient field
|
|
195
|
+
// instead. Dirty marks coalesce in their Set until the next frame, while an
|
|
196
|
+
// `end` notification also restores every dependent after an abandoned stroke.
|
|
197
|
+
const unsubscribeLiveTerrain = useLiveTerrain.subscribe(() => {
|
|
198
|
+
markTerrainSupportDependents(store.getState().nodes, markDirty);
|
|
199
|
+
});
|
|
200
|
+
return () => {
|
|
201
|
+
unsubscribeScene();
|
|
202
|
+
unsubscribeLiveTerrain();
|
|
203
|
+
};
|
|
151
204
|
}
|
|
152
205
|
function arraysEqual(a, b) {
|
|
153
206
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
154
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* A level's stored height moved: plane-bound walls follow the new plane,
|
|
210
|
+
* stair rise re-derives, and ceilings/fences re-resolve their clamp — mark
|
|
211
|
+
* them all so their systems rebuild. Restacking the level containers alone
|
|
212
|
+
* leaves their geometry stale.
|
|
213
|
+
*/
|
|
214
|
+
export function markLevelHeightDependents(level, nodes, markDirty) {
|
|
215
|
+
for (const childId of level.children) {
|
|
216
|
+
const child = nodes[childId];
|
|
217
|
+
if (!child)
|
|
218
|
+
continue;
|
|
219
|
+
if (child.type === 'wall' ||
|
|
220
|
+
child.type === 'stair' ||
|
|
221
|
+
child.type === 'ceiling' ||
|
|
222
|
+
child.type === 'fence') {
|
|
223
|
+
markDirty(child.id);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* A deck slab's walking surface moved: stairs attached to it via
|
|
229
|
+
* `deckSlabId` derive their rise from that elevation, so their geometry
|
|
230
|
+
* (and rise-derived affordances) must rebuild.
|
|
231
|
+
*/
|
|
232
|
+
export function markDeckAttachedStairs(slabId, nodes, markDirty) {
|
|
233
|
+
for (const node of Object.values(nodes)) {
|
|
234
|
+
if (node.type === 'stair' && node.deckSlabId === slabId) {
|
|
235
|
+
markDirty(node.id);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Dirty every consumer of a slab's top or underside. Kept pure so committed
|
|
241
|
+
* scene writes and live handle previews use the same dependency boundary.
|
|
242
|
+
*/
|
|
243
|
+
export function markSlabChangeDependents(previous, next, nodes, markDirty) {
|
|
244
|
+
const supportChanged = next.polygon !== previous.polygon ||
|
|
245
|
+
next.elevation !== previous.elevation ||
|
|
246
|
+
next.holes !== previous.holes;
|
|
247
|
+
if (supportChanged) {
|
|
248
|
+
markNodesOverlappingSlab(previous, nodes, markDirty);
|
|
249
|
+
markNodesOverlappingSlab(next, nodes, markDirty);
|
|
250
|
+
}
|
|
251
|
+
if (next.elevation !== previous.elevation) {
|
|
252
|
+
markDeckAttachedStairs(next.id, nodes, markDirty);
|
|
253
|
+
}
|
|
254
|
+
if (supportChanged ||
|
|
255
|
+
next.thickness !== previous.thickness ||
|
|
256
|
+
next.recessed !== previous.recessed) {
|
|
257
|
+
markCoveringDependentsBelow(resolveLevelId(next, nodes), nodes, markDirty);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* The sculpted ground moved: every node the terrain *supports* must re-elevate.
|
|
262
|
+
*
|
|
263
|
+
* The other rules in this file gate on a footprint overlapping the changed
|
|
264
|
+
* surface. Terrain has no such gate — a stroke rewrites a field that spans the
|
|
265
|
+
* whole lot, and the resolver samples it at each node's own XZ — so the sweep is
|
|
266
|
+
* every floor-placed node on a storey at grade, plus every wall whose explicit
|
|
267
|
+
* terrain infill samples that field. During a live stroke it fires per dab, but
|
|
268
|
+
* dirty ids coalesce in a Set until the frame systems consume them; the scene
|
|
269
|
+
* graph itself is still written only once on commit.
|
|
270
|
+
*
|
|
271
|
+
* Without this a sculpt silently desyncs the scene from its own ground. Nothing
|
|
272
|
+
* re-runs `getFloorPlacedElevation`, so the React commit that rebinds a node
|
|
273
|
+
* group's base Y leaves it there: a column that was resting on a hillside drops
|
|
274
|
+
* to the datum and stays buried under the terrain it used to stand on.
|
|
275
|
+
*
|
|
276
|
+
* Gated on `isLevelAtSiteDatum` — the same predicate `terrainSupportLift` uses to
|
|
277
|
+
* decide whether it drapes at all, so the two cannot disagree about which storey
|
|
278
|
+
* is on the ground.
|
|
279
|
+
*/
|
|
280
|
+
export function markTerrainSupportDependents(nodes, markDirty) {
|
|
281
|
+
const gradeLevels = new Map();
|
|
282
|
+
const isGrade = (levelId) => {
|
|
283
|
+
let cached = gradeLevels.get(levelId);
|
|
284
|
+
if (cached === undefined) {
|
|
285
|
+
cached = isLevelAtSiteDatum(nodes, levelId);
|
|
286
|
+
gradeLevels.set(levelId, cached);
|
|
287
|
+
}
|
|
288
|
+
return cached;
|
|
289
|
+
};
|
|
290
|
+
for (const node of Object.values(nodes)) {
|
|
291
|
+
if (node.type === 'slab' && node.fillToTerrain === true) {
|
|
292
|
+
if (isGrade(resolveLevelId(node, nodes)))
|
|
293
|
+
markDirty(node.id);
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (node.type === 'wall') {
|
|
297
|
+
if (node.supportSlabId !== GROUND_SUPPORT_ID && node.fillToTerrain !== true)
|
|
298
|
+
continue;
|
|
299
|
+
if (!isGrade(resolveLevelId(node, nodes)))
|
|
300
|
+
continue;
|
|
301
|
+
markDirty(node.id);
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
const floorPlaced = nodeRegistry.get(node.type)?.capabilities?.floorPlaced;
|
|
305
|
+
if (!floorPlaced)
|
|
306
|
+
continue;
|
|
307
|
+
if (floorPlaced.applies && !floorPlaced.applies(node))
|
|
308
|
+
continue;
|
|
309
|
+
// Items hosted on a shelf or table inherit Y from the parent group; only
|
|
310
|
+
// level-parented nodes read the ground. Mirrors the resolver's own gate.
|
|
311
|
+
const parentId = node.parentId;
|
|
312
|
+
const parent = parentId ? nodes[parentId] : null;
|
|
313
|
+
if (parent && parent.type !== 'level')
|
|
314
|
+
continue;
|
|
315
|
+
if (!isGrade(resolveLevelId(node, nodes)))
|
|
316
|
+
continue;
|
|
317
|
+
markDirty(node.id);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* A slab on `slabLevelId` was created/deleted or changed shape/placement:
|
|
322
|
+
* the covering bound (slab underside) over the level BELOW moved, so that
|
|
323
|
+
* level's plane-bound walls and clamped ceilings must rebuild.
|
|
324
|
+
*/
|
|
325
|
+
export function markCoveringDependentsBelow(slabLevelId, nodes, markDirty) {
|
|
326
|
+
const below = getLevelBelow(slabLevelId, nodes);
|
|
327
|
+
if (!below)
|
|
328
|
+
return;
|
|
329
|
+
for (const childId of below.children) {
|
|
330
|
+
const child = nodes[childId];
|
|
331
|
+
if (child?.type === 'wall' || child?.type === 'ceiling') {
|
|
332
|
+
markDirty(child.id);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
155
336
|
/**
|
|
156
337
|
* Mark all floor items and walls that may be affected by a slab change as dirty.
|
|
157
338
|
*/
|
|
@@ -159,6 +340,24 @@ function markNodesOverlappingSlab(slab, nodes, markDirty) {
|
|
|
159
340
|
if (slab.polygon.length < 3)
|
|
160
341
|
return;
|
|
161
342
|
const slabLevelId = resolveLevelId(slab, nodes);
|
|
343
|
+
// Walls AND floor-placed nodes follow the slab's RENDERED footprint
|
|
344
|
+
// (band-adopted edges reach the wall's outer face), so the dirty gate
|
|
345
|
+
// must test the same polygon the support queries re-evaluate — a stored
|
|
346
|
+
// polygon that stops short of the wall body would otherwise never
|
|
347
|
+
// re-elevate nodes sitting over the adopted band.
|
|
348
|
+
const levelWalls = [];
|
|
349
|
+
const siblingSlabs = [];
|
|
350
|
+
for (const node of Object.values(nodes)) {
|
|
351
|
+
if (node.type === 'wall' && resolveLevelId(node, nodes) === slabLevelId) {
|
|
352
|
+
levelWalls.push(node);
|
|
353
|
+
}
|
|
354
|
+
else if (node.type === 'slab' &&
|
|
355
|
+
node.id !== slab.id &&
|
|
356
|
+
resolveLevelId(node, nodes) === slabLevelId) {
|
|
357
|
+
siblingSlabs.push(node);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const renderedPolygon = getRenderableSlabPolygon(slab, { walls: levelWalls, siblingSlabs });
|
|
162
361
|
for (const node of Object.values(nodes)) {
|
|
163
362
|
if (node.type === 'wall') {
|
|
164
363
|
const wall = node;
|
|
@@ -169,7 +368,7 @@ function markNodesOverlappingSlab(slab, nodes, markDirty) {
|
|
|
169
368
|
end: wall.end,
|
|
170
369
|
curveOffset: wall.curveOffset ?? 0,
|
|
171
370
|
thickness: wall.thickness,
|
|
172
|
-
},
|
|
371
|
+
}, renderedPolygon)) {
|
|
173
372
|
markDirty(node.id);
|
|
174
373
|
}
|
|
175
374
|
continue;
|
|
@@ -195,7 +394,7 @@ function markNodesOverlappingSlab(slab, nodes, markDirty) {
|
|
|
195
394
|
if (!position)
|
|
196
395
|
continue;
|
|
197
396
|
for (const footprint of getFloorPlacedFootprints(floorPlaced, node, { nodes })) {
|
|
198
|
-
if (itemOverlapsPolygon(footprint.position ?? position, footprint.dimensions, footprint.rotation,
|
|
397
|
+
if (itemOverlapsPolygon(footprint.position ?? position, footprint.dimensions, footprint.rotation, renderedPolygon, 0.01)) {
|
|
199
398
|
markDirty(node.id);
|
|
200
399
|
break;
|
|
201
400
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"support-host-id.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/support-host-id.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,WAAW,CAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AnyNode, FenceNode, WallNode } from '../../schema';
|
|
2
|
+
export type SupportSlabPatch = {
|
|
3
|
+
supportSlabId: string | undefined;
|
|
4
|
+
};
|
|
5
|
+
export type SupportSlabPatchOptions = {
|
|
6
|
+
/**
|
|
7
|
+
* Pointer-decided support cap (level-local Y) — see
|
|
8
|
+
* `FloorPlacedElevationArgs.maxElevation`. When set, the persisted host
|
|
9
|
+
* reproduces the CAPPED election: the elected lower slab wins over a
|
|
10
|
+
* deck hanging above the cap, and `GROUND_SUPPORT_ID` is stored when the
|
|
11
|
+
* ground is elected while capped-out slabs still overlap the footprint.
|
|
12
|
+
*/
|
|
13
|
+
maxElevation?: number | null;
|
|
14
|
+
/** Pointer- or snap-decided host. Ground is a first-class support source. */
|
|
15
|
+
preferredSlabId?: string | null;
|
|
16
|
+
};
|
|
17
|
+
export declare function resolveSupportSlabPatch(node: AnyNode, nodes: Record<string, AnyNode>, options?: SupportSlabPatchOptions): SupportSlabPatch;
|
|
18
|
+
export declare function resolveWallSupportSlabPatch(wall: WallNode, nodes: Record<string, AnyNode>, options?: SupportSlabPatchOptions): SupportSlabPatch;
|
|
19
|
+
/**
|
|
20
|
+
* Re-elect a moved wall without discarding an explicit construction source.
|
|
21
|
+
*
|
|
22
|
+
* Move tools change only the wall's plan geometry. A persisted host therefore
|
|
23
|
+
* remains preferred when it still supports the new footprint; the normal
|
|
24
|
+
* resolver falls back when a slab no longer overlaps. Ground is also an
|
|
25
|
+
* explicit source, so preserving it keeps terrain-hosted walls on terrain.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveMovedWallSupportSlabPatch(wall: WallNode, nodes: Record<string, AnyNode>): SupportSlabPatch;
|
|
28
|
+
/** Fence-like shape the fence host election needs — plain segment, arc, or spline. */
|
|
29
|
+
export type FenceSupportInput = Pick<FenceNode, 'start' | 'end' | 'curveOffset' | 'path' | 'thickness' | 'parentId'>;
|
|
30
|
+
/**
|
|
31
|
+
* Support-host patch for a fence: elect the slab the fence line stands on
|
|
32
|
+
* and persist it as `supportSlabId` (the fence lift resolves absent =
|
|
33
|
+
* level floor — see `packages/nodes/src/fence/lift.ts`).
|
|
34
|
+
*
|
|
35
|
+
* The centerline (chord, sampled arc, or spline path) is turned into thin
|
|
36
|
+
* band footprints and run through the same candidate machinery items use.
|
|
37
|
+
* `options.maxElevation` is the pointer-decided cap: aiming at the floor
|
|
38
|
+
* under a deck elects the floor, aiming at the deck top elects the deck.
|
|
39
|
+
*
|
|
40
|
+
* Persist rule: the items ambiguity rule (stacked candidates disagree)
|
|
41
|
+
* PLUS the elevated-host case — a winner sitting meaningfully above the
|
|
42
|
+
* level floor must be persisted even when unambiguous (a balcony deck with
|
|
43
|
+
* nothing underneath), or the commit loses the election entirely since
|
|
44
|
+
* fences run no per-frame election. A single default ground slab (its top
|
|
45
|
+
* within `SUPPORT_ELEVATION_EPSILON` of the floor) stays unpersisted so
|
|
46
|
+
* plain fences keep sitting at the level base. A capped-out election (all
|
|
47
|
+
* overlapping slabs above the aimed-at ground) also resolves to the floor
|
|
48
|
+
* via the same absent-host default. Pure; exported for tests.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveFenceSupportSlabPatch(fence: FenceSupportInput, nodes: Record<string, AnyNode>, options?: SupportSlabPatchOptions): SupportSlabPatch;
|
|
51
|
+
//# sourceMappingURL=support-host-patch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"support-host-patch.d.ts","sourceRoot":"","sources":["../../../src/hooks/spatial-grid/support-host-patch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAa,SAAS,EAAY,QAAQ,EAAE,MAAM,cAAc,CAAA;AAKrF,MAAM,MAAM,gBAAgB,GAAG;IAAE,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAEpE,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,CAAA;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,uBAAuB,GAChC,gBAAgB,CAiDlB;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,uBAAuB,GAChC,gBAAgB,CAiDlB;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,gBAAgB,CAIlB;AAED,sFAAsF;AACtF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAClC,SAAS,EACT,OAAO,GAAG,KAAK,GAAG,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,CACpE,CAAA;AA4BD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,iBAAiB,EACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,uBAAuB,GAChC,gBAAgB,CA4ClB"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { nodeRegistry } from '../../registry';
|
|
2
|
+
import { getWallCurveFrameAt, isCurvedWall } from '../../systems/wall/wall-curve';
|
|
3
|
+
import { GROUND_SUPPORT_ID, getFloorPlacedFootprints } from './floor-placed-elevation';
|
|
4
|
+
import { SUPPORT_ELEVATION_EPSILON, spatialGridManager } from './spatial-grid-manager';
|
|
5
|
+
export function resolveSupportSlabPatch(node, nodes, options) {
|
|
6
|
+
const floorPlaced = nodeRegistry.get(node.type)?.capabilities?.floorPlaced;
|
|
7
|
+
if (!floorPlaced || (floorPlaced.applies && !floorPlaced.applies(node))) {
|
|
8
|
+
return { supportSlabId: undefined };
|
|
9
|
+
}
|
|
10
|
+
const parentId = node.parentId ?? null;
|
|
11
|
+
const parent = parentId ? nodes[parentId] : null;
|
|
12
|
+
if (parent?.type !== 'level')
|
|
13
|
+
return { supportSlabId: undefined };
|
|
14
|
+
const maxElevation = options?.maxElevation;
|
|
15
|
+
const footprints = getFloorPlacedFootprints(floorPlaced, node, { nodes });
|
|
16
|
+
const candidateElevations = new Set();
|
|
17
|
+
let winner = null;
|
|
18
|
+
let cappedOut = false;
|
|
19
|
+
for (const footprint of footprints) {
|
|
20
|
+
const position = footprint.position ?? node.position;
|
|
21
|
+
if (!Array.isArray(position) || position.length !== 3)
|
|
22
|
+
continue;
|
|
23
|
+
const candidates = spatialGridManager.getSupportCandidatesForFootprint(parent.id, position, footprint.dimensions, footprint.rotation);
|
|
24
|
+
for (const candidate of candidates)
|
|
25
|
+
candidateElevations.add(candidate.elevation);
|
|
26
|
+
const support = spatialGridManager.getSlabSupportForItem(parent.id, position, footprint.dimensions, footprint.rotation, maxElevation);
|
|
27
|
+
if (support.slabId && (!winner || support.elevation > winner.elevation)) {
|
|
28
|
+
winner = { slabId: support.slabId, elevation: support.elevation };
|
|
29
|
+
}
|
|
30
|
+
if (maxElevation != null && support.slabId === null && candidates.length > 0) {
|
|
31
|
+
cappedOut = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (winner !== null) {
|
|
35
|
+
return { supportSlabId: candidateElevations.size >= 2 ? winner.slabId : undefined };
|
|
36
|
+
}
|
|
37
|
+
// Capped election chose the ground while overlapping slabs sit above the
|
|
38
|
+
// cap: persist the ground host, or the uncapped per-frame election would
|
|
39
|
+
// lift the committed node back onto the deck.
|
|
40
|
+
return { supportSlabId: cappedOut ? GROUND_SUPPORT_ID : undefined };
|
|
41
|
+
}
|
|
42
|
+
export function resolveWallSupportSlabPatch(wall, nodes, options) {
|
|
43
|
+
const parent = wall.parentId ? nodes[wall.parentId] : null;
|
|
44
|
+
if (parent?.type !== 'level')
|
|
45
|
+
return { supportSlabId: undefined };
|
|
46
|
+
if (options?.preferredSlabId === GROUND_SUPPORT_ID) {
|
|
47
|
+
return { supportSlabId: GROUND_SUPPORT_ID };
|
|
48
|
+
}
|
|
49
|
+
// Winner under the pointer cap (when given): a deck hanging above the
|
|
50
|
+
// aimed-at surface can't capture the elected base, so a wall drawn at the
|
|
51
|
+
// floor underneath it persists the floor slab the user actually targeted.
|
|
52
|
+
const support = spatialGridManager.getSlabSupportForWall(parent.id, wall.start, wall.end, wall.curveOffset, wall.thickness, options?.preferredSlabId, options?.maxElevation);
|
|
53
|
+
const candidateElevations = new Set();
|
|
54
|
+
for (const node of Object.values(nodes)) {
|
|
55
|
+
if (node.type !== 'slab' || node.parentId !== parent.id)
|
|
56
|
+
continue;
|
|
57
|
+
const candidate = node;
|
|
58
|
+
const preferred = spatialGridManager.getSlabSupportForWall(parent.id, wall.start, wall.end, wall.curveOffset, wall.thickness, candidate.id);
|
|
59
|
+
if (preferred.electedSlabId === candidate.id) {
|
|
60
|
+
candidateElevations.add(candidate.elevation);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (support.electedSlabId === options?.preferredSlabId) {
|
|
64
|
+
return { supportSlabId: support.electedSlabId ?? undefined };
|
|
65
|
+
}
|
|
66
|
+
if (options?.maxElevation != null &&
|
|
67
|
+
support.electedSlabId === null &&
|
|
68
|
+
candidateElevations.size > 0) {
|
|
69
|
+
return { supportSlabId: GROUND_SUPPORT_ID };
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
supportSlabId: candidateElevations.size >= 2 ? (support.electedSlabId ?? undefined) : undefined,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Re-elect a moved wall without discarding an explicit construction source.
|
|
77
|
+
*
|
|
78
|
+
* Move tools change only the wall's plan geometry. A persisted host therefore
|
|
79
|
+
* remains preferred when it still supports the new footprint; the normal
|
|
80
|
+
* resolver falls back when a slab no longer overlaps. Ground is also an
|
|
81
|
+
* explicit source, so preserving it keeps terrain-hosted walls on terrain.
|
|
82
|
+
*/
|
|
83
|
+
export function resolveMovedWallSupportSlabPatch(wall, nodes) {
|
|
84
|
+
return resolveWallSupportSlabPatch(wall, nodes, {
|
|
85
|
+
preferredSlabId: wall.supportSlabId ?? null,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** Sample count for a curved (sagitta) fence centerline, matching the wall band test. */
|
|
89
|
+
const FENCE_CURVE_SUPPORT_SAMPLES = 16;
|
|
90
|
+
/** Fallback fence thickness (schema default) when the node carries none. */
|
|
91
|
+
const DEFAULT_FENCE_THICKNESS = 0.08;
|
|
92
|
+
/** Minimum band depth so the footprint survives the election's polygon inset. */
|
|
93
|
+
const MIN_FENCE_SUPPORT_BAND = 0.05;
|
|
94
|
+
function fenceCenterlinePoints(fence) {
|
|
95
|
+
if (fence.path && fence.path.length >= 2) {
|
|
96
|
+
return fence.path.map((point) => [point[0], point[1]]);
|
|
97
|
+
}
|
|
98
|
+
const wallLike = { start: fence.start, end: fence.end, curveOffset: fence.curveOffset ?? 0 };
|
|
99
|
+
if ((fence.curveOffset ?? 0) !== 0 && isCurvedWall(wallLike)) {
|
|
100
|
+
const points = [];
|
|
101
|
+
for (let i = 0; i <= FENCE_CURVE_SUPPORT_SAMPLES; i++) {
|
|
102
|
+
const frame = getWallCurveFrameAt(wallLike, i / FENCE_CURVE_SUPPORT_SAMPLES);
|
|
103
|
+
points.push([frame.point.x, frame.point.y]);
|
|
104
|
+
}
|
|
105
|
+
return points;
|
|
106
|
+
}
|
|
107
|
+
return [
|
|
108
|
+
[fence.start[0], fence.start[1]],
|
|
109
|
+
[fence.end[0], fence.end[1]],
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Support-host patch for a fence: elect the slab the fence line stands on
|
|
114
|
+
* and persist it as `supportSlabId` (the fence lift resolves absent =
|
|
115
|
+
* level floor — see `packages/nodes/src/fence/lift.ts`).
|
|
116
|
+
*
|
|
117
|
+
* The centerline (chord, sampled arc, or spline path) is turned into thin
|
|
118
|
+
* band footprints and run through the same candidate machinery items use.
|
|
119
|
+
* `options.maxElevation` is the pointer-decided cap: aiming at the floor
|
|
120
|
+
* under a deck elects the floor, aiming at the deck top elects the deck.
|
|
121
|
+
*
|
|
122
|
+
* Persist rule: the items ambiguity rule (stacked candidates disagree)
|
|
123
|
+
* PLUS the elevated-host case — a winner sitting meaningfully above the
|
|
124
|
+
* level floor must be persisted even when unambiguous (a balcony deck with
|
|
125
|
+
* nothing underneath), or the commit loses the election entirely since
|
|
126
|
+
* fences run no per-frame election. A single default ground slab (its top
|
|
127
|
+
* within `SUPPORT_ELEVATION_EPSILON` of the floor) stays unpersisted so
|
|
128
|
+
* plain fences keep sitting at the level base. A capped-out election (all
|
|
129
|
+
* overlapping slabs above the aimed-at ground) also resolves to the floor
|
|
130
|
+
* via the same absent-host default. Pure; exported for tests.
|
|
131
|
+
*/
|
|
132
|
+
export function resolveFenceSupportSlabPatch(fence, nodes, options) {
|
|
133
|
+
const parent = fence.parentId ? nodes[fence.parentId] : null;
|
|
134
|
+
if (parent?.type !== 'level')
|
|
135
|
+
return { supportSlabId: undefined };
|
|
136
|
+
const maxElevation = options?.maxElevation;
|
|
137
|
+
const band = Math.max(fence.thickness ?? DEFAULT_FENCE_THICKNESS, MIN_FENCE_SUPPORT_BAND);
|
|
138
|
+
const points = fenceCenterlinePoints(fence);
|
|
139
|
+
const candidateElevations = new Set();
|
|
140
|
+
let winner = null;
|
|
141
|
+
for (let i = 1; i < points.length; i++) {
|
|
142
|
+
const [ax, az] = points[i - 1];
|
|
143
|
+
const [bx, bz] = points[i];
|
|
144
|
+
const length = Math.hypot(bx - ax, bz - az);
|
|
145
|
+
if (length < 1e-6)
|
|
146
|
+
continue;
|
|
147
|
+
const position = [(ax + bx) / 2, 0, (az + bz) / 2];
|
|
148
|
+
const dimensions = [length, 1, band];
|
|
149
|
+
// getItemFootprint's rotation convention: local +X maps to
|
|
150
|
+
// (cos yRot, sin yRot) in XZ, so the segment angle aligns the band.
|
|
151
|
+
const rotation = [0, Math.atan2(bz - az, bx - ax), 0];
|
|
152
|
+
const candidates = spatialGridManager.getSupportCandidatesForFootprint(parent.id, position, dimensions, rotation);
|
|
153
|
+
for (const candidate of candidates)
|
|
154
|
+
candidateElevations.add(candidate.elevation);
|
|
155
|
+
const support = spatialGridManager.getSlabSupportForItem(parent.id, position, dimensions, rotation, maxElevation);
|
|
156
|
+
if (support.slabId && (!winner || support.elevation > winner.elevation)) {
|
|
157
|
+
winner = { slabId: support.slabId, elevation: support.elevation };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (winner === null)
|
|
161
|
+
return { supportSlabId: undefined };
|
|
162
|
+
const persist = candidateElevations.size >= 2 || winner.elevation > SUPPORT_ELEVATION_EPSILON;
|
|
163
|
+
return { supportSlabId: persist ? winner.slabId : undefined };
|
|
164
|
+
}
|