@pascal-app/core 0.8.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/events/bus.d.ts +67 -13
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/scene-registry/item-clip-registry.d.ts +18 -0
- package/dist/hooks/scene-registry/item-clip-registry.d.ts.map +1 -0
- package/dist/hooks/scene-registry/item-clip-registry.js +10 -0
- package/dist/hooks/scene-registry/scene-registry.d.ts +14 -23
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +59 -26
- package/dist/hooks/scene-registry/scene-registry.test.d.ts +2 -0
- package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +1 -0
- package/dist/hooks/scene-registry/scene-registry.test.js +26 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +366 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +79 -11
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +508 -141
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +109 -18
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +363 -0
- package/dist/index.d.ts +36 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -6
- package/dist/lib/level-name.d.ts +4 -0
- package/dist/lib/level-name.d.ts.map +1 -0
- package/dist/lib/level-name.js +10 -0
- package/dist/lib/measurement-geometry.d.ts +19 -0
- package/dist/lib/measurement-geometry.d.ts.map +1 -0
- package/dist/lib/measurement-geometry.js +232 -0
- package/dist/lib/measurement-geometry.test.d.ts +2 -0
- package/dist/lib/measurement-geometry.test.d.ts.map +1 -0
- package/dist/lib/measurement-geometry.test.js +102 -0
- package/dist/lib/polygon-geometry.d.ts +0 -1
- package/dist/lib/polygon-geometry.d.ts.map +1 -1
- package/dist/lib/polygon-geometry.js +0 -18
- package/dist/lib/polygon-relations.d.ts +10 -0
- package/dist/lib/polygon-relations.d.ts.map +1 -0
- package/dist/lib/polygon-relations.js +73 -0
- package/dist/lib/selection-proxy.d.ts +4 -0
- package/dist/lib/selection-proxy.d.ts.map +1 -0
- package/dist/lib/selection-proxy.js +12 -0
- package/dist/lib/slab-polygon.d.ts +31 -2
- package/dist/lib/slab-polygon.d.ts.map +1 -1
- package/dist/lib/slab-polygon.js +525 -31
- package/dist/lib/slab-polygon.test.d.ts +2 -0
- package/dist/lib/slab-polygon.test.d.ts.map +1 -0
- package/dist/lib/slab-polygon.test.js +635 -0
- package/dist/lib/slots.d.ts +14 -0
- package/dist/lib/slots.d.ts.map +1 -0
- package/dist/lib/slots.js +26 -0
- package/dist/lib/space-detection-pause.test.d.ts +2 -0
- package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
- package/dist/lib/space-detection-pause.test.js +42 -0
- package/dist/lib/space-detection.d.ts +54 -2
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +599 -124
- package/dist/lib/space-detection.test.d.ts +2 -0
- package/dist/lib/space-detection.test.d.ts.map +1 -0
- package/dist/lib/space-detection.test.js +376 -0
- package/dist/lib/wall-distance.d.ts +60 -0
- package/dist/lib/wall-distance.d.ts.map +1 -0
- package/dist/lib/wall-distance.js +85 -0
- package/dist/lib/zone-quantities.d.ts +21 -0
- package/dist/lib/zone-quantities.d.ts.map +1 -0
- package/dist/lib/zone-quantities.js +183 -0
- package/dist/lib/zone-quantities.test.d.ts +2 -0
- package/dist/lib/zone-quantities.test.d.ts.map +1 -0
- package/dist/lib/zone-quantities.test.js +82 -0
- package/dist/material-library.d.ts +20 -1
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +3737 -200
- package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
- package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
- package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
- package/dist/registry/handles.d.ts +394 -0
- package/dist/registry/handles.d.ts.map +1 -0
- package/dist/registry/handles.js +24 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/registry.d.ts +136 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +282 -0
- package/dist/registry/registry.test.d.ts +2 -0
- package/dist/registry/registry.test.d.ts.map +1 -0
- package/dist/registry/registry.test.js +196 -0
- package/dist/registry/relations-resolver.d.ts +42 -0
- package/dist/registry/relations-resolver.d.ts.map +1 -0
- package/dist/registry/relations-resolver.js +77 -0
- package/dist/registry/relations-resolver.test.d.ts +2 -0
- package/dist/registry/relations-resolver.test.d.ts.map +1 -0
- package/dist/registry/relations-resolver.test.js +183 -0
- package/dist/registry/scene-api.d.ts +44 -0
- package/dist/registry/scene-api.d.ts.map +1 -0
- package/dist/registry/scene-api.js +111 -0
- package/dist/registry/scene-api.test.d.ts +2 -0
- package/dist/registry/scene-api.test.d.ts.map +1 -0
- package/dist/registry/scene-api.test.js +183 -0
- package/dist/registry/subtree.d.ts +67 -0
- package/dist/registry/subtree.d.ts.map +1 -0
- package/dist/registry/subtree.js +123 -0
- package/dist/registry/subtree.test.d.ts +2 -0
- package/dist/registry/subtree.test.d.ts.map +1 -0
- package/dist/registry/subtree.test.js +163 -0
- package/dist/registry/types.d.ts +2099 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +1 -0
- package/dist/schema/index.d.ts +35 -4
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +32 -4
- package/dist/schema/material.d.ts +13 -2
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +17 -5
- package/dist/schema/material.test.d.ts +2 -0
- package/dist/schema/material.test.d.ts.map +1 -0
- package/dist/schema/material.test.js +43 -0
- package/dist/schema/nodes/box-vent.d.ts +74 -0
- package/dist/schema/nodes/box-vent.d.ts.map +1 -0
- package/dist/schema/nodes/box-vent.js +63 -0
- package/dist/schema/nodes/building.d.ts +1 -1
- package/dist/schema/nodes/building.d.ts.map +1 -1
- package/dist/schema/nodes/building.js +3 -2
- package/dist/schema/nodes/cabinet.d.ts +534 -0
- package/dist/schema/nodes/cabinet.d.ts.map +1 -0
- package/dist/schema/nodes/cabinet.js +137 -0
- package/dist/schema/nodes/ceiling.d.ts +5 -2
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +5 -1
- package/dist/schema/nodes/chimney.d.ts +158 -0
- package/dist/schema/nodes/chimney.d.ts.map +1 -0
- package/dist/schema/nodes/chimney.js +67 -0
- package/dist/schema/nodes/column.d.ts +600 -7
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +589 -6
- package/dist/schema/nodes/cupola.d.ts +66 -0
- package/dist/schema/nodes/cupola.d.ts.map +1 -0
- package/dist/schema/nodes/cupola.js +28 -0
- package/dist/schema/nodes/door.d.ts +13 -5
- package/dist/schema/nodes/door.d.ts.map +1 -1
- package/dist/schema/nodes/door.js +12 -0
- package/dist/schema/nodes/dormer.d.ts +228 -0
- package/dist/schema/nodes/dormer.d.ts.map +1 -0
- package/dist/schema/nodes/dormer.js +119 -0
- package/dist/schema/nodes/downspout.d.ts +75 -0
- package/dist/schema/nodes/downspout.d.ts.map +1 -0
- package/dist/schema/nodes/downspout.js +63 -0
- package/dist/schema/nodes/duct-fitting.d.ts +91 -0
- package/dist/schema/nodes/duct-fitting.d.ts.map +1 -0
- package/dist/schema/nodes/duct-fitting.js +92 -0
- package/dist/schema/nodes/duct-segment.d.ts +61 -0
- package/dist/schema/nodes/duct-segment.d.ts.map +1 -0
- package/dist/schema/nodes/duct-segment.js +73 -0
- package/dist/schema/nodes/duct-terminal.d.ts +57 -0
- package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
- package/dist/schema/nodes/duct-terminal.js +51 -0
- package/dist/schema/nodes/elevator.d.ts +106 -0
- package/dist/schema/nodes/elevator.d.ts.map +1 -0
- package/dist/schema/nodes/elevator.js +57 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
- package/dist/schema/nodes/eyebrow-vent.js +40 -0
- package/dist/schema/nodes/fence.d.ts +19 -2
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +27 -2
- package/dist/schema/nodes/gutter.d.ts +84 -0
- package/dist/schema/nodes/gutter.d.ts.map +1 -0
- package/dist/schema/nodes/gutter.js +76 -0
- package/dist/schema/nodes/hvac-equipment.d.ts +63 -0
- package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
- package/dist/schema/nodes/hvac-equipment.js +55 -0
- package/dist/schema/nodes/item.d.ts +12 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +20 -0
- package/dist/schema/nodes/level.d.ts +1 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +4 -0
- package/dist/schema/nodes/lineset.d.ts +42 -0
- package/dist/schema/nodes/lineset.d.ts.map +1 -0
- package/dist/schema/nodes/lineset.js +38 -0
- package/dist/schema/nodes/liquid-line.d.ts +34 -0
- package/dist/schema/nodes/liquid-line.d.ts.map +1 -0
- package/dist/schema/nodes/liquid-line.js +24 -0
- package/dist/schema/nodes/measurement.d.ts +306 -0
- package/dist/schema/nodes/measurement.d.ts.map +1 -0
- package/dist/schema/nodes/measurement.js +87 -0
- package/dist/schema/nodes/measurement.test.d.ts +2 -0
- package/dist/schema/nodes/measurement.test.d.ts.map +1 -0
- package/dist/schema/nodes/measurement.test.js +139 -0
- package/dist/schema/nodes/pipe-fitting.d.ts +56 -0
- package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-fitting.js +45 -0
- package/dist/schema/nodes/pipe-segment.d.ts +48 -0
- package/dist/schema/nodes/pipe-segment.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-segment.js +36 -0
- package/dist/schema/nodes/pipe-trap.d.ts +43 -0
- package/dist/schema/nodes/pipe-trap.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-trap.js +36 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +2 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.js +205 -0
- package/dist/schema/nodes/ridge-vent.d.ts +82 -0
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent.js +248 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts +90 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-shape.js +318 -0
- package/dist/schema/nodes/roof-segment-shape.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-shape.test.js +175 -0
- package/dist/schema/nodes/roof-segment-surface.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-surface.test.js +48 -0
- package/dist/schema/nodes/roof-segment-trim.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-trim.test.js +107 -0
- package/dist/schema/nodes/roof-segment-walls.d.ts +101 -0
- package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-walls.js +330 -0
- package/dist/schema/nodes/roof-segment-walls.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-walls.test.js +92 -0
- package/dist/schema/nodes/roof-segment.d.ts +273 -3
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +539 -3
- package/dist/schema/nodes/roof.d.ts +8 -8
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +4 -16
- package/dist/schema/nodes/shelf.d.ts +106 -0
- package/dist/schema/nodes/shelf.d.ts.map +1 -0
- package/dist/schema/nodes/shelf.js +90 -0
- package/dist/schema/nodes/site.d.ts +1 -122
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/site.js +2 -6
- package/dist/schema/nodes/skylight.d.ts +279 -0
- package/dist/schema/nodes/skylight.d.ts.map +1 -0
- package/dist/schema/nodes/skylight.js +152 -0
- package/dist/schema/nodes/slab.d.ts +5 -2
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +5 -1
- package/dist/schema/nodes/solar-panel.d.ts +117 -0
- package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
- package/dist/schema/nodes/solar-panel.js +47 -0
- package/dist/schema/nodes/stair-segment.d.ts +4 -4
- package/dist/schema/nodes/stair.d.ts +13 -12
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +3 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
- package/dist/schema/nodes/surface-hole-metadata.js +3 -1
- package/dist/schema/nodes/turbine-vent.d.ts +68 -0
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
- package/dist/schema/nodes/turbine-vent.js +41 -0
- package/dist/schema/nodes/wall.d.ts +245 -6
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +257 -0
- package/dist/schema/nodes/wall.test.d.ts +2 -0
- package/dist/schema/nodes/wall.test.d.ts.map +1 -0
- package/dist/schema/nodes/wall.test.js +208 -0
- package/dist/schema/nodes/window.d.ts +11 -3
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +12 -0
- package/dist/schema/nodes/zone.d.ts +2 -0
- package/dist/schema/nodes/zone.d.ts.map +1 -1
- package/dist/schema/nodes/zone.js +6 -0
- package/dist/schema/scene-material.d.ts +41 -0
- package/dist/schema/scene-material.d.ts.map +1 -0
- package/dist/schema/scene-material.js +9 -0
- package/dist/schema/types.d.ts +2850 -655
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +49 -0
- package/dist/services/alignment-anchors.d.ts +100 -0
- package/dist/services/alignment-anchors.d.ts.map +1 -0
- package/dist/services/alignment-anchors.js +312 -0
- package/dist/services/alignment-anchors.test.d.ts +2 -0
- package/dist/services/alignment-anchors.test.d.ts.map +1 -0
- package/dist/services/alignment-anchors.test.js +383 -0
- package/dist/services/alignment.d.ts +131 -0
- package/dist/services/alignment.d.ts.map +1 -0
- package/dist/services/alignment.js +171 -0
- package/dist/services/alignment.test.d.ts +2 -0
- package/dist/services/alignment.test.d.ts.map +1 -0
- package/dist/services/alignment.test.js +92 -0
- package/dist/services/drag-session.d.ts +47 -0
- package/dist/services/drag-session.d.ts.map +1 -0
- package/dist/services/drag-session.js +100 -0
- package/dist/services/drag-session.test.d.ts +2 -0
- package/dist/services/drag-session.test.d.ts.map +1 -0
- package/dist/services/drag-session.test.js +219 -0
- package/dist/services/hosting.d.ts +88 -0
- package/dist/services/hosting.d.ts.map +1 -0
- package/dist/services/hosting.js +131 -0
- package/dist/services/hosting.test.d.ts +2 -0
- package/dist/services/hosting.test.d.ts.map +1 -0
- package/dist/services/hosting.test.js +230 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +12 -0
- package/dist/services/level-height.d.ts +28 -0
- package/dist/services/level-height.d.ts.map +1 -0
- package/dist/services/level-height.js +62 -0
- package/dist/services/movement.d.ts +48 -0
- package/dist/services/movement.d.ts.map +1 -0
- package/dist/services/movement.js +65 -0
- package/dist/services/movement.test.d.ts +2 -0
- package/dist/services/movement.test.d.ts.map +1 -0
- package/dist/services/movement.test.js +133 -0
- package/dist/services/opening-guides.d.ts +141 -0
- package/dist/services/opening-guides.d.ts.map +1 -0
- package/dist/services/opening-guides.js +267 -0
- package/dist/services/opening-guides.test.d.ts +2 -0
- package/dist/services/opening-guides.test.d.ts.map +1 -0
- package/dist/services/opening-guides.test.js +208 -0
- package/dist/services/port-connectivity.d.ts +115 -0
- package/dist/services/port-connectivity.d.ts.map +1 -0
- package/dist/services/port-connectivity.js +300 -0
- package/dist/services/port-connectivity.test.d.ts +2 -0
- package/dist/services/port-connectivity.test.d.ts.map +1 -0
- package/dist/services/port-connectivity.test.js +297 -0
- package/dist/services/riser-diagram.d.ts +40 -0
- package/dist/services/riser-diagram.d.ts.map +1 -0
- package/dist/services/riser-diagram.js +110 -0
- package/dist/services/riser-diagram.test.d.ts +2 -0
- package/dist/services/riser-diagram.test.d.ts.map +1 -0
- package/dist/services/riser-diagram.test.js +66 -0
- package/dist/services/single-undo-dance.test.d.ts +2 -0
- package/dist/services/single-undo-dance.test.d.ts.map +1 -0
- package/dist/services/single-undo-dance.test.js +210 -0
- package/dist/services/snap.d.ts +80 -0
- package/dist/services/snap.d.ts.map +1 -0
- package/dist/services/snap.js +140 -0
- package/dist/services/snap.test.d.ts +2 -0
- package/dist/services/snap.test.d.ts.map +1 -0
- package/dist/services/snap.test.js +137 -0
- package/dist/services/system-graph.d.ts +30 -0
- package/dist/services/system-graph.d.ts.map +1 -0
- package/dist/services/system-graph.js +164 -0
- package/dist/services/system-graph.test.d.ts +2 -0
- package/dist/services/system-graph.test.d.ts.map +1 -0
- package/dist/services/system-graph.test.js +138 -0
- package/dist/services/validate-dwv.d.ts +32 -0
- package/dist/services/validate-dwv.d.ts.map +1 -0
- package/dist/services/validate-dwv.js +118 -0
- package/dist/services/validate-dwv.test.d.ts +2 -0
- package/dist/services/validate-dwv.test.d.ts.map +1 -0
- package/dist/services/validate-dwv.test.js +109 -0
- package/dist/solar-panel-presets.d.ts +17 -0
- package/dist/solar-panel-presets.d.ts.map +1 -0
- package/dist/solar-panel-presets.js +43 -0
- package/dist/store/actions/node-actions.d.ts +14 -2
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +585 -15
- package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
- package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
- package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
- package/dist/store/actions/reparent.test.d.ts +2 -0
- package/dist/store/actions/reparent.test.d.ts.map +1 -0
- package/dist/store/actions/reparent.test.js +212 -0
- package/dist/store/actions/ridge-vent-update.test.d.ts +2 -0
- package/dist/store/actions/ridge-vent-update.test.d.ts.map +1 -0
- package/dist/store/actions/ridge-vent-update.test.js +264 -0
- package/dist/store/history-control.d.ts +11 -0
- package/dist/store/history-control.d.ts.map +1 -1
- package/dist/store/history-control.js +33 -0
- package/dist/store/use-interactive.d.ts +39 -0
- package/dist/store/use-interactive.d.ts.map +1 -1
- package/dist/store/use-interactive.js +75 -0
- package/dist/store/use-live-node-overrides.d.ts +22 -0
- package/dist/store/use-live-node-overrides.d.ts.map +1 -0
- package/dist/store/use-live-node-overrides.js +59 -0
- package/dist/store/use-scene-dirty-tracking.test.d.ts +2 -0
- package/dist/store/use-scene-dirty-tracking.test.d.ts.map +1 -0
- package/dist/store/use-scene-dirty-tracking.test.js +68 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-elevator-migration.test.js +109 -0
- package/dist/store/use-scene-measurement-roundtrip.test.d.ts +2 -0
- package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +1 -0
- package/dist/store/use-scene-measurement-roundtrip.test.js +97 -0
- package/dist/store/use-scene-plugins.test.d.ts +2 -0
- package/dist/store/use-scene-plugins.test.d.ts.map +1 -0
- package/dist/store/use-scene-plugins.test.js +51 -0
- package/dist/store/use-scene-wall-slot-migration.test.d.ts +2 -0
- package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-wall-slot-migration.test.js +210 -0
- package/dist/store/use-scene-window-migration.test.d.ts +2 -0
- package/dist/store/use-scene-window-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-window-migration.test.js +85 -0
- package/dist/store/use-scene.d.ts +28 -2
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +546 -49
- package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
- package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-dispatch.js +57 -0
- package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
- package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-geometry.js +50 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.js +218 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-system.js +48 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime-system.js +8 -0
- package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
- package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.js +225 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.test.js +64 -0
- package/dist/systems/elevator/elevator-service.d.ts +19 -0
- package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-service.js +111 -0
- package/dist/systems/fence/fence-centerline.d.ts +12 -0
- package/dist/systems/fence/fence-centerline.d.ts.map +1 -0
- package/dist/systems/fence/fence-centerline.js +37 -0
- package/dist/systems/fence/fence-spline.d.ts +39 -0
- package/dist/systems/fence/fence-spline.d.ts.map +1 -0
- package/dist/systems/fence/fence-spline.js +196 -0
- package/dist/systems/fence/fence-spline.test.d.ts +2 -0
- package/dist/systems/fence/fence-spline.test.d.ts.map +1 -0
- package/dist/systems/fence/fence-spline.test.js +136 -0
- package/dist/systems/stair/stair-footprint.d.ts +44 -0
- package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
- package/dist/systems/stair/stair-footprint.js +178 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +2447 -0
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.js +93 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.test.js +80 -0
- package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-sync.js +155 -147
- package/dist/systems/stair/stair-opening-sync.test.js +424 -2
- package/dist/systems/stair/stair-opening-system.d.ts +2 -0
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-system.js +104 -0
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +38 -2
- package/dist/systems/wall/wall-mitering.test.d.ts +2 -0
- package/dist/systems/wall/wall-mitering.test.d.ts.map +1 -0
- package/dist/systems/wall/wall-mitering.test.js +63 -0
- package/dist/systems/wall/wall-move.d.ts +48 -0
- package/dist/systems/wall/wall-move.d.ts.map +1 -0
- package/dist/systems/wall/wall-move.js +193 -0
- package/dist/utils/clone-scene-graph.d.ts +7 -4
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +29 -7
- package/dist/utils/clone-scene-graph.test.d.ts +2 -0
- package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/clone-scene-graph.test.js +62 -0
- package/dist/utils/heal-scene-graph.d.ts +18 -0
- package/dist/utils/heal-scene-graph.d.ts.map +1 -0
- package/dist/utils/heal-scene-graph.js +90 -0
- package/dist/utils/heal-scene-graph.test.d.ts +2 -0
- package/dist/utils/heal-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/heal-scene-graph.test.js +88 -0
- package/dist/validation/validate-build-json.d.ts +44 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -0
- package/dist/validation/validate-build-json.js +256 -0
- package/package.json +11 -4
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import { getRenderableSlabPolygon } from '../../lib/slab-polygon';
|
|
2
|
+
import { nodeRegistry } from '../../registry';
|
|
1
3
|
import { getScaledDimensions, isLowProfileItemSurface } from '../../schema';
|
|
2
4
|
import useScene from '../../store/use-scene';
|
|
5
|
+
import { getWallCurveFrameAt, isCurvedWall } from '../../systems/wall/wall-curve';
|
|
6
|
+
import { DEFAULT_WALL_THICKNESS } from '../../systems/wall/wall-footprint';
|
|
7
|
+
import { getFloorPlacedFootprints } from './floor-placed-elevation';
|
|
3
8
|
import { SpatialGrid } from './spatial-grid';
|
|
4
9
|
import { WallSpatialGrid } from './wall-spatial-grid';
|
|
5
10
|
// ============================================================================
|
|
@@ -38,6 +43,24 @@ function getItemFootprint(position, dimensions, rotation, inset = 0) {
|
|
|
38
43
|
[x + (-halfW * cos - halfD * sin), z + (-halfW * sin + halfD * cos)],
|
|
39
44
|
];
|
|
40
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Axis-aligned XZ extent of a footprint at `position`, rotated by `yRot`. The
|
|
48
|
+
* rotated width/depth is the same conservative bound the floor-placement draft
|
|
49
|
+
* uses, so a draft and an existing node are compared with identical math.
|
|
50
|
+
*/
|
|
51
|
+
function footprintBoundsXZ(position, dimensions, yRot) {
|
|
52
|
+
const [width, , depth] = dimensions;
|
|
53
|
+
const cos = Math.abs(Math.cos(yRot));
|
|
54
|
+
const sin = Math.abs(Math.sin(yRot));
|
|
55
|
+
const rotatedW = width * cos + depth * sin;
|
|
56
|
+
const rotatedD = width * sin + depth * cos;
|
|
57
|
+
return {
|
|
58
|
+
minX: position[0] - rotatedW / 2,
|
|
59
|
+
maxX: position[0] + rotatedW / 2,
|
|
60
|
+
minZ: position[2] - rotatedD / 2,
|
|
61
|
+
maxZ: position[2] + rotatedD / 2,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
41
64
|
function getItemLocalBounds(item) {
|
|
42
65
|
const [width, height, depth] = getScaledDimensions(item);
|
|
43
66
|
const minZ = item.asset.attachTo === 'wall-side' ? -depth : -depth / 2;
|
|
@@ -97,6 +120,24 @@ function resolveNodeLevelId(node, nodes) {
|
|
|
97
120
|
}
|
|
98
121
|
return 'default';
|
|
99
122
|
}
|
|
123
|
+
function expandIgnoredNodeIds(ignoreIds, nodes) {
|
|
124
|
+
const ignored = new Set(ignoreIds ?? []);
|
|
125
|
+
const queue = [...ignored];
|
|
126
|
+
while (queue.length > 0) {
|
|
127
|
+
const id = queue.pop();
|
|
128
|
+
const node = nodes[id];
|
|
129
|
+
const children = node?.children;
|
|
130
|
+
if (!Array.isArray(children))
|
|
131
|
+
continue;
|
|
132
|
+
for (const childId of children) {
|
|
133
|
+
if (typeof childId !== 'string' || ignored.has(childId))
|
|
134
|
+
continue;
|
|
135
|
+
ignored.add(childId);
|
|
136
|
+
queue.push(childId);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return ignored;
|
|
140
|
+
}
|
|
100
141
|
/**
|
|
101
142
|
* Test if two line segments (a1->a2) and (b1->b2) intersect.
|
|
102
143
|
*/
|
|
@@ -162,91 +203,399 @@ export function itemOverlapsPolygon(position, dimensions, rotation, polygon, ins
|
|
|
162
203
|
}
|
|
163
204
|
return false;
|
|
164
205
|
}
|
|
206
|
+
function pointSegmentDistance(px, pz, ax, az, bx, bz) {
|
|
207
|
+
const dx = bx - ax;
|
|
208
|
+
const dz = bz - az;
|
|
209
|
+
const lengthSquared = dx * dx + dz * dz;
|
|
210
|
+
if (lengthSquared < 1e-18)
|
|
211
|
+
return Math.hypot(px - ax, pz - az);
|
|
212
|
+
const t = Math.max(0, Math.min(1, ((px - ax) * dx + (pz - az) * dz) / lengthSquared));
|
|
213
|
+
return Math.hypot(px - (ax + dx * t), pz - (az + dz * t));
|
|
214
|
+
}
|
|
215
|
+
// Ray-cast pointInPolygon is unreliable for points exactly on the polygon
|
|
216
|
+
// boundary: the answer flips depending on which side of the polygon the edge
|
|
217
|
+
// is on. Interval classification below therefore treats "within this distance
|
|
218
|
+
// of the boundary" as inside explicitly, so walls sitting exactly on a slab
|
|
219
|
+
// edge (the common case — auto-slab polygons derive from wall centerlines)
|
|
220
|
+
// classify identically on every side of the slab.
|
|
221
|
+
const ON_BOUNDARY_EPSILON = 1e-4;
|
|
222
|
+
function pointOnPolygonBoundary(px, pz, polygon) {
|
|
223
|
+
const n = polygon.length;
|
|
224
|
+
for (let i = 0; i < n; i++) {
|
|
225
|
+
const [ax, az] = polygon[i];
|
|
226
|
+
const [bx, bz] = polygon[(i + 1) % n];
|
|
227
|
+
if (pointSegmentDistance(px, pz, ax, az, bx, bz) <= ON_BOUNDARY_EPSILON)
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
function mergeIntervals(intervals) {
|
|
233
|
+
if (intervals.length <= 1)
|
|
234
|
+
return intervals;
|
|
235
|
+
const sorted = [...intervals].sort((a, b) => a[0] - b[0]);
|
|
236
|
+
const merged = [[sorted[0][0], sorted[0][1]]];
|
|
237
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
238
|
+
const [intervalStart, intervalEnd] = sorted[i];
|
|
239
|
+
const last = merged[merged.length - 1];
|
|
240
|
+
if (intervalStart <= last[1] + 1e-9) {
|
|
241
|
+
last[1] = Math.max(last[1], intervalEnd);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
merged.push([intervalStart, intervalEnd]);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return merged;
|
|
248
|
+
}
|
|
249
|
+
/** Total length of a merged (sorted, disjoint) interval list. */
|
|
250
|
+
function intervalsLength(intervals) {
|
|
251
|
+
let total = 0;
|
|
252
|
+
for (const [intervalStart, intervalEnd] of intervals)
|
|
253
|
+
total += intervalEnd - intervalStart;
|
|
254
|
+
return total;
|
|
255
|
+
}
|
|
256
|
+
/** `base` minus `cut`. Both inputs may be unsorted; the result is merged. */
|
|
257
|
+
function subtractIntervals(base, cut) {
|
|
258
|
+
if (base.length === 0 || cut.length === 0)
|
|
259
|
+
return mergeIntervals(base);
|
|
260
|
+
const cuts = mergeIntervals(cut);
|
|
261
|
+
const result = [];
|
|
262
|
+
for (const [baseStart, baseEnd] of mergeIntervals(base)) {
|
|
263
|
+
let cursor = baseStart;
|
|
264
|
+
for (const [cutStart, cutEnd] of cuts) {
|
|
265
|
+
if (cutEnd <= cursor)
|
|
266
|
+
continue;
|
|
267
|
+
if (cutStart >= baseEnd)
|
|
268
|
+
break;
|
|
269
|
+
if (cutStart > cursor)
|
|
270
|
+
result.push([cursor, cutStart]);
|
|
271
|
+
cursor = cutEnd;
|
|
272
|
+
if (cursor >= baseEnd)
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
if (cursor < baseEnd)
|
|
276
|
+
result.push([cursor, baseEnd]);
|
|
277
|
+
}
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
165
280
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
281
|
+
* Sub-intervals of segment (ax,az)→(bx,bz) that lie inside the polygon (and,
|
|
282
|
+
* when `includeBoundary`, on its boundary), as [t0, t1] fractions of the
|
|
283
|
+
* segment. The segment is split at every crossing with a polygon edge and
|
|
284
|
+
* each sub-interval is classified by its midpoint, so no test point ever
|
|
285
|
+
* sits on a crossing.
|
|
169
286
|
*/
|
|
170
|
-
function
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
287
|
+
function segmentInsideIntervals(ax, az, bx, bz, polygon, includeBoundary) {
|
|
288
|
+
const dx = bx - ax;
|
|
289
|
+
const dz = bz - az;
|
|
290
|
+
const length = Math.hypot(dx, dz);
|
|
291
|
+
if (length < 1e-9)
|
|
292
|
+
return [];
|
|
293
|
+
const ts = [0, 1];
|
|
294
|
+
const n = polygon.length;
|
|
295
|
+
for (let i = 0; i < n; i++) {
|
|
296
|
+
const [px, pz] = polygon[i];
|
|
297
|
+
const [qx, qz] = polygon[(i + 1) % n];
|
|
298
|
+
const ex = qx - px;
|
|
299
|
+
const ez = qz - pz;
|
|
300
|
+
const denom = dx * ez - dz * ex;
|
|
301
|
+
if (Math.abs(denom) < 1e-12)
|
|
302
|
+
continue; // parallel/collinear — nothing to split at
|
|
303
|
+
const t = ((px - ax) * ez - (pz - az) * ex) / denom;
|
|
304
|
+
const s = ((px - ax) * dz - (pz - az) * dx) / denom;
|
|
305
|
+
if (t > 0 && t < 1 && s >= -1e-9 && s <= 1 + 1e-9)
|
|
306
|
+
ts.push(t);
|
|
307
|
+
}
|
|
308
|
+
ts.sort((a, b) => a - b);
|
|
309
|
+
const inside = [];
|
|
310
|
+
for (let i = 1; i < ts.length; i++) {
|
|
311
|
+
const t0 = ts[i - 1];
|
|
312
|
+
const t1 = ts[i];
|
|
313
|
+
if (t1 - t0 < 1e-9)
|
|
314
|
+
continue;
|
|
315
|
+
const tm = (t0 + t1) / 2;
|
|
316
|
+
const mx = ax + dx * tm;
|
|
317
|
+
const mz = az + dz * tm;
|
|
318
|
+
const midpointInside = pointOnPolygonBoundary(mx, mz, polygon)
|
|
319
|
+
? includeBoundary
|
|
320
|
+
: pointInPolygon(mx, mz, polygon);
|
|
321
|
+
if (midpointInside)
|
|
322
|
+
inside.push([t0, t1]);
|
|
323
|
+
}
|
|
324
|
+
return inside;
|
|
325
|
+
}
|
|
326
|
+
function polylineLength(points) {
|
|
327
|
+
let total = 0;
|
|
328
|
+
for (let i = 1; i < points.length; i++) {
|
|
329
|
+
total += Math.hypot(points[i].x - points[i - 1].x, points[i].y - points[i - 1].y);
|
|
330
|
+
}
|
|
331
|
+
return total;
|
|
187
332
|
}
|
|
188
333
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* Note: A wall with just one endpoint touching the edge but the rest outside
|
|
196
|
-
* is NOT considered overlapping (adjacent only).
|
|
334
|
+
* Inside sub-intervals of a polyline against a polygon, in cumulative
|
|
335
|
+
* arc-length units from the polyline start (merged, disjoint). Boundary
|
|
336
|
+
* contact counts as inside for slab support (walls sit exactly on slab
|
|
337
|
+
* edges — see ON_BOUNDARY_EPSILON above); hole callers pass
|
|
338
|
+
* `includeBoundary: false` so a wall running along a stairwell hole's
|
|
339
|
+
* rim keeps the rim's support.
|
|
197
340
|
*/
|
|
198
|
-
|
|
341
|
+
function polylineInsideIntervals(points, polygon, includeBoundary = true) {
|
|
342
|
+
const intervals = [];
|
|
343
|
+
let offset = 0;
|
|
344
|
+
for (let i = 1; i < points.length; i++) {
|
|
345
|
+
const a = points[i - 1];
|
|
346
|
+
const b = points[i];
|
|
347
|
+
const segmentLength = Math.hypot(b.x - a.x, b.y - a.y);
|
|
348
|
+
if (segmentLength < 1e-9)
|
|
349
|
+
continue;
|
|
350
|
+
for (const [t0, t1] of segmentInsideIntervals(a.x, a.y, b.x, b.y, polygon, includeBoundary)) {
|
|
351
|
+
intervals.push([offset + t0 * segmentLength, offset + t1 * segmentLength]);
|
|
352
|
+
}
|
|
353
|
+
offset += segmentLength;
|
|
354
|
+
}
|
|
355
|
+
return mergeIntervals(intervals);
|
|
356
|
+
}
|
|
357
|
+
function polylineInsideLength(points, polygon) {
|
|
358
|
+
return intervalsLength(polylineInsideIntervals(points, polygon));
|
|
359
|
+
}
|
|
360
|
+
// Minimum length of wall that must lie on/inside a slab polygon before the
|
|
361
|
+
// wall counts as overlapping it. Point contact (a perpendicular wall butting
|
|
362
|
+
// into a room's edge) clips to ~zero length and never reaches this, so such
|
|
363
|
+
// walls don't follow the slab's elevation.
|
|
364
|
+
const WALL_SLAB_MIN_OVERLAP = 0.05;
|
|
365
|
+
/**
|
|
366
|
+
* Centerline of the wall plus its two face lines (centerline offset by
|
|
367
|
+
* ±halfThickness). The face lines catch walls whose centerline sits on or
|
|
368
|
+
* just outside the slab boundary but whose body reaches onto the slab —
|
|
369
|
+
* e.g. slab polygons drawn to the room's interior faces.
|
|
370
|
+
*/
|
|
371
|
+
function wallTestPolylines(start, end, curveOffset, halfThickness) {
|
|
372
|
+
const wallLike = { start, end, curveOffset };
|
|
373
|
+
if (curveOffset !== 0 && isCurvedWall(wallLike)) {
|
|
374
|
+
const count = 16;
|
|
375
|
+
const center = [];
|
|
376
|
+
const left = [];
|
|
377
|
+
const right = [];
|
|
378
|
+
for (let i = 0; i <= count; i++) {
|
|
379
|
+
const frame = getWallCurveFrameAt(wallLike, i / count);
|
|
380
|
+
center.push(frame.point);
|
|
381
|
+
left.push({
|
|
382
|
+
x: frame.point.x + frame.normal.x * halfThickness,
|
|
383
|
+
y: frame.point.y + frame.normal.y * halfThickness,
|
|
384
|
+
});
|
|
385
|
+
right.push({
|
|
386
|
+
x: frame.point.x - frame.normal.x * halfThickness,
|
|
387
|
+
y: frame.point.y - frame.normal.y * halfThickness,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
return halfThickness > 0 ? [center, left, right] : [center];
|
|
391
|
+
}
|
|
392
|
+
const center = [
|
|
393
|
+
{ x: start[0], y: start[1] },
|
|
394
|
+
{ x: end[0], y: end[1] },
|
|
395
|
+
];
|
|
199
396
|
const dx = end[0] - start[0];
|
|
200
397
|
const dz = end[1] - start[1];
|
|
201
|
-
const len = Math.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
398
|
+
const len = Math.hypot(dx, dz);
|
|
399
|
+
if (len < 1e-10 || halfThickness <= 0)
|
|
400
|
+
return [center];
|
|
401
|
+
const nx = (-dz / len) * halfThickness;
|
|
402
|
+
const nz = (dx / len) * halfThickness;
|
|
403
|
+
return [
|
|
404
|
+
center,
|
|
405
|
+
[
|
|
406
|
+
{ x: start[0] + nx, y: start[1] + nz },
|
|
407
|
+
{ x: end[0] + nx, y: end[1] + nz },
|
|
408
|
+
],
|
|
409
|
+
[
|
|
410
|
+
{ x: start[0] - nx, y: start[1] - nz },
|
|
411
|
+
{ x: end[0] - nx, y: end[1] - nz },
|
|
412
|
+
],
|
|
413
|
+
];
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Test whether a wall overlaps a slab polygon along a segment of its length.
|
|
417
|
+
*
|
|
418
|
+
* The wall's centerline and both face lines are clipped against the polygon;
|
|
419
|
+
* the wall overlaps when the longest clipped inside-or-on-boundary length
|
|
420
|
+
* exceeds a threshold (5cm, halved for very short walls). Because interval
|
|
421
|
+
* midpoints classify "on the boundary" as inside explicitly (never by
|
|
422
|
+
* ray-cast tie-breaking), a wall sitting exactly on a slab edge resolves
|
|
423
|
+
* identically on every side of the slab.
|
|
424
|
+
*
|
|
425
|
+
* A wall that only touches the polygon at a point — a perpendicular wall
|
|
426
|
+
* butting into a room's edge, or a corner-to-corner touch — clips to ~zero
|
|
427
|
+
* length and does NOT overlap.
|
|
428
|
+
*/
|
|
429
|
+
export function wallOverlapsPolygon(startOrWall, endOrPolygon, polygonArg) {
|
|
430
|
+
// Two call shapes:
|
|
431
|
+
// wallOverlapsPolygon(wallLike, polygon) — preferred; curve-aware
|
|
432
|
+
// wallOverlapsPolygon(start, end, polygon) — legacy chord-only
|
|
433
|
+
let start;
|
|
434
|
+
let end;
|
|
435
|
+
let polygon;
|
|
436
|
+
let curveOffset = 0;
|
|
437
|
+
let thickness = DEFAULT_WALL_THICKNESS;
|
|
438
|
+
if (Array.isArray(startOrWall)) {
|
|
439
|
+
start = startOrWall;
|
|
440
|
+
end = endOrPolygon;
|
|
441
|
+
polygon = polygonArg;
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
start = startOrWall.start;
|
|
445
|
+
end = startOrWall.end;
|
|
446
|
+
curveOffset = startOrWall.curveOffset ?? 0;
|
|
447
|
+
thickness = startOrWall.thickness ?? DEFAULT_WALL_THICKNESS;
|
|
448
|
+
polygon = endOrPolygon;
|
|
449
|
+
}
|
|
450
|
+
const halfThickness = Math.max(thickness / 2, 0);
|
|
451
|
+
const polylines = wallTestPolylines(start, end, curveOffset, halfThickness);
|
|
452
|
+
const centerLength = polylineLength(polylines[0]);
|
|
453
|
+
if (centerLength < 1e-9)
|
|
454
|
+
return false;
|
|
455
|
+
let overlap = 0;
|
|
456
|
+
for (const line of polylines) {
|
|
457
|
+
overlap = Math.max(overlap, polylineInsideLength(line, polygon));
|
|
458
|
+
}
|
|
459
|
+
const threshold = Math.max(1e-3, Math.min(WALL_SLAB_MIN_OVERLAP, centerLength * 0.5));
|
|
460
|
+
return overlap >= threshold;
|
|
461
|
+
}
|
|
462
|
+
// A slab elevation must support at least this fraction of the wall's
|
|
463
|
+
// length before it can dictate the wall's base. Below majority, a raised
|
|
464
|
+
// slab reaching one endpoint would hoist the whole wall off the floor
|
|
465
|
+
// that actually carries it.
|
|
466
|
+
const WALL_SLAB_SUPPORT_MAJORITY = 0.5;
|
|
467
|
+
// Slabs whose elevations differ by less than this pool their support:
|
|
468
|
+
// a wall shared between two rooms' slabs is covered roughly half by
|
|
469
|
+
// each, and must still follow their common elevation.
|
|
470
|
+
const WALL_SLAB_ELEVATION_POOL_EPSILON = 1e-4;
|
|
471
|
+
export function computeWallSlabSupport(wallLike, slabs, levelWalls) {
|
|
472
|
+
const { start, end, curveOffset = 0, thickness = DEFAULT_WALL_THICKNESS } = wallLike;
|
|
473
|
+
const halfThickness = Math.max(thickness / 2, 0);
|
|
474
|
+
const polylines = wallTestPolylines(start, end, curveOffset, halfThickness);
|
|
475
|
+
const polylineLengths = polylines.map(polylineLength);
|
|
476
|
+
const wallLength = polylineLengths[0];
|
|
477
|
+
if (wallLength < 1e-9) {
|
|
478
|
+
return { elevation: 0, baseElevation: 0, baseSegments: [] };
|
|
479
|
+
}
|
|
480
|
+
const minSupport = Math.max(1e-3, Math.min(WALL_SLAB_MIN_OVERLAP, wallLength * 0.5));
|
|
481
|
+
const groups = [];
|
|
482
|
+
for (const slab of slabs) {
|
|
483
|
+
if (slab.polygon.length < 3)
|
|
484
|
+
continue;
|
|
485
|
+
const renderedPolygon = getRenderableSlabPolygon(slab, {
|
|
486
|
+
walls: levelWalls,
|
|
487
|
+
siblingSlabs: slabs.filter((other) => other.id !== slab.id),
|
|
488
|
+
});
|
|
489
|
+
let supported = 0;
|
|
490
|
+
const perPolyline = polylines.map((line) => {
|
|
491
|
+
let intervals = polylineInsideIntervals(line, renderedPolygon);
|
|
492
|
+
for (const hole of slab.holes || []) {
|
|
493
|
+
if (intervals.length === 0)
|
|
494
|
+
break;
|
|
495
|
+
if (hole.length < 3)
|
|
496
|
+
continue;
|
|
497
|
+
intervals = subtractIntervals(intervals, polylineInsideIntervals(line, hole, false));
|
|
498
|
+
}
|
|
499
|
+
supported = Math.max(supported, intervalsLength(intervals));
|
|
500
|
+
return intervals;
|
|
501
|
+
});
|
|
502
|
+
if (supported < minSupport)
|
|
503
|
+
continue;
|
|
504
|
+
const elevation = slab.elevation ?? 0.05;
|
|
505
|
+
let group = groups.find((candidate) => Math.abs(candidate.elevation - elevation) <= WALL_SLAB_ELEVATION_POOL_EPSILON);
|
|
506
|
+
if (!group) {
|
|
507
|
+
group = { elevation, perPolyline: polylines.map(() => []) };
|
|
508
|
+
groups.push(group);
|
|
509
|
+
}
|
|
510
|
+
for (let i = 0; i < perPolyline.length; i++) {
|
|
511
|
+
group.perPolyline[i].push(...perPolyline[i]);
|
|
247
512
|
}
|
|
248
513
|
}
|
|
249
|
-
|
|
514
|
+
const evaluatedGroups = groups.map((group) => {
|
|
515
|
+
let coverage = 0;
|
|
516
|
+
const mergedPerPolyline = group.perPolyline.map(mergeIntervals);
|
|
517
|
+
for (let i = 0; i < group.perPolyline.length; i++) {
|
|
518
|
+
const lineLength = polylineLengths[i];
|
|
519
|
+
if (lineLength < 1e-9)
|
|
520
|
+
continue;
|
|
521
|
+
coverage = Math.max(coverage, intervalsLength(mergedPerPolyline[i]) / lineLength);
|
|
522
|
+
}
|
|
523
|
+
return { ...group, coverage, mergedPerPolyline };
|
|
524
|
+
});
|
|
525
|
+
let majorityElevation = Number.NEGATIVE_INFINITY;
|
|
526
|
+
let bestElevation = Number.NEGATIVE_INFINITY;
|
|
527
|
+
let bestCoverage = -1;
|
|
528
|
+
for (const group of evaluatedGroups) {
|
|
529
|
+
if (group.coverage >= WALL_SLAB_SUPPORT_MAJORITY - 1e-6) {
|
|
530
|
+
majorityElevation = Math.max(majorityElevation, group.elevation);
|
|
531
|
+
}
|
|
532
|
+
if (group.coverage > bestCoverage + 1e-6 ||
|
|
533
|
+
(Math.abs(group.coverage - bestCoverage) <= 1e-6 && group.elevation > bestElevation)) {
|
|
534
|
+
bestCoverage = group.coverage;
|
|
535
|
+
bestElevation = group.elevation;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
const elevation = majorityElevation !== Number.NEGATIVE_INFINITY
|
|
539
|
+
? majorityElevation
|
|
540
|
+
: bestElevation === Number.NEGATIVE_INFINITY
|
|
541
|
+
? 0
|
|
542
|
+
: bestElevation;
|
|
543
|
+
const normalizedIntervals = (group, polylineIndex) => {
|
|
544
|
+
const lineLength = polylineLengths[polylineIndex];
|
|
545
|
+
if (lineLength < 1e-9)
|
|
546
|
+
return [];
|
|
547
|
+
return group.mergedPerPolyline[polylineIndex].map(([intervalStart, intervalEnd]) => [intervalStart / lineLength, intervalEnd / lineLength]);
|
|
548
|
+
};
|
|
549
|
+
const normalizedByGroup = evaluatedGroups.map((group) => ({
|
|
550
|
+
elevation: group.elevation,
|
|
551
|
+
perPolyline: group.mergedPerPolyline.map((_, index) => normalizedIntervals(group, index)),
|
|
552
|
+
}));
|
|
553
|
+
const breakpoints = [0, 1];
|
|
554
|
+
for (const group of normalizedByGroup) {
|
|
555
|
+
for (const intervals of group.perPolyline) {
|
|
556
|
+
for (const [intervalStart, intervalEnd] of intervals) {
|
|
557
|
+
breakpoints.push(intervalStart, intervalEnd);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
breakpoints.sort((left, right) => left - right);
|
|
562
|
+
const uniqueBreakpoints = breakpoints.filter((value, index) => index === 0 || value - breakpoints[index - 1] > 1e-7);
|
|
563
|
+
const highestAt = (polylineIndex, t) => {
|
|
564
|
+
let highest = Number.NEGATIVE_INFINITY;
|
|
565
|
+
for (const group of normalizedByGroup) {
|
|
566
|
+
if (group.perPolyline[polylineIndex]?.some(([intervalStart, intervalEnd]) => t >= intervalStart - 1e-7 && t <= intervalEnd + 1e-7)) {
|
|
567
|
+
highest = Math.max(highest, group.elevation);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return highest;
|
|
571
|
+
};
|
|
572
|
+
const baseSegments = [];
|
|
573
|
+
for (let index = 1; index < uniqueBreakpoints.length; index++) {
|
|
574
|
+
const start = uniqueBreakpoints[index - 1];
|
|
575
|
+
const end = uniqueBreakpoints[index];
|
|
576
|
+
if (end - start < 1e-7)
|
|
577
|
+
continue;
|
|
578
|
+
const midpoint = (start + end) / 2;
|
|
579
|
+
const leftElevation = polylines.length >= 3 ? highestAt(1, midpoint) : Number.NEGATIVE_INFINITY;
|
|
580
|
+
const rightElevation = polylines.length >= 3 ? highestAt(2, midpoint) : Number.NEGATIVE_INFINITY;
|
|
581
|
+
const faceElevations = [leftElevation, rightElevation].filter(Number.isFinite);
|
|
582
|
+
const segmentElevation = faceElevations.length > 0 ? Math.min(...faceElevations) : Math.max(highestAt(0, midpoint), 0);
|
|
583
|
+
const previous = baseSegments[baseSegments.length - 1];
|
|
584
|
+
if (previous &&
|
|
585
|
+
Math.abs(previous.elevation - segmentElevation) <= WALL_SLAB_ELEVATION_POOL_EPSILON) {
|
|
586
|
+
previous.end = end;
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
baseSegments.push({ start, end, elevation: segmentElevation });
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (baseSegments.length === 0)
|
|
593
|
+
baseSegments.push({ start: 0, end: 1, elevation });
|
|
594
|
+
const baseElevation = Math.min(...baseSegments.map((segment) => segment.elevation));
|
|
595
|
+
return { elevation, baseElevation, baseSegments };
|
|
596
|
+
}
|
|
597
|
+
export function computeWallSlabElevation(wallLike, slabs, levelWalls) {
|
|
598
|
+
return computeWallSlabSupport(wallLike, slabs, levelWalls).elevation;
|
|
250
599
|
}
|
|
251
600
|
export class SpatialGridManager {
|
|
252
601
|
floorGrids = new Map(); // levelId -> grid
|
|
@@ -433,37 +782,51 @@ export class SpatialGridManager {
|
|
|
433
782
|
}
|
|
434
783
|
// Query methods
|
|
435
784
|
canPlaceOnFloor(levelId, position, dimensions, rotation, ignoreIds) {
|
|
785
|
+
return this.canPlaceOnFloorFootprints(levelId, [{ position, dimensions, rotation }], ignoreIds);
|
|
786
|
+
}
|
|
787
|
+
canPlaceOnFloorFootprints(levelId, footprints, ignoreIds) {
|
|
436
788
|
const nodes = useScene.getState().nodes;
|
|
437
|
-
const ignoreSet =
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
789
|
+
const ignoreSet = expandIgnoredNodeIds(ignoreIds, nodes);
|
|
790
|
+
const draftBounds = footprints.map((footprint) => footprintBoundsXZ(footprint.position, footprint.dimensions, footprint.rotation[1] ?? 0));
|
|
791
|
+
for (let i = 0; i < draftBounds.length; i += 1) {
|
|
792
|
+
const a = draftBounds[i];
|
|
793
|
+
for (let j = i + 1; j < draftBounds.length; j += 1) {
|
|
794
|
+
const b = draftBounds[j];
|
|
795
|
+
if (intervalsOverlap(a.minX, a.maxX, b.minX, b.maxX) &&
|
|
796
|
+
intervalsOverlap(a.minZ, a.maxZ, b.minZ, b.maxZ)) {
|
|
797
|
+
return { valid: false, conflictIds: [] };
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
// A floor placement conflicts with any other COLLIDING floor-resting node,
|
|
802
|
+
// not just items — every kind whose `floorPlaced.collides` is set (item /
|
|
803
|
+
// shelf / column / cabinet / stair) contributes its footprint(s) as an
|
|
804
|
+
// obstacle. Each candidate's XZ extent is read from the same declarative
|
|
805
|
+
// footprint the elevation + sync paths use, so adding a colliding kind
|
|
806
|
+
// needs no change here.
|
|
450
807
|
const conflicts = [];
|
|
451
808
|
for (const node of Object.values(nodes)) {
|
|
452
|
-
if (node.
|
|
809
|
+
if (ignoreSet.has(node.id))
|
|
453
810
|
continue;
|
|
454
|
-
const
|
|
455
|
-
if (
|
|
811
|
+
const floorPlaced = nodeRegistry.get(node.type)?.capabilities?.floorPlaced;
|
|
812
|
+
if (!floorPlaced?.collides)
|
|
456
813
|
continue;
|
|
457
|
-
if (
|
|
814
|
+
if (floorPlaced.applies && !floorPlaced.applies(node))
|
|
458
815
|
continue;
|
|
459
|
-
|
|
816
|
+
// Low-profile item surfaces (rugs, mats) are stack-on targets, not
|
|
817
|
+
// obstacles — keep the long-standing item-only exemption.
|
|
818
|
+
if (node.type === 'item' && isLowProfileItemSurface(node))
|
|
460
819
|
continue;
|
|
461
|
-
if (resolveNodeLevelId(
|
|
820
|
+
if (resolveNodeLevelId(node, nodes) !== levelId)
|
|
462
821
|
continue;
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
822
|
+
for (const footprint of getFloorPlacedFootprints(floorPlaced, node, { nodes })) {
|
|
823
|
+
const fpRotation = Array.isArray(footprint.rotation) ? (footprint.rotation[1] ?? 0) : 0;
|
|
824
|
+
const bounds = footprintBoundsXZ(footprint.position ?? node.position, footprint.dimensions, fpRotation);
|
|
825
|
+
if (draftBounds.some((draft) => intervalsOverlap(draft.minX, draft.maxX, bounds.minX, bounds.maxX) &&
|
|
826
|
+
intervalsOverlap(draft.minZ, draft.maxZ, bounds.minZ, bounds.maxZ))) {
|
|
827
|
+
conflicts.push(node.id);
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
467
830
|
}
|
|
468
831
|
}
|
|
469
832
|
return { valid: conflicts.length === 0, conflictIds: conflicts };
|
|
@@ -595,55 +958,59 @@ export class SpatialGridManager {
|
|
|
595
958
|
}
|
|
596
959
|
/**
|
|
597
960
|
* Get the slab elevation for a wall by checking if it overlaps with any slab polygon (excluding holes).
|
|
598
|
-
* Uses wallOverlapsPolygon which handles edge cases (points on boundary, collinear segments).
|
|
599
961
|
* Returns the highest slab elevation found, or 0 if none.
|
|
962
|
+
*
|
|
963
|
+
* Accepts an optional `curveOffset` so curved walls evaluate overlap
|
|
964
|
+
* against their actual centerline samples, not just the chord.
|
|
600
965
|
*/
|
|
601
|
-
getSlabElevationForWall(levelId, start, end) {
|
|
966
|
+
getSlabElevationForWall(levelId, start, end, curveOffset = 0, thickness = DEFAULT_WALL_THICKNESS) {
|
|
967
|
+
return this.getSlabSupportForWall(levelId, start, end, curveOffset, thickness).elevation;
|
|
968
|
+
}
|
|
969
|
+
getSlabSupportForWall(levelId, start, end, curveOffset = 0, thickness = DEFAULT_WALL_THICKNESS) {
|
|
602
970
|
const slabMap = this.slabsByLevel.get(levelId);
|
|
603
|
-
if (!slabMap)
|
|
604
|
-
return
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
971
|
+
if (!slabMap) {
|
|
972
|
+
return {
|
|
973
|
+
elevation: 0,
|
|
974
|
+
baseElevation: 0,
|
|
975
|
+
baseSegments: [{ start: 0, end: 1, elevation: 0 }],
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
return computeWallSlabSupport({ start, end, curveOffset, thickness }, [...slabMap.values()], this.getLevelWallNodes(levelId));
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Walls on a level, resolved fresh from the scene store (the manager's
|
|
982
|
+
* own wall map is only maintained on create/delete, not on updates).
|
|
983
|
+
* Cached per scene `nodes` record so per-pointer-tick callers
|
|
984
|
+
* (door/window move) don't rescan the node map.
|
|
985
|
+
*/
|
|
986
|
+
levelWallsCache = new WeakMap();
|
|
987
|
+
getLevelWallNodes(levelId) {
|
|
988
|
+
const nodes = useScene.getState().nodes;
|
|
989
|
+
let byLevel = this.levelWallsCache.get(nodes);
|
|
990
|
+
if (!byLevel) {
|
|
991
|
+
byLevel = new Map();
|
|
992
|
+
this.levelWallsCache.set(nodes, byLevel);
|
|
993
|
+
}
|
|
994
|
+
const cached = byLevel.get(levelId);
|
|
995
|
+
if (cached)
|
|
996
|
+
return cached;
|
|
997
|
+
const walls = [];
|
|
998
|
+
for (const node of Object.values(nodes)) {
|
|
999
|
+
if (node.type !== 'wall')
|
|
617
1000
|
continue;
|
|
1001
|
+
// Walk the parent chain to the owning level (guarded against cycles).
|
|
1002
|
+
let current = node;
|
|
1003
|
+
let guard = 0;
|
|
1004
|
+
while (current && current.type !== 'level' && guard < 16) {
|
|
1005
|
+
current = current.parentId ? nodes[current.parentId] : undefined;
|
|
1006
|
+
guard += 1;
|
|
618
1007
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
// midpoint falls in a staircase hole but the wall's endpoints are on solid slab.
|
|
622
|
-
const dx = end[0] - start[0];
|
|
623
|
-
const dz = end[1] - start[1];
|
|
624
|
-
let hasValidPoint = false;
|
|
625
|
-
for (const t of [0, 0.25, 0.5, 0.75, 1]) {
|
|
626
|
-
const px = start[0] + dx * t;
|
|
627
|
-
const pz = start[1] + dz * t;
|
|
628
|
-
let inHole = false;
|
|
629
|
-
for (const hole of holes) {
|
|
630
|
-
if (hole.length >= 3 && pointInPolygon(px, pz, hole)) {
|
|
631
|
-
inHole = true;
|
|
632
|
-
break;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
if (!inHole) {
|
|
636
|
-
hasValidPoint = true;
|
|
637
|
-
break;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
if (hasValidPoint) {
|
|
641
|
-
const elevation = slab.elevation ?? 0.05;
|
|
642
|
-
if (elevation > maxElevation)
|
|
643
|
-
maxElevation = elevation;
|
|
1008
|
+
if (current?.type === 'level' && current.id === levelId) {
|
|
1009
|
+
walls.push(node);
|
|
644
1010
|
}
|
|
645
1011
|
}
|
|
646
|
-
|
|
1012
|
+
byLevel.set(levelId, walls);
|
|
1013
|
+
return walls;
|
|
647
1014
|
}
|
|
648
1015
|
/**
|
|
649
1016
|
* Check if an item can be placed on a ceiling.
|