@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
package/dist/lib/slab-polygon.js
CHANGED
|
@@ -1,21 +1,472 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { isCurvedWall, sampleWallCenterline } from '../systems/wall/wall-curve';
|
|
2
|
+
import { getWallThickness } from '../systems/wall/wall-footprint';
|
|
3
|
+
/**
|
|
4
|
+
* Render-time slab polygon rules.
|
|
5
|
+
*
|
|
6
|
+
* Slab nodes store the wall-centerline polygon (auto slabs) or the drawn
|
|
7
|
+
* polygon (manual slabs) — render offsets are NEVER stored in node data.
|
|
8
|
+
* At geometry build time each polygon edge is SPLIT at the clipped span
|
|
9
|
+
* boundaries of every overlapping candidate (sibling slab edges and wall
|
|
10
|
+
* centerlines in the adoption band) and each sub-edge is classified
|
|
11
|
+
* independently — a single stored edge can be backed differently along
|
|
12
|
+
* its span (two rooms offset diagonally share a wall only where they
|
|
13
|
+
* overlap, so one room's edge is interior beside the sibling and its own
|
|
14
|
+
* facade elsewhere). Each sub-edge is PROJECTED onto an absolute target
|
|
15
|
+
* line:
|
|
16
|
+
*
|
|
17
|
+
* - INTERIOR — a sibling slab has a collinear, overlapping edge across
|
|
18
|
+
* it (directly, or across the same wall's footprint band). Projected
|
|
19
|
+
* EXACTLY onto a shared line so both neighbours emit the same seam
|
|
20
|
+
* and tile with no gap or overlap. Equal-height rooms partition the
|
|
21
|
+
* real wall band at its centerline. At an unequal-height boundary, the
|
|
22
|
+
* higher slab carries the full band to the lower room's wall face and
|
|
23
|
+
* the lower slab terminates at that same plane. This closes the step
|
|
24
|
+
* below the raised wall without lowering the wall or exposing a pocket.
|
|
25
|
+
* Without a wall, the target is the midline between the stored edges.
|
|
26
|
+
* The coincident vertical seam faces carry opposite outward
|
|
27
|
+
* normals and every slab material is front-side, so at most one face
|
|
28
|
+
* renders per view — no z-fighting.
|
|
29
|
+
* - WALL-BACKED — no slab neighbour across the edge, but the sub-edge
|
|
30
|
+
* lies inside a wall's footprint band (lateral distance from the
|
|
31
|
+
* centerline within half-thickness + adoption tolerance). Projected
|
|
32
|
+
* to the wall's OUTER face line so the slab reaches flush with the
|
|
33
|
+
* facade regardless of where the stored edge sits inside the band —
|
|
34
|
+
* this self-heals legacy data stored at wall faces or with old baked
|
|
35
|
+
* render offsets.
|
|
36
|
+
* - FREE — no neighbour, no wall. Rendered exactly as drawn.
|
|
37
|
+
*
|
|
38
|
+
* Sub-edges of one edge with different projections are joined by a
|
|
39
|
+
* perpendicular STEP connector at the breakpoint. Breakpoints sit on
|
|
40
|
+
* candidate span boundaries — wall junctions — so the step's vertical
|
|
41
|
+
* face lands inside the crossing wall's footprint and stays hidden
|
|
42
|
+
* under the wall body.
|
|
43
|
+
*/
|
|
44
|
+
/** Lateral distance within which a sibling slab edge counts as directly "across" an edge. */
|
|
45
|
+
const SLAB_NEIGHBOR_LATERAL_TOLERANCE = 0.05;
|
|
46
|
+
/**
|
|
47
|
+
* Extra lateral tolerance beyond a wall's half-thickness for band
|
|
48
|
+
* adoption. 0.06 keeps every previously-adopted edge adopted (the old
|
|
49
|
+
* fixed 0.1 tolerance equals half-thickness + 0.05 for the thinnest
|
|
50
|
+
* 0.1 walls, and auto slab polygons simplified with a 0.08 tolerance on
|
|
51
|
+
* curved walls stay inside half + 0.06), while giving hand-adjusted
|
|
52
|
+
* edges ~6cm of slack on either side of the wall faces.
|
|
53
|
+
*/
|
|
54
|
+
const WALL_ADOPTION_TOLERANCE = 0.06;
|
|
55
|
+
/** Minimum collinear overlap (m) before a candidate drives the classification. */
|
|
56
|
+
const MIN_CLASSIFYING_OVERLAP = 0.05;
|
|
57
|
+
/**
|
|
58
|
+
* Minimum sub-edge length (m) when splitting an edge at candidate span
|
|
59
|
+
* boundaries. Breakpoints closer than this to the previous one or to an
|
|
60
|
+
* edge endpoint are dropped, so slivers never survive into the ring.
|
|
61
|
+
*/
|
|
62
|
+
const MIN_SUBEDGE_LENGTH = 0.05;
|
|
63
|
+
/**
|
|
64
|
+
* Two candidate walls whose centerlines sit within this lateral
|
|
65
|
+
* difference count as equally near; the larger span overlap wins
|
|
66
|
+
* (collinear runs of different-thickness walls along one edge).
|
|
67
|
+
* Otherwise the nearest centerline wins (parallel close walls).
|
|
68
|
+
*/
|
|
69
|
+
const WALL_LATERAL_TIE_EPSILON = 0.02;
|
|
70
|
+
const CURVED_WALL_SAMPLE_SEGMENTS = 32;
|
|
71
|
+
const SLAB_SEAM_ELEVATION_EPSILON = 1e-4;
|
|
72
|
+
const DEFAULT_SLAB_ELEVATION = 0.05;
|
|
73
|
+
/** Prevent near-parallel offset lines from producing unbounded corner spikes. */
|
|
74
|
+
const MAX_CORNER_MITER_RATIO = 10;
|
|
75
|
+
function polygonWindingSign(polygon) {
|
|
76
|
+
let area2 = 0;
|
|
77
|
+
for (let index = 0; index < polygon.length; index += 1) {
|
|
78
|
+
const next = (index + 1) % polygon.length;
|
|
79
|
+
area2 += polygon[index][0] * polygon[next][1] - polygon[next][0] * polygon[index][1];
|
|
80
|
+
}
|
|
81
|
+
return area2 >= 0 ? 1 : -1;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Derive a {@link SlabPolygonContext} from a registry `GeometryContext`:
|
|
85
|
+
* sibling slabs come from `ctx.siblings` (same kind, same parent, self
|
|
86
|
+
* excluded) and walls from the parent level's children.
|
|
87
|
+
*/
|
|
88
|
+
export function slabPolygonContextFromGeometry(ctx) {
|
|
89
|
+
if (!ctx)
|
|
90
|
+
return { walls: [], siblingSlabs: [] };
|
|
91
|
+
const siblingSlabs = ctx.siblings.filter((node) => node.type === 'slab');
|
|
92
|
+
const walls = [];
|
|
93
|
+
const parentChildIds = ctx.parent?.children;
|
|
94
|
+
if (Array.isArray(parentChildIds)) {
|
|
95
|
+
for (const childId of parentChildIds) {
|
|
96
|
+
const child = ctx.resolve(childId);
|
|
97
|
+
if (child?.type === 'wall') {
|
|
98
|
+
walls.push(child);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { walls, siblingSlabs };
|
|
103
|
+
}
|
|
104
|
+
export function getRenderableSlabPolygon(slabNode, context) {
|
|
105
|
+
const polygon = slabNode.polygon;
|
|
106
|
+
if (polygon.length < 3) {
|
|
107
|
+
return polygon.map(([x, z]) => [x, z]);
|
|
108
|
+
}
|
|
109
|
+
const subSpans = computeEdgeSubSpans(polygon, slabNode.elevation ?? DEFAULT_SLAB_ELEVATION, context);
|
|
110
|
+
if (subSpans.every((spans) => spans.length === 1 && spans[0].offset === 0)) {
|
|
111
|
+
return polygon.map(([x, z]) => [x, z]);
|
|
112
|
+
}
|
|
113
|
+
return offsetPolygonPerEdge(polygon, subSpans);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Clip the candidate segment `(px, pz) → (qx, qz)` against the span of
|
|
117
|
+
* the edge starting at `(ax, az)` with normalized direction
|
|
118
|
+
* `(dirX, dirZ)`, counting only when the candidate is collinear with
|
|
119
|
+
* the edge within `lateralTolerance` (both candidate endpoints within
|
|
120
|
+
* that distance of the edge's infinite line — testing against the
|
|
121
|
+
* infinite line, not the edge span, is what lets a T-junction
|
|
122
|
+
* sub-segment match its longer host edge).
|
|
123
|
+
*/
|
|
124
|
+
function clipCollinearSegment(ax, az, dirX, dirZ, edgeLength, px, pz, qx, qz, lateralTolerance) {
|
|
125
|
+
const relPX = px - ax;
|
|
126
|
+
const relPZ = pz - az;
|
|
127
|
+
const latP = relPX * dirZ - relPZ * dirX;
|
|
128
|
+
if (Math.abs(latP) > lateralTolerance)
|
|
129
|
+
return null;
|
|
130
|
+
const relQX = qx - ax;
|
|
131
|
+
const relQZ = qz - az;
|
|
132
|
+
const latQ = relQX * dirZ - relQZ * dirX;
|
|
133
|
+
if (Math.abs(latQ) > lateralTolerance)
|
|
134
|
+
return null;
|
|
135
|
+
const t0 = relPX * dirX + relPZ * dirZ;
|
|
136
|
+
const t1 = relQX * dirX + relQZ * dirZ;
|
|
137
|
+
const low = Math.max(Math.min(t0, t1), 0);
|
|
138
|
+
const high = Math.min(Math.max(t0, t1), edgeLength);
|
|
139
|
+
const overlap = high - low;
|
|
140
|
+
if (overlap <= 0)
|
|
141
|
+
return null;
|
|
142
|
+
const tMid = (low + high) / 2;
|
|
143
|
+
const u = Math.abs(t1 - t0) < 1e-12 ? 0.5 : (tMid - t0) / (t1 - t0);
|
|
144
|
+
return { overlap, lateral: latP + (latQ - latP) * u, start: low, end: high };
|
|
145
|
+
}
|
|
146
|
+
function wallCenterlineSegments(wall) {
|
|
147
|
+
if (!isCurvedWall(wall)) {
|
|
148
|
+
return [[wall.start[0], wall.start[1], wall.end[0], wall.end[1]]];
|
|
149
|
+
}
|
|
150
|
+
const points = sampleWallCenterline(wall, CURVED_WALL_SAMPLE_SEGMENTS);
|
|
151
|
+
const segments = [];
|
|
152
|
+
for (let index = 0; index < points.length - 1; index += 1) {
|
|
153
|
+
const from = points[index];
|
|
154
|
+
const to = points[index + 1];
|
|
155
|
+
segments.push([from.x, from.y, to.x, to.y]);
|
|
156
|
+
}
|
|
157
|
+
return segments;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Best wall whose footprint band contains the edge: the centerline is
|
|
161
|
+
* collinear within half-thickness + {@link WALL_ADOPTION_TOLERANCE} and
|
|
162
|
+
* span-overlaps the edge by at least `requiredOverlap`. Nearest
|
|
163
|
+
* centerline wins; near-ties fall back to the larger overlap.
|
|
164
|
+
*/
|
|
165
|
+
function matchEdgeWallBand(ax, az, dirX, dirZ, edgeLength, requiredOverlap, candidates) {
|
|
166
|
+
let best = null;
|
|
167
|
+
for (const candidate of candidates) {
|
|
168
|
+
const tolerance = candidate.halfThickness + WALL_ADOPTION_TOLERANCE;
|
|
169
|
+
let overlap = 0;
|
|
170
|
+
let weightedLateral = 0;
|
|
171
|
+
for (const [px, pz, qx, qz] of candidate.segments) {
|
|
172
|
+
const match = clipCollinearSegment(ax, az, dirX, dirZ, edgeLength, px, pz, qx, qz, tolerance);
|
|
173
|
+
if (!match)
|
|
174
|
+
continue;
|
|
175
|
+
overlap += match.overlap;
|
|
176
|
+
weightedLateral += match.lateral * match.overlap;
|
|
177
|
+
}
|
|
178
|
+
if (overlap < requiredOverlap)
|
|
179
|
+
continue;
|
|
180
|
+
const lateral = weightedLateral / overlap;
|
|
181
|
+
if (!best) {
|
|
182
|
+
best = { wall: candidate.wall, halfThickness: candidate.halfThickness, overlap, lateral };
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const bestAbs = Math.abs(best.lateral);
|
|
186
|
+
const thisAbs = Math.abs(lateral);
|
|
187
|
+
const nearTie = Math.abs(thisAbs - bestAbs) <= WALL_LATERAL_TIE_EPSILON;
|
|
188
|
+
if (nearTie ? overlap > best.overlap : thisAbs < bestAbs) {
|
|
189
|
+
best = { wall: candidate.wall, halfThickness: candidate.halfThickness, overlap, lateral };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return best;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Reshape-snap counterpart of the render band rule: when the edge
|
|
196
|
+
* `a → b` lies inside a wall's footprint band, return the edge
|
|
197
|
+
* translated onto that wall's CENTERLINE — the canonical stored
|
|
198
|
+
* position (matching what auto slabs store); the render rule then
|
|
199
|
+
* places it at the face. `maxLateral` optionally tightens the stick
|
|
200
|
+
* distance (non-magnetic modes keep only a connect-radius stick).
|
|
201
|
+
*/
|
|
202
|
+
export function snapSlabEdgeToWallBand(a, b, walls, options) {
|
|
203
|
+
const dx = b[0] - a[0];
|
|
204
|
+
const dz = b[1] - a[1];
|
|
205
|
+
const edgeLength = Math.hypot(dx, dz);
|
|
206
|
+
if (edgeLength < 1e-9)
|
|
207
|
+
return null;
|
|
208
|
+
const dirX = dx / edgeLength;
|
|
209
|
+
const dirZ = dz / edgeLength;
|
|
210
|
+
const requiredOverlap = Math.min(MIN_CLASSIFYING_OVERLAP, edgeLength * 0.5);
|
|
211
|
+
const candidates = walls.map((wall) => ({
|
|
212
|
+
wall,
|
|
213
|
+
segments: wallCenterlineSegments(wall),
|
|
214
|
+
halfThickness: getWallThickness(wall) / 2,
|
|
215
|
+
}));
|
|
216
|
+
const match = matchEdgeWallBand(a[0], a[1], dirX, dirZ, edgeLength, requiredOverlap, candidates);
|
|
217
|
+
if (!match)
|
|
218
|
+
return null;
|
|
219
|
+
if (options?.maxLateral !== undefined && Math.abs(match.lateral) > options.maxLateral)
|
|
220
|
+
return null;
|
|
221
|
+
const nx = dirZ * match.lateral;
|
|
222
|
+
const nz = -dirX * match.lateral;
|
|
223
|
+
return {
|
|
224
|
+
wallId: match.wall.id,
|
|
225
|
+
edge: [
|
|
226
|
+
[a[0] + nx, a[1] + nz],
|
|
227
|
+
[b[0] + nx, b[1] + nz],
|
|
228
|
+
],
|
|
229
|
+
};
|
|
10
230
|
}
|
|
11
231
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
232
|
+
* Split every polygon edge at candidate span boundaries and classify
|
|
233
|
+
* each sub-span independently. Returns one non-empty span list per
|
|
234
|
+
* edge, covering [0, edgeLength] without gaps.
|
|
14
235
|
*/
|
|
15
|
-
function
|
|
236
|
+
function computeEdgeSubSpans(polygon, selfElevation, context) {
|
|
237
|
+
const n = polygon.length;
|
|
238
|
+
// Winding sign: the outward normal of an edge with direction `dir` is
|
|
239
|
+
// `s * (dirZ, -dirX)`, so a target lateral `L` measured along
|
|
240
|
+
// `(dirZ, -dirX)` is `s * L` along the outward normal.
|
|
241
|
+
const s = polygonWindingSign(polygon);
|
|
242
|
+
const neighborSegments = [];
|
|
243
|
+
for (const sibling of context.siblingSlabs) {
|
|
244
|
+
const siblingPolygon = sibling.polygon;
|
|
245
|
+
if (siblingPolygon.length < 2)
|
|
246
|
+
continue;
|
|
247
|
+
const elevation = sibling.elevation ?? DEFAULT_SLAB_ELEVATION;
|
|
248
|
+
const siblingWinding = polygonWindingSign(siblingPolygon);
|
|
249
|
+
for (let index = 0; index < siblingPolygon.length; index += 1) {
|
|
250
|
+
const from = siblingPolygon[index];
|
|
251
|
+
const to = siblingPolygon[(index + 1) % siblingPolygon.length];
|
|
252
|
+
const dx = to[0] - from[0];
|
|
253
|
+
const dz = to[1] - from[1];
|
|
254
|
+
const length = Math.hypot(dx, dz);
|
|
255
|
+
if (length < 1e-9)
|
|
256
|
+
continue;
|
|
257
|
+
neighborSegments.push({
|
|
258
|
+
segment: [from[0], from[1], to[0], to[1]],
|
|
259
|
+
elevation,
|
|
260
|
+
inwardX: (-siblingWinding * dz) / length,
|
|
261
|
+
inwardZ: (siblingWinding * dx) / length,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const wallCandidates = context.walls.map((wall) => ({
|
|
266
|
+
wall,
|
|
267
|
+
segments: wallCenterlineSegments(wall),
|
|
268
|
+
halfThickness: getWallThickness(wall) / 2,
|
|
269
|
+
}));
|
|
270
|
+
// Sibling breakpoints also matter for legacy face-aligned polygons up
|
|
271
|
+
// to a full wall band away from the edge (the band-sibling interior
|
|
272
|
+
// rule in classifySpan), so clip them with the widest band reach any
|
|
273
|
+
// level wall allows. Over-collection is harmless — same-target
|
|
274
|
+
// neighbours re-fuse after classification.
|
|
275
|
+
let siblingBreakTolerance = SLAB_NEIGHBOR_LATERAL_TOLERANCE;
|
|
276
|
+
for (const candidate of wallCandidates) {
|
|
277
|
+
siblingBreakTolerance = Math.max(siblingBreakTolerance, 2 * (candidate.halfThickness + WALL_ADOPTION_TOLERANCE));
|
|
278
|
+
}
|
|
279
|
+
const subSpans = [];
|
|
280
|
+
for (let index = 0; index < n; index += 1) {
|
|
281
|
+
const a = polygon[index];
|
|
282
|
+
const b = polygon[(index + 1) % n];
|
|
283
|
+
const dx = b[0] - a[0];
|
|
284
|
+
const dz = b[1] - a[1];
|
|
285
|
+
const edgeLength = Math.hypot(dx, dz);
|
|
286
|
+
if (edgeLength < 1e-9) {
|
|
287
|
+
subSpans.push([{ start: 0, end: edgeLength, offset: 0, key: 'free' }]);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const dirX = dx / edgeLength;
|
|
291
|
+
const dirZ = dz / edgeLength;
|
|
292
|
+
const rawBreakpoints = [];
|
|
293
|
+
for (const candidate of wallCandidates) {
|
|
294
|
+
const tolerance = candidate.halfThickness + WALL_ADOPTION_TOLERANCE;
|
|
295
|
+
for (const [px, pz, qx, qz] of candidate.segments) {
|
|
296
|
+
const match = clipCollinearSegment(a[0], a[1], dirX, dirZ, edgeLength, px, pz, qx, qz, tolerance);
|
|
297
|
+
if (match)
|
|
298
|
+
rawBreakpoints.push(match.start, match.end);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const inwardX = -s * dirZ;
|
|
302
|
+
const inwardZ = s * dirX;
|
|
303
|
+
for (const { segment: [px, pz, qx, qz], inwardX: siblingInwardX, inwardZ: siblingInwardZ, } of neighborSegments) {
|
|
304
|
+
// Coincident/stacked slabs have their interiors on the same side of
|
|
305
|
+
// the edge. Only an edge whose sibling interior is across this edge
|
|
306
|
+
// can form a room-to-room seam.
|
|
307
|
+
if (inwardX * siblingInwardX + inwardZ * siblingInwardZ >= -0.5)
|
|
308
|
+
continue;
|
|
309
|
+
const match = clipCollinearSegment(a[0], a[1], dirX, dirZ, edgeLength, px, pz, qx, qz, siblingBreakTolerance);
|
|
310
|
+
if (match)
|
|
311
|
+
rawBreakpoints.push(match.start, match.end);
|
|
312
|
+
}
|
|
313
|
+
rawBreakpoints.sort((left, right) => left - right);
|
|
314
|
+
const breakpoints = [];
|
|
315
|
+
let previous = 0;
|
|
316
|
+
for (const t of rawBreakpoints) {
|
|
317
|
+
if (t - previous < MIN_SUBEDGE_LENGTH)
|
|
318
|
+
continue;
|
|
319
|
+
// Sorted ascending — every later breakpoint is even closer to the end.
|
|
320
|
+
if (edgeLength - t < MIN_SUBEDGE_LENGTH)
|
|
321
|
+
break;
|
|
322
|
+
breakpoints.push(t);
|
|
323
|
+
previous = t;
|
|
324
|
+
}
|
|
325
|
+
const bounds = [0, ...breakpoints, edgeLength];
|
|
326
|
+
const spans = [];
|
|
327
|
+
for (let k = 0; k + 1 < bounds.length; k += 1) {
|
|
328
|
+
spans.push(classifySpan(a, dirX, dirZ, bounds[k], bounds[k + 1], s, selfElevation, wallCandidates, neighborSegments));
|
|
329
|
+
}
|
|
330
|
+
// Re-fuse same-target neighbours, reclassifying over the fused span:
|
|
331
|
+
// curved-wall sampling collapses back to one span per contiguous
|
|
332
|
+
// band, and an edge fully backed by one target reproduces the
|
|
333
|
+
// whole-edge projection exactly.
|
|
334
|
+
let fused = true;
|
|
335
|
+
while (fused && spans.length > 1) {
|
|
336
|
+
fused = false;
|
|
337
|
+
for (let k = 0; k + 1 < spans.length; k += 1) {
|
|
338
|
+
if (spans[k].key !== spans[k + 1].key)
|
|
339
|
+
continue;
|
|
340
|
+
const merged = classifySpan(a, dirX, dirZ, spans[k].start, spans[k + 1].end, s, selfElevation, wallCandidates, neighborSegments);
|
|
341
|
+
spans.splice(k, 2, merged);
|
|
342
|
+
fused = true;
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
subSpans.push(spans);
|
|
347
|
+
}
|
|
348
|
+
return subSpans;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Classify one span of the edge `a + t·dir`, `t ∈ [start, end]`, with
|
|
352
|
+
* the whole-edge rules: direct sibling wins, sibling across the same
|
|
353
|
+
* wall band forces interior, otherwise the matched wall's outer face,
|
|
354
|
+
* otherwise free. Interior spans backed by a wall project onto the
|
|
355
|
+
* wall centerline at equal heights. At unequal heights both slabs target
|
|
356
|
+
* the lower room's wall face, giving the higher slab the full band.
|
|
357
|
+
*/
|
|
358
|
+
function classifySpan(a, dirX, dirZ, start, end, s, selfElevation, wallCandidates, neighborSegments) {
|
|
359
|
+
const sx = a[0] + dirX * start;
|
|
360
|
+
const sz = a[1] + dirZ * start;
|
|
361
|
+
const spanLength = end - start;
|
|
362
|
+
// Short spans still need classification — require at most half their span.
|
|
363
|
+
const requiredOverlap = Math.min(MIN_CLASSIFYING_OVERLAP, spanLength * 0.5);
|
|
364
|
+
const inwardX = -s * dirZ;
|
|
365
|
+
const inwardZ = s * dirX;
|
|
366
|
+
const wallMatch = matchEdgeWallBand(sx, sz, dirX, dirZ, spanLength, requiredOverlap, wallCandidates);
|
|
367
|
+
// Direct neighbour: a sibling edge collinear within the tight
|
|
368
|
+
// tolerance regardless of any wall (slabs butted against each other).
|
|
369
|
+
let directOverlap = 0;
|
|
370
|
+
let directWeightedLateral = 0;
|
|
371
|
+
let directSiblingElevation = null;
|
|
372
|
+
for (const { segment: [px, pz, qx, qz], elevation, inwardX: siblingInwardX, inwardZ: siblingInwardZ, } of neighborSegments) {
|
|
373
|
+
if (inwardX * siblingInwardX + inwardZ * siblingInwardZ >= -0.5)
|
|
374
|
+
continue;
|
|
375
|
+
const match = clipCollinearSegment(sx, sz, dirX, dirZ, spanLength, px, pz, qx, qz, SLAB_NEIGHBOR_LATERAL_TOLERANCE);
|
|
376
|
+
if (!match)
|
|
377
|
+
continue;
|
|
378
|
+
directOverlap += match.overlap;
|
|
379
|
+
directWeightedLateral += match.lateral * match.overlap;
|
|
380
|
+
directSiblingElevation =
|
|
381
|
+
directSiblingElevation === null ? elevation : Math.max(directSiblingElevation, elevation);
|
|
382
|
+
}
|
|
383
|
+
let interiorLateral = null;
|
|
384
|
+
let siblingElevation = null;
|
|
385
|
+
if (directOverlap >= requiredOverlap) {
|
|
386
|
+
// No-wall fallback: half the mean sibling separation — the midline
|
|
387
|
+
// between the two stored edges. Symmetric: the sibling measures the
|
|
388
|
+
// same separation with opposite sign from its own line, so both
|
|
389
|
+
// project onto the same line and the seam stays gapless.
|
|
390
|
+
interiorLateral = wallMatch ? wallMatch.lateral : directWeightedLateral / directOverlap / 2;
|
|
391
|
+
siblingElevation = directSiblingElevation;
|
|
392
|
+
}
|
|
393
|
+
else if (wallMatch) {
|
|
394
|
+
// Rooms across a shared wall: legacy face-aligned polygons sit a
|
|
395
|
+
// full thickness apart — far beyond the direct tolerance — but
|
|
396
|
+
// both edges live inside the same wall band. Both sides must
|
|
397
|
+
// classify interior, otherwise each would project to the opposite
|
|
398
|
+
// outer face and overlap under the wall.
|
|
399
|
+
const bandTolerance = wallMatch.halfThickness + WALL_ADOPTION_TOLERANCE;
|
|
400
|
+
const looseTolerance = Math.abs(wallMatch.lateral) + bandTolerance;
|
|
401
|
+
let bandOverlap = 0;
|
|
402
|
+
for (const { segment: [px, pz, qx, qz], elevation, inwardX: siblingInwardX, inwardZ: siblingInwardZ, } of neighborSegments) {
|
|
403
|
+
if (inwardX * siblingInwardX + inwardZ * siblingInwardZ >= -0.5)
|
|
404
|
+
continue;
|
|
405
|
+
const match = clipCollinearSegment(sx, sz, dirX, dirZ, spanLength, px, pz, qx, qz, looseTolerance);
|
|
406
|
+
if (!match)
|
|
407
|
+
continue;
|
|
408
|
+
if (Math.abs(match.lateral - wallMatch.lateral) > bandTolerance)
|
|
409
|
+
continue;
|
|
410
|
+
bandOverlap += match.overlap;
|
|
411
|
+
siblingElevation =
|
|
412
|
+
siblingElevation === null ? elevation : Math.max(siblingElevation, elevation);
|
|
413
|
+
}
|
|
414
|
+
if (bandOverlap >= requiredOverlap) {
|
|
415
|
+
interiorLateral = wallMatch.lateral;
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
siblingElevation = null;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (interiorLateral !== null) {
|
|
422
|
+
if (wallMatch && siblingElevation !== null) {
|
|
423
|
+
const elevationDelta = selfElevation - siblingElevation;
|
|
424
|
+
if (elevationDelta > SLAB_SEAM_ELEVATION_EPSILON) {
|
|
425
|
+
return {
|
|
426
|
+
start,
|
|
427
|
+
end,
|
|
428
|
+
offset: s * wallMatch.lateral + wallMatch.halfThickness,
|
|
429
|
+
key: `interior|${wallMatch.wall.id}|higher`,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
if (elevationDelta < -SLAB_SEAM_ELEVATION_EPSILON) {
|
|
433
|
+
return {
|
|
434
|
+
start,
|
|
435
|
+
end,
|
|
436
|
+
offset: s * wallMatch.lateral - wallMatch.halfThickness,
|
|
437
|
+
key: `interior|${wallMatch.wall.id}|lower`,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
start,
|
|
443
|
+
end,
|
|
444
|
+
offset: s * interiorLateral,
|
|
445
|
+
key: `interior|${wallMatch ? wallMatch.wall.id : '~'}`,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
if (wallMatch) {
|
|
449
|
+
return {
|
|
450
|
+
start,
|
|
451
|
+
end,
|
|
452
|
+
offset: s * wallMatch.lateral + wallMatch.halfThickness,
|
|
453
|
+
key: `wall|${wallMatch.wall.id}`,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
return { start, end, offset: 0, key: 'free' };
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Offset each edge's sub-spans along the edge's outward normal by their
|
|
460
|
+
* own amounts (negative insets), then rebuild the ring: consecutive
|
|
461
|
+
* sub-spans of one edge with different offsets are joined by a
|
|
462
|
+
* perpendicular step connector at the breakpoint (both boundary points
|
|
463
|
+
* share the breakpoint param), and corners between different edges
|
|
464
|
+
* intersect the offset lines of the adjoining sub-spans.
|
|
465
|
+
*/
|
|
466
|
+
function offsetPolygonPerEdge(polygon, subSpans) {
|
|
16
467
|
const n = polygon.length;
|
|
17
468
|
if (n < 3)
|
|
18
|
-
return polygon;
|
|
469
|
+
return polygon.map(([x, z]) => [x, z]);
|
|
19
470
|
// Determine winding via signed area
|
|
20
471
|
let area2 = 0;
|
|
21
472
|
for (let i = 0; i < n; i++) {
|
|
@@ -23,35 +474,78 @@ function outsetPolygon(polygon, amount) {
|
|
|
23
474
|
area2 += polygon[i][0] * polygon[j][1] - polygon[j][0] * polygon[i][1];
|
|
24
475
|
}
|
|
25
476
|
const s = area2 >= 0 ? 1 : -1;
|
|
26
|
-
|
|
27
|
-
const offEdges = [];
|
|
477
|
+
const frames = [];
|
|
28
478
|
for (let i = 0; i < n; i++) {
|
|
29
479
|
const j = (i + 1) % n;
|
|
30
480
|
const dx = polygon[j][0] - polygon[i][0];
|
|
31
481
|
const dz = polygon[j][1] - polygon[i][1];
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
const nx = ((s * dz) / len) * amount;
|
|
38
|
-
const nz = ((s * -dx) / len) * amount;
|
|
39
|
-
offEdges.push([polygon[i][0] + nx, polygon[i][1] + nz, dx, dz]);
|
|
482
|
+
const length = Math.hypot(dx, dz);
|
|
483
|
+
const dirX = length < 1e-9 ? 0 : dx / length;
|
|
484
|
+
const dirZ = length < 1e-9 ? 0 : dz / length;
|
|
485
|
+
frames.push({ ax: polygon[i][0], az: polygon[i][1], dx, dz, dirX, dirZ });
|
|
40
486
|
}
|
|
41
|
-
|
|
487
|
+
const pointAt = (edge, t, offset) => {
|
|
488
|
+
const frame = frames[edge];
|
|
489
|
+
return [
|
|
490
|
+
frame.ax + frame.dirX * t + s * frame.dirZ * offset,
|
|
491
|
+
frame.az + frame.dirZ * t - s * frame.dirX * offset,
|
|
492
|
+
];
|
|
493
|
+
};
|
|
42
494
|
const result = [];
|
|
495
|
+
const push = (point) => {
|
|
496
|
+
const previous = result[result.length - 1];
|
|
497
|
+
if (previous && Math.hypot(previous[0] - point[0], previous[1] - point[1]) < 1e-9)
|
|
498
|
+
return;
|
|
499
|
+
result.push(point);
|
|
500
|
+
};
|
|
43
501
|
for (let i = 0; i < n; i++) {
|
|
502
|
+
const spans = subSpans[i];
|
|
503
|
+
// Step connectors between differently-projected sub-spans of edge i.
|
|
504
|
+
// Equal offsets (different walls sharing a face line) collapse to a
|
|
505
|
+
// single collinear vertex via the coincident-point guard in `push`.
|
|
506
|
+
for (let k = 1; k < spans.length; k += 1) {
|
|
507
|
+
push(pointAt(i, spans[k - 1].end, spans[k - 1].offset));
|
|
508
|
+
push(pointAt(i, spans[k].start, spans[k].offset));
|
|
509
|
+
}
|
|
510
|
+
// Corner between edge i and edge j: intersect the offset lines of
|
|
511
|
+
// edge i's last sub-span and edge j's first sub-span.
|
|
44
512
|
const j = (i + 1) % n;
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
513
|
+
const last = spans[spans.length - 1];
|
|
514
|
+
const first = subSpans[j][0];
|
|
515
|
+
const [ax, az] = pointAt(i, last.start, last.offset);
|
|
516
|
+
const endI = pointAt(i, last.end, last.offset);
|
|
517
|
+
const startJ = pointAt(j, first.start, first.offset);
|
|
518
|
+
const [bx, bz] = startJ;
|
|
519
|
+
const frameI = frames[i];
|
|
520
|
+
const frameJ = frames[j];
|
|
521
|
+
const denom = frameI.dx * frameJ.dz - frameI.dz * frameJ.dx;
|
|
48
522
|
if (Math.abs(denom) < 1e-9) {
|
|
49
|
-
// Parallel edges
|
|
50
|
-
|
|
523
|
+
// Parallel edges have no unique intersection. Emit both offset
|
|
524
|
+
// endpoints — collinear edges with different offsets need the step
|
|
525
|
+
// between them.
|
|
526
|
+
push(endI);
|
|
527
|
+
push(startJ);
|
|
51
528
|
}
|
|
52
529
|
else {
|
|
53
|
-
const t = ((bx - ax) *
|
|
54
|
-
|
|
530
|
+
const t = ((bx - ax) * frameJ.dz - (bz - az) * frameJ.dx) / denom;
|
|
531
|
+
const intersection = [ax + t * frameI.dx, az + t * frameI.dz];
|
|
532
|
+
const miterReach = Math.max(Math.hypot(intersection[0] - endI[0], intersection[1] - endI[1]), Math.hypot(intersection[0] - startJ[0], intersection[1] - startJ[1]));
|
|
533
|
+
const offsetScale = Math.max(Math.abs(last.offset), Math.abs(first.offset), 1e-9);
|
|
534
|
+
if (!(Number.isFinite(intersection[0]) && Number.isFinite(intersection[1])) ||
|
|
535
|
+
miterReach > offsetScale * MAX_CORNER_MITER_RATIO) {
|
|
536
|
+
push(endI);
|
|
537
|
+
push(startJ);
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
push(intersection);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (result.length > 1) {
|
|
545
|
+
const firstPoint = result[0];
|
|
546
|
+
const lastPoint = result[result.length - 1];
|
|
547
|
+
if (Math.hypot(firstPoint[0] - lastPoint[0], firstPoint[1] - lastPoint[1]) < 1e-9) {
|
|
548
|
+
result.pop();
|
|
55
549
|
}
|
|
56
550
|
}
|
|
57
551
|
return result;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slab-polygon.test.d.ts","sourceRoot":"","sources":["../../src/lib/slab-polygon.test.ts"],"names":[],"mappings":""}
|