@pascal-app/core 0.8.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/events/bus.d.ts +67 -13
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/scene-registry/item-clip-registry.d.ts +18 -0
- package/dist/hooks/scene-registry/item-clip-registry.d.ts.map +1 -0
- package/dist/hooks/scene-registry/item-clip-registry.js +10 -0
- package/dist/hooks/scene-registry/scene-registry.d.ts +14 -23
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +59 -26
- package/dist/hooks/scene-registry/scene-registry.test.d.ts +2 -0
- package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +1 -0
- package/dist/hooks/scene-registry/scene-registry.test.js +26 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +366 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +79 -11
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +508 -141
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +109 -18
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +363 -0
- package/dist/index.d.ts +36 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -6
- package/dist/lib/level-name.d.ts +4 -0
- package/dist/lib/level-name.d.ts.map +1 -0
- package/dist/lib/level-name.js +10 -0
- package/dist/lib/measurement-geometry.d.ts +19 -0
- package/dist/lib/measurement-geometry.d.ts.map +1 -0
- package/dist/lib/measurement-geometry.js +232 -0
- package/dist/lib/measurement-geometry.test.d.ts +2 -0
- package/dist/lib/measurement-geometry.test.d.ts.map +1 -0
- package/dist/lib/measurement-geometry.test.js +102 -0
- package/dist/lib/polygon-geometry.d.ts +0 -1
- package/dist/lib/polygon-geometry.d.ts.map +1 -1
- package/dist/lib/polygon-geometry.js +0 -18
- package/dist/lib/polygon-relations.d.ts +10 -0
- package/dist/lib/polygon-relations.d.ts.map +1 -0
- package/dist/lib/polygon-relations.js +73 -0
- package/dist/lib/selection-proxy.d.ts +4 -0
- package/dist/lib/selection-proxy.d.ts.map +1 -0
- package/dist/lib/selection-proxy.js +12 -0
- package/dist/lib/slab-polygon.d.ts +31 -2
- package/dist/lib/slab-polygon.d.ts.map +1 -1
- package/dist/lib/slab-polygon.js +525 -31
- package/dist/lib/slab-polygon.test.d.ts +2 -0
- package/dist/lib/slab-polygon.test.d.ts.map +1 -0
- package/dist/lib/slab-polygon.test.js +635 -0
- package/dist/lib/slots.d.ts +14 -0
- package/dist/lib/slots.d.ts.map +1 -0
- package/dist/lib/slots.js +26 -0
- package/dist/lib/space-detection-pause.test.d.ts +2 -0
- package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
- package/dist/lib/space-detection-pause.test.js +42 -0
- package/dist/lib/space-detection.d.ts +54 -2
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +599 -124
- package/dist/lib/space-detection.test.d.ts +2 -0
- package/dist/lib/space-detection.test.d.ts.map +1 -0
- package/dist/lib/space-detection.test.js +376 -0
- package/dist/lib/wall-distance.d.ts +60 -0
- package/dist/lib/wall-distance.d.ts.map +1 -0
- package/dist/lib/wall-distance.js +85 -0
- package/dist/lib/zone-quantities.d.ts +21 -0
- package/dist/lib/zone-quantities.d.ts.map +1 -0
- package/dist/lib/zone-quantities.js +183 -0
- package/dist/lib/zone-quantities.test.d.ts +2 -0
- package/dist/lib/zone-quantities.test.d.ts.map +1 -0
- package/dist/lib/zone-quantities.test.js +82 -0
- package/dist/material-library.d.ts +20 -1
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +3737 -200
- package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
- package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
- package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
- package/dist/registry/handles.d.ts +394 -0
- package/dist/registry/handles.d.ts.map +1 -0
- package/dist/registry/handles.js +24 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/registry.d.ts +136 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +282 -0
- package/dist/registry/registry.test.d.ts +2 -0
- package/dist/registry/registry.test.d.ts.map +1 -0
- package/dist/registry/registry.test.js +196 -0
- package/dist/registry/relations-resolver.d.ts +42 -0
- package/dist/registry/relations-resolver.d.ts.map +1 -0
- package/dist/registry/relations-resolver.js +77 -0
- package/dist/registry/relations-resolver.test.d.ts +2 -0
- package/dist/registry/relations-resolver.test.d.ts.map +1 -0
- package/dist/registry/relations-resolver.test.js +183 -0
- package/dist/registry/scene-api.d.ts +44 -0
- package/dist/registry/scene-api.d.ts.map +1 -0
- package/dist/registry/scene-api.js +111 -0
- package/dist/registry/scene-api.test.d.ts +2 -0
- package/dist/registry/scene-api.test.d.ts.map +1 -0
- package/dist/registry/scene-api.test.js +183 -0
- package/dist/registry/subtree.d.ts +67 -0
- package/dist/registry/subtree.d.ts.map +1 -0
- package/dist/registry/subtree.js +123 -0
- package/dist/registry/subtree.test.d.ts +2 -0
- package/dist/registry/subtree.test.d.ts.map +1 -0
- package/dist/registry/subtree.test.js +163 -0
- package/dist/registry/types.d.ts +2099 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +1 -0
- package/dist/schema/index.d.ts +35 -4
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +32 -4
- package/dist/schema/material.d.ts +13 -2
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +17 -5
- package/dist/schema/material.test.d.ts +2 -0
- package/dist/schema/material.test.d.ts.map +1 -0
- package/dist/schema/material.test.js +43 -0
- package/dist/schema/nodes/box-vent.d.ts +74 -0
- package/dist/schema/nodes/box-vent.d.ts.map +1 -0
- package/dist/schema/nodes/box-vent.js +63 -0
- package/dist/schema/nodes/building.d.ts +1 -1
- package/dist/schema/nodes/building.d.ts.map +1 -1
- package/dist/schema/nodes/building.js +3 -2
- package/dist/schema/nodes/cabinet.d.ts +534 -0
- package/dist/schema/nodes/cabinet.d.ts.map +1 -0
- package/dist/schema/nodes/cabinet.js +137 -0
- package/dist/schema/nodes/ceiling.d.ts +5 -2
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +5 -1
- package/dist/schema/nodes/chimney.d.ts +158 -0
- package/dist/schema/nodes/chimney.d.ts.map +1 -0
- package/dist/schema/nodes/chimney.js +67 -0
- package/dist/schema/nodes/column.d.ts +600 -7
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +589 -6
- package/dist/schema/nodes/cupola.d.ts +66 -0
- package/dist/schema/nodes/cupola.d.ts.map +1 -0
- package/dist/schema/nodes/cupola.js +28 -0
- package/dist/schema/nodes/door.d.ts +13 -5
- package/dist/schema/nodes/door.d.ts.map +1 -1
- package/dist/schema/nodes/door.js +12 -0
- package/dist/schema/nodes/dormer.d.ts +228 -0
- package/dist/schema/nodes/dormer.d.ts.map +1 -0
- package/dist/schema/nodes/dormer.js +119 -0
- package/dist/schema/nodes/downspout.d.ts +75 -0
- package/dist/schema/nodes/downspout.d.ts.map +1 -0
- package/dist/schema/nodes/downspout.js +63 -0
- package/dist/schema/nodes/duct-fitting.d.ts +91 -0
- package/dist/schema/nodes/duct-fitting.d.ts.map +1 -0
- package/dist/schema/nodes/duct-fitting.js +92 -0
- package/dist/schema/nodes/duct-segment.d.ts +61 -0
- package/dist/schema/nodes/duct-segment.d.ts.map +1 -0
- package/dist/schema/nodes/duct-segment.js +73 -0
- package/dist/schema/nodes/duct-terminal.d.ts +57 -0
- package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
- package/dist/schema/nodes/duct-terminal.js +51 -0
- package/dist/schema/nodes/elevator.d.ts +106 -0
- package/dist/schema/nodes/elevator.d.ts.map +1 -0
- package/dist/schema/nodes/elevator.js +57 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
- package/dist/schema/nodes/eyebrow-vent.js +40 -0
- package/dist/schema/nodes/fence.d.ts +19 -2
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +27 -2
- package/dist/schema/nodes/gutter.d.ts +84 -0
- package/dist/schema/nodes/gutter.d.ts.map +1 -0
- package/dist/schema/nodes/gutter.js +76 -0
- package/dist/schema/nodes/hvac-equipment.d.ts +63 -0
- package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
- package/dist/schema/nodes/hvac-equipment.js +55 -0
- package/dist/schema/nodes/item.d.ts +12 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +20 -0
- package/dist/schema/nodes/level.d.ts +1 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +4 -0
- package/dist/schema/nodes/lineset.d.ts +42 -0
- package/dist/schema/nodes/lineset.d.ts.map +1 -0
- package/dist/schema/nodes/lineset.js +38 -0
- package/dist/schema/nodes/liquid-line.d.ts +34 -0
- package/dist/schema/nodes/liquid-line.d.ts.map +1 -0
- package/dist/schema/nodes/liquid-line.js +24 -0
- package/dist/schema/nodes/measurement.d.ts +306 -0
- package/dist/schema/nodes/measurement.d.ts.map +1 -0
- package/dist/schema/nodes/measurement.js +87 -0
- package/dist/schema/nodes/measurement.test.d.ts +2 -0
- package/dist/schema/nodes/measurement.test.d.ts.map +1 -0
- package/dist/schema/nodes/measurement.test.js +139 -0
- package/dist/schema/nodes/pipe-fitting.d.ts +56 -0
- package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-fitting.js +45 -0
- package/dist/schema/nodes/pipe-segment.d.ts +48 -0
- package/dist/schema/nodes/pipe-segment.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-segment.js +36 -0
- package/dist/schema/nodes/pipe-trap.d.ts +43 -0
- package/dist/schema/nodes/pipe-trap.d.ts.map +1 -0
- package/dist/schema/nodes/pipe-trap.js +36 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +2 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent-defaults.test.js +205 -0
- package/dist/schema/nodes/ridge-vent.d.ts +82 -0
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent.js +248 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts +90 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-shape.js +318 -0
- package/dist/schema/nodes/roof-segment-shape.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-shape.test.js +175 -0
- package/dist/schema/nodes/roof-segment-surface.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-surface.test.js +48 -0
- package/dist/schema/nodes/roof-segment-trim.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-trim.test.js +107 -0
- package/dist/schema/nodes/roof-segment-walls.d.ts +101 -0
- package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-walls.js +330 -0
- package/dist/schema/nodes/roof-segment-walls.test.d.ts +2 -0
- package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-walls.test.js +92 -0
- package/dist/schema/nodes/roof-segment.d.ts +273 -3
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +539 -3
- package/dist/schema/nodes/roof.d.ts +8 -8
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +4 -16
- package/dist/schema/nodes/shelf.d.ts +106 -0
- package/dist/schema/nodes/shelf.d.ts.map +1 -0
- package/dist/schema/nodes/shelf.js +90 -0
- package/dist/schema/nodes/site.d.ts +1 -122
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/site.js +2 -6
- package/dist/schema/nodes/skylight.d.ts +279 -0
- package/dist/schema/nodes/skylight.d.ts.map +1 -0
- package/dist/schema/nodes/skylight.js +152 -0
- package/dist/schema/nodes/slab.d.ts +5 -2
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +5 -1
- package/dist/schema/nodes/solar-panel.d.ts +117 -0
- package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
- package/dist/schema/nodes/solar-panel.js +47 -0
- package/dist/schema/nodes/stair-segment.d.ts +4 -4
- package/dist/schema/nodes/stair.d.ts +13 -12
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +3 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
- package/dist/schema/nodes/surface-hole-metadata.js +3 -1
- package/dist/schema/nodes/turbine-vent.d.ts +68 -0
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
- package/dist/schema/nodes/turbine-vent.js +41 -0
- package/dist/schema/nodes/wall.d.ts +245 -6
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +257 -0
- package/dist/schema/nodes/wall.test.d.ts +2 -0
- package/dist/schema/nodes/wall.test.d.ts.map +1 -0
- package/dist/schema/nodes/wall.test.js +208 -0
- package/dist/schema/nodes/window.d.ts +11 -3
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +12 -0
- package/dist/schema/nodes/zone.d.ts +2 -0
- package/dist/schema/nodes/zone.d.ts.map +1 -1
- package/dist/schema/nodes/zone.js +6 -0
- package/dist/schema/scene-material.d.ts +41 -0
- package/dist/schema/scene-material.d.ts.map +1 -0
- package/dist/schema/scene-material.js +9 -0
- package/dist/schema/types.d.ts +2850 -655
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +49 -0
- package/dist/services/alignment-anchors.d.ts +100 -0
- package/dist/services/alignment-anchors.d.ts.map +1 -0
- package/dist/services/alignment-anchors.js +312 -0
- package/dist/services/alignment-anchors.test.d.ts +2 -0
- package/dist/services/alignment-anchors.test.d.ts.map +1 -0
- package/dist/services/alignment-anchors.test.js +383 -0
- package/dist/services/alignment.d.ts +131 -0
- package/dist/services/alignment.d.ts.map +1 -0
- package/dist/services/alignment.js +171 -0
- package/dist/services/alignment.test.d.ts +2 -0
- package/dist/services/alignment.test.d.ts.map +1 -0
- package/dist/services/alignment.test.js +92 -0
- package/dist/services/drag-session.d.ts +47 -0
- package/dist/services/drag-session.d.ts.map +1 -0
- package/dist/services/drag-session.js +100 -0
- package/dist/services/drag-session.test.d.ts +2 -0
- package/dist/services/drag-session.test.d.ts.map +1 -0
- package/dist/services/drag-session.test.js +219 -0
- package/dist/services/hosting.d.ts +88 -0
- package/dist/services/hosting.d.ts.map +1 -0
- package/dist/services/hosting.js +131 -0
- package/dist/services/hosting.test.d.ts +2 -0
- package/dist/services/hosting.test.d.ts.map +1 -0
- package/dist/services/hosting.test.js +230 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +12 -0
- package/dist/services/level-height.d.ts +28 -0
- package/dist/services/level-height.d.ts.map +1 -0
- package/dist/services/level-height.js +62 -0
- package/dist/services/movement.d.ts +48 -0
- package/dist/services/movement.d.ts.map +1 -0
- package/dist/services/movement.js +65 -0
- package/dist/services/movement.test.d.ts +2 -0
- package/dist/services/movement.test.d.ts.map +1 -0
- package/dist/services/movement.test.js +133 -0
- package/dist/services/opening-guides.d.ts +141 -0
- package/dist/services/opening-guides.d.ts.map +1 -0
- package/dist/services/opening-guides.js +267 -0
- package/dist/services/opening-guides.test.d.ts +2 -0
- package/dist/services/opening-guides.test.d.ts.map +1 -0
- package/dist/services/opening-guides.test.js +208 -0
- package/dist/services/port-connectivity.d.ts +115 -0
- package/dist/services/port-connectivity.d.ts.map +1 -0
- package/dist/services/port-connectivity.js +300 -0
- package/dist/services/port-connectivity.test.d.ts +2 -0
- package/dist/services/port-connectivity.test.d.ts.map +1 -0
- package/dist/services/port-connectivity.test.js +297 -0
- package/dist/services/riser-diagram.d.ts +40 -0
- package/dist/services/riser-diagram.d.ts.map +1 -0
- package/dist/services/riser-diagram.js +110 -0
- package/dist/services/riser-diagram.test.d.ts +2 -0
- package/dist/services/riser-diagram.test.d.ts.map +1 -0
- package/dist/services/riser-diagram.test.js +66 -0
- package/dist/services/single-undo-dance.test.d.ts +2 -0
- package/dist/services/single-undo-dance.test.d.ts.map +1 -0
- package/dist/services/single-undo-dance.test.js +210 -0
- package/dist/services/snap.d.ts +80 -0
- package/dist/services/snap.d.ts.map +1 -0
- package/dist/services/snap.js +140 -0
- package/dist/services/snap.test.d.ts +2 -0
- package/dist/services/snap.test.d.ts.map +1 -0
- package/dist/services/snap.test.js +137 -0
- package/dist/services/system-graph.d.ts +30 -0
- package/dist/services/system-graph.d.ts.map +1 -0
- package/dist/services/system-graph.js +164 -0
- package/dist/services/system-graph.test.d.ts +2 -0
- package/dist/services/system-graph.test.d.ts.map +1 -0
- package/dist/services/system-graph.test.js +138 -0
- package/dist/services/validate-dwv.d.ts +32 -0
- package/dist/services/validate-dwv.d.ts.map +1 -0
- package/dist/services/validate-dwv.js +118 -0
- package/dist/services/validate-dwv.test.d.ts +2 -0
- package/dist/services/validate-dwv.test.d.ts.map +1 -0
- package/dist/services/validate-dwv.test.js +109 -0
- package/dist/solar-panel-presets.d.ts +17 -0
- package/dist/solar-panel-presets.d.ts.map +1 -0
- package/dist/solar-panel-presets.js +43 -0
- package/dist/store/actions/node-actions.d.ts +14 -2
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +585 -15
- package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
- package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
- package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
- package/dist/store/actions/reparent.test.d.ts +2 -0
- package/dist/store/actions/reparent.test.d.ts.map +1 -0
- package/dist/store/actions/reparent.test.js +212 -0
- package/dist/store/actions/ridge-vent-update.test.d.ts +2 -0
- package/dist/store/actions/ridge-vent-update.test.d.ts.map +1 -0
- package/dist/store/actions/ridge-vent-update.test.js +264 -0
- package/dist/store/history-control.d.ts +11 -0
- package/dist/store/history-control.d.ts.map +1 -1
- package/dist/store/history-control.js +33 -0
- package/dist/store/use-interactive.d.ts +39 -0
- package/dist/store/use-interactive.d.ts.map +1 -1
- package/dist/store/use-interactive.js +75 -0
- package/dist/store/use-live-node-overrides.d.ts +22 -0
- package/dist/store/use-live-node-overrides.d.ts.map +1 -0
- package/dist/store/use-live-node-overrides.js +59 -0
- package/dist/store/use-scene-dirty-tracking.test.d.ts +2 -0
- package/dist/store/use-scene-dirty-tracking.test.d.ts.map +1 -0
- package/dist/store/use-scene-dirty-tracking.test.js +68 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-elevator-migration.test.js +109 -0
- package/dist/store/use-scene-measurement-roundtrip.test.d.ts +2 -0
- package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +1 -0
- package/dist/store/use-scene-measurement-roundtrip.test.js +97 -0
- package/dist/store/use-scene-plugins.test.d.ts +2 -0
- package/dist/store/use-scene-plugins.test.d.ts.map +1 -0
- package/dist/store/use-scene-plugins.test.js +51 -0
- package/dist/store/use-scene-wall-slot-migration.test.d.ts +2 -0
- package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-wall-slot-migration.test.js +210 -0
- package/dist/store/use-scene-window-migration.test.d.ts +2 -0
- package/dist/store/use-scene-window-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-window-migration.test.js +85 -0
- package/dist/store/use-scene.d.ts +28 -2
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +546 -49
- package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
- package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-dispatch.js +57 -0
- package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
- package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-geometry.js +50 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.js +218 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-system.js +48 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime-system.js +8 -0
- package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
- package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.js +225 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.test.js +64 -0
- package/dist/systems/elevator/elevator-service.d.ts +19 -0
- package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-service.js +111 -0
- package/dist/systems/fence/fence-centerline.d.ts +12 -0
- package/dist/systems/fence/fence-centerline.d.ts.map +1 -0
- package/dist/systems/fence/fence-centerline.js +37 -0
- package/dist/systems/fence/fence-spline.d.ts +39 -0
- package/dist/systems/fence/fence-spline.d.ts.map +1 -0
- package/dist/systems/fence/fence-spline.js +196 -0
- package/dist/systems/fence/fence-spline.test.d.ts +2 -0
- package/dist/systems/fence/fence-spline.test.d.ts.map +1 -0
- package/dist/systems/fence/fence-spline.test.js +136 -0
- package/dist/systems/stair/stair-footprint.d.ts +44 -0
- package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
- package/dist/systems/stair/stair-footprint.js +178 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +2447 -0
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.js +93 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.test.js +80 -0
- package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-sync.js +155 -147
- package/dist/systems/stair/stair-opening-sync.test.js +424 -2
- package/dist/systems/stair/stair-opening-system.d.ts +2 -0
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-system.js +104 -0
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +38 -2
- package/dist/systems/wall/wall-mitering.test.d.ts +2 -0
- package/dist/systems/wall/wall-mitering.test.d.ts.map +1 -0
- package/dist/systems/wall/wall-mitering.test.js +63 -0
- package/dist/systems/wall/wall-move.d.ts +48 -0
- package/dist/systems/wall/wall-move.d.ts.map +1 -0
- package/dist/systems/wall/wall-move.js +193 -0
- package/dist/utils/clone-scene-graph.d.ts +7 -4
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +29 -7
- package/dist/utils/clone-scene-graph.test.d.ts +2 -0
- package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/clone-scene-graph.test.js +62 -0
- package/dist/utils/heal-scene-graph.d.ts +18 -0
- package/dist/utils/heal-scene-graph.d.ts.map +1 -0
- package/dist/utils/heal-scene-graph.js +90 -0
- package/dist/utils/heal-scene-graph.test.d.ts +2 -0
- package/dist/utils/heal-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/heal-scene-graph.test.js +88 -0
- package/dist/validation/validate-build-json.d.ts +44 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -0
- package/dist/validation/validate-build-json.js +256 -0
- package/package.json +11 -4
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { SlabNode, WallNode } from '../schema';
|
|
3
|
+
import { pointInPolygon } from './polygon-relations';
|
|
4
|
+
import { getRenderableSlabPolygon, snapSlabEdgeToWallBand } from './slab-polygon';
|
|
5
|
+
function wallOf(start, end, thickness = 0.1) {
|
|
6
|
+
return WallNode.parse({ start, end, thickness });
|
|
7
|
+
}
|
|
8
|
+
function slabOf(polygon, autoFromWalls = true, elevation) {
|
|
9
|
+
return SlabNode.parse(elevation === undefined ? { polygon, autoFromWalls } : { polygon, autoFromWalls, elevation });
|
|
10
|
+
}
|
|
11
|
+
function xs(polygon) {
|
|
12
|
+
return polygon.map((point) => point[0]);
|
|
13
|
+
}
|
|
14
|
+
function zs(polygon) {
|
|
15
|
+
return polygon.map((point) => point[1]);
|
|
16
|
+
}
|
|
17
|
+
/** Assert the ring contains every expected vertex (order-independent). */
|
|
18
|
+
function expectRingToInclude(polygon, points) {
|
|
19
|
+
const missing = points.filter(([x, z]) => !polygon.some((p) => Math.abs(p[0] - x) < 1e-6 && Math.abs(p[1] - z) < 1e-6));
|
|
20
|
+
expect(missing).toEqual([]);
|
|
21
|
+
}
|
|
22
|
+
const roomA = [
|
|
23
|
+
[0, 0],
|
|
24
|
+
[4, 0],
|
|
25
|
+
[4, 3],
|
|
26
|
+
[0, 3],
|
|
27
|
+
];
|
|
28
|
+
const roomB = [
|
|
29
|
+
[4, 0],
|
|
30
|
+
[8, 0],
|
|
31
|
+
[8, 3],
|
|
32
|
+
[4, 3],
|
|
33
|
+
];
|
|
34
|
+
// Two rooms side by side sharing the centerline wall at x=4.
|
|
35
|
+
const twoRoomWalls = [
|
|
36
|
+
wallOf([0, 0], [4, 0]),
|
|
37
|
+
wallOf([4, 0], [8, 0]),
|
|
38
|
+
wallOf([8, 0], [8, 3]),
|
|
39
|
+
wallOf([8, 3], [4, 3]),
|
|
40
|
+
wallOf([4, 3], [0, 3]),
|
|
41
|
+
wallOf([0, 3], [0, 0]),
|
|
42
|
+
wallOf([4, 0], [4, 3]),
|
|
43
|
+
];
|
|
44
|
+
describe('getRenderableSlabPolygon', () => {
|
|
45
|
+
test('adjacent room slabs share the exact centerline seam without gap or overlap', () => {
|
|
46
|
+
const slabA = slabOf(roomA);
|
|
47
|
+
const slabB = slabOf(roomB);
|
|
48
|
+
const polyA = getRenderableSlabPolygon(slabA, {
|
|
49
|
+
walls: twoRoomWalls,
|
|
50
|
+
siblingSlabs: [slabB],
|
|
51
|
+
});
|
|
52
|
+
const polyB = getRenderableSlabPolygon(slabB, {
|
|
53
|
+
walls: twoRoomWalls,
|
|
54
|
+
siblingSlabs: [slabA],
|
|
55
|
+
});
|
|
56
|
+
// A: exterior edges flush with the 0.1-thick facade (+0.05), shared
|
|
57
|
+
// edge exactly on the wall centerline x=4.
|
|
58
|
+
expect(Math.min(...xs(polyA))).toBeCloseTo(-0.05);
|
|
59
|
+
expect(Math.max(...xs(polyA))).toBeCloseTo(4);
|
|
60
|
+
expect(Math.min(...zs(polyA))).toBeCloseTo(-0.05);
|
|
61
|
+
expect(Math.max(...zs(polyA))).toBeCloseTo(3.05);
|
|
62
|
+
expect(Math.min(...xs(polyB))).toBeCloseTo(4);
|
|
63
|
+
expect(Math.max(...xs(polyB))).toBeCloseTo(8.05);
|
|
64
|
+
// No overlap across the shared wall (FP noise only)...
|
|
65
|
+
expect(Math.max(...xs(polyA))).toBeLessThanOrEqual(Math.min(...xs(polyB)) + 1e-9);
|
|
66
|
+
// ...and the seam is EXACTLY shared: both rings project the shared
|
|
67
|
+
// edge onto the same centerline x=4 with matching endpoints.
|
|
68
|
+
const seamZs = (poly) => poly
|
|
69
|
+
.filter((point) => point[0] === 4)
|
|
70
|
+
.map((point) => point[1])
|
|
71
|
+
.sort((left, right) => left - right);
|
|
72
|
+
const seamA = seamZs(polyA);
|
|
73
|
+
const seamB = seamZs(polyB);
|
|
74
|
+
expect(seamA).toHaveLength(2);
|
|
75
|
+
expect(seamB).toHaveLength(2);
|
|
76
|
+
expect(seamA[0]).toBeCloseTo(seamB[0], 12);
|
|
77
|
+
expect(seamA[1]).toBeCloseTo(seamB[1], 12);
|
|
78
|
+
// Grid-sample the strip under the shared wall band: every point is
|
|
79
|
+
// inside at least one slab — the old relief slit is gone, so deleting
|
|
80
|
+
// the wall would expose a continuous floor.
|
|
81
|
+
for (let x = 3.95; x <= 4.0501; x += 0.01) {
|
|
82
|
+
for (let z = 0; z <= 3.001; z += 0.15) {
|
|
83
|
+
expect(pointInPolygon([x, z], polyA) || pointInPolygon([x, z], polyB)).toBe(true);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
test('exterior edge expands by half of THAT wall thickness', () => {
|
|
88
|
+
const walls = [
|
|
89
|
+
wallOf([0, 0], [4, 0]),
|
|
90
|
+
wallOf([4, 0], [4, 3]),
|
|
91
|
+
wallOf([4, 3], [0, 3]),
|
|
92
|
+
// Non-default thickness on the left facade only.
|
|
93
|
+
wallOf([0, 3], [0, 0], 0.3),
|
|
94
|
+
];
|
|
95
|
+
const poly = getRenderableSlabPolygon(slabOf(roomA), { walls, siblingSlabs: [] });
|
|
96
|
+
expect(Math.min(...xs(poly))).toBeCloseTo(-0.15);
|
|
97
|
+
expect(Math.max(...xs(poly))).toBeCloseTo(4.05);
|
|
98
|
+
expect(Math.min(...zs(poly))).toBeCloseTo(-0.05);
|
|
99
|
+
expect(Math.max(...zs(poly))).toBeCloseTo(3.05);
|
|
100
|
+
});
|
|
101
|
+
test('freehand edges away from any wall render exactly as drawn', () => {
|
|
102
|
+
const drawn = [
|
|
103
|
+
[10, 10],
|
|
104
|
+
[12, 10],
|
|
105
|
+
[12, 12],
|
|
106
|
+
[10, 12],
|
|
107
|
+
];
|
|
108
|
+
const poly = getRenderableSlabPolygon(slabOf(drawn, false), {
|
|
109
|
+
walls: twoRoomWalls,
|
|
110
|
+
siblingSlabs: [],
|
|
111
|
+
});
|
|
112
|
+
expect(poly).toEqual(drawn);
|
|
113
|
+
});
|
|
114
|
+
test('manual slabs follow the same per-edge rule as auto slabs', () => {
|
|
115
|
+
const poly = getRenderableSlabPolygon(slabOf(roomA, false), {
|
|
116
|
+
walls: twoRoomWalls,
|
|
117
|
+
siblingSlabs: [slabOf(roomB)],
|
|
118
|
+
});
|
|
119
|
+
expect(Math.max(...xs(poly))).toBeCloseTo(4);
|
|
120
|
+
expect(Math.min(...xs(poly))).toBeCloseTo(-0.05);
|
|
121
|
+
});
|
|
122
|
+
test('T-junction: a neighbour edge that is a sub-segment still reads as interior', () => {
|
|
123
|
+
// Big 6×5 room; a 2×2 bay hangs below, sealed against the interior of
|
|
124
|
+
// the big room's bottom wall between x=1 and x=3.
|
|
125
|
+
const big = slabOf([
|
|
126
|
+
[0, 0],
|
|
127
|
+
[6, 0],
|
|
128
|
+
[6, 5],
|
|
129
|
+
[0, 5],
|
|
130
|
+
]);
|
|
131
|
+
const bay = slabOf([
|
|
132
|
+
[1, -2],
|
|
133
|
+
[3, -2],
|
|
134
|
+
[3, 0],
|
|
135
|
+
[1, 0],
|
|
136
|
+
]);
|
|
137
|
+
const walls = [
|
|
138
|
+
wallOf([0, 0], [6, 0]),
|
|
139
|
+
wallOf([6, 0], [6, 5]),
|
|
140
|
+
wallOf([6, 5], [0, 5]),
|
|
141
|
+
wallOf([0, 5], [0, 0]),
|
|
142
|
+
wallOf([1, 0], [1, -2]),
|
|
143
|
+
wallOf([1, -2], [3, -2]),
|
|
144
|
+
wallOf([3, -2], [3, 0]),
|
|
145
|
+
];
|
|
146
|
+
// The bay's top edge lies on a sub-segment of the big slab's bottom
|
|
147
|
+
// edge — interior, seamed on the shared wall centerline z=0, while
|
|
148
|
+
// its free-standing sides stay on-wall.
|
|
149
|
+
const bayPoly = getRenderableSlabPolygon(bay, { walls, siblingSlabs: [big] });
|
|
150
|
+
expect(Math.max(...zs(bayPoly))).toBeCloseTo(0);
|
|
151
|
+
expect(Math.min(...zs(bayPoly))).toBeCloseTo(-2.05);
|
|
152
|
+
expect(Math.min(...xs(bayPoly))).toBeCloseTo(0.95);
|
|
153
|
+
expect(Math.max(...xs(bayPoly))).toBeCloseTo(3.05);
|
|
154
|
+
// The big slab's bottom edge is backed differently along its span:
|
|
155
|
+
// centerline seam across the bay (z=0), facade-flush elsewhere
|
|
156
|
+
// (z=-0.05), joined by step connectors at the bay junction walls
|
|
157
|
+
// x=1 and x=3 (the old whole-edge rule pulled the entire edge back).
|
|
158
|
+
const bigPoly = getRenderableSlabPolygon(big, { walls, siblingSlabs: [bay] });
|
|
159
|
+
expect(Math.min(...zs(bigPoly))).toBeCloseTo(-0.05);
|
|
160
|
+
expect(Math.max(...zs(bigPoly))).toBeCloseTo(5.05);
|
|
161
|
+
expectRingToInclude(bigPoly, [
|
|
162
|
+
[1, -0.05],
|
|
163
|
+
[1, 0],
|
|
164
|
+
[3, 0],
|
|
165
|
+
[3, -0.05],
|
|
166
|
+
]);
|
|
167
|
+
});
|
|
168
|
+
test('an edge on a wall longer than itself still reaches the facade', () => {
|
|
169
|
+
// Slab edge [1,0]→[3,0] sits mid-span on a 6m wall.
|
|
170
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
171
|
+
[1, 0],
|
|
172
|
+
[3, 0],
|
|
173
|
+
[3, 2],
|
|
174
|
+
[1, 2],
|
|
175
|
+
]), { walls: [wallOf([0, 0], [6, 0])], siblingSlabs: [] });
|
|
176
|
+
expect(Math.min(...zs(poly))).toBeCloseTo(-0.05);
|
|
177
|
+
// The other three edges are free — rendered as drawn.
|
|
178
|
+
expect(Math.max(...zs(poly))).toBeCloseTo(2);
|
|
179
|
+
expect(Math.min(...xs(poly))).toBeCloseTo(1);
|
|
180
|
+
expect(Math.max(...xs(poly))).toBeCloseTo(3);
|
|
181
|
+
});
|
|
182
|
+
test('legacy edge stored at the inner wall face projects to the outer face', () => {
|
|
183
|
+
// Wall centerline z=0, thickness 0.1 — the legacy slab edge sits at the
|
|
184
|
+
// inner face z=0.05. Absolute projection must land the rendered edge on
|
|
185
|
+
// the OUTER face (-0.05), not at inner face + t/2 (= centerline).
|
|
186
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
187
|
+
[0, 0.05],
|
|
188
|
+
[4, 0.05],
|
|
189
|
+
[4, 3],
|
|
190
|
+
[0, 3],
|
|
191
|
+
], false), { walls: [wallOf([0, 0], [4, 0])], siblingSlabs: [] });
|
|
192
|
+
expect(Math.min(...zs(poly))).toBeCloseTo(-0.05);
|
|
193
|
+
});
|
|
194
|
+
test('legacy edge stored at the outer wall face stays at the face (no overshoot)', () => {
|
|
195
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
196
|
+
[0, -0.05],
|
|
197
|
+
[4, -0.05],
|
|
198
|
+
[4, 3],
|
|
199
|
+
[0, 3],
|
|
200
|
+
], false), { walls: [wallOf([0, 0], [4, 0])], siblingSlabs: [] });
|
|
201
|
+
expect(Math.min(...zs(poly))).toBeCloseTo(-0.05);
|
|
202
|
+
});
|
|
203
|
+
test('a thick wall adopts a face-aligned edge beyond the old fixed tolerance', () => {
|
|
204
|
+
// t=0.3: inner face is 0.15 off the centerline — past the old fixed 0.1
|
|
205
|
+
// tolerance, so this edge used to classify FREE and never expand.
|
|
206
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
207
|
+
[0, 0.15],
|
|
208
|
+
[4, 0.15],
|
|
209
|
+
[4, 3],
|
|
210
|
+
[0, 3],
|
|
211
|
+
], false), { walls: [wallOf([0, 0], [4, 0], 0.3)], siblingSlabs: [] });
|
|
212
|
+
expect(Math.min(...zs(poly))).toBeCloseTo(-0.15);
|
|
213
|
+
});
|
|
214
|
+
test('edges outside the adoption band stay free', () => {
|
|
215
|
+
// Band for t=0.1 is half + 0.06 = 0.11 — an edge 0.12 away is kept as drawn.
|
|
216
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
217
|
+
[0, 0.12],
|
|
218
|
+
[4, 0.12],
|
|
219
|
+
[4, 3],
|
|
220
|
+
[0, 3],
|
|
221
|
+
], false), { walls: [wallOf([0, 0], [4, 0])], siblingSlabs: [] });
|
|
222
|
+
expect(Math.min(...zs(poly))).toBeCloseTo(0.12);
|
|
223
|
+
});
|
|
224
|
+
test('two parallel close walls: the nearest centerline wins', () => {
|
|
225
|
+
// Thin wall at z=0 (band 0.11) and thick wall at z=0.3 (t=0.3, band
|
|
226
|
+
// 0.21). An edge at z=0.1 is inside BOTH bands (laterals 0.1 and 0.2);
|
|
227
|
+
// it must adopt the nearer thin wall and land on ITS outer face.
|
|
228
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
229
|
+
[0, 0.1],
|
|
230
|
+
[4, 0.1],
|
|
231
|
+
[4, 3],
|
|
232
|
+
[0, 3],
|
|
233
|
+
], false), {
|
|
234
|
+
walls: [wallOf([0, 0], [4, 0]), wallOf([0, 0.3], [4, 0.3], 0.3)],
|
|
235
|
+
siblingSlabs: [],
|
|
236
|
+
});
|
|
237
|
+
expect(Math.min(...zs(poly))).toBeCloseTo(-0.05);
|
|
238
|
+
});
|
|
239
|
+
test('span overlap below the minimum leaves the edge free', () => {
|
|
240
|
+
// The wall only overlaps the last 2cm of the edge span — under the 5cm
|
|
241
|
+
// classification minimum.
|
|
242
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
243
|
+
[0, 0],
|
|
244
|
+
[4, 0],
|
|
245
|
+
[4, 3],
|
|
246
|
+
[0, 3],
|
|
247
|
+
], false), { walls: [wallOf([3.98, 0], [4.5, 0])], siblingSlabs: [] });
|
|
248
|
+
expect(Math.min(...zs(poly))).toBeCloseTo(0);
|
|
249
|
+
});
|
|
250
|
+
test('legacy face-aligned rooms across a thick wall still tile without overlap', () => {
|
|
251
|
+
// Both rooms stored at the INNER faces of the shared t=0.3 wall at x=4
|
|
252
|
+
// (edges 0.3 apart — far beyond the direct sibling tolerance). Each edge
|
|
253
|
+
// is inside the wall band with the sibling across the same band, so both
|
|
254
|
+
// classify interior and land exactly on the CENTERLINE instead of
|
|
255
|
+
// projecting to opposite outer faces (which would overlap by a full
|
|
256
|
+
// thickness).
|
|
257
|
+
const walls = [
|
|
258
|
+
wallOf([0, 0], [8, 0]),
|
|
259
|
+
wallOf([8, 0], [8, 3]),
|
|
260
|
+
wallOf([8, 3], [0, 3]),
|
|
261
|
+
wallOf([0, 3], [0, 0]),
|
|
262
|
+
wallOf([4, 0], [4, 3], 0.3),
|
|
263
|
+
];
|
|
264
|
+
const legacyA = slabOf([
|
|
265
|
+
[0, 0],
|
|
266
|
+
[3.85, 0],
|
|
267
|
+
[3.85, 3],
|
|
268
|
+
[0, 3],
|
|
269
|
+
], false);
|
|
270
|
+
const legacyB = slabOf([
|
|
271
|
+
[4.15, 0],
|
|
272
|
+
[8, 0],
|
|
273
|
+
[8, 3],
|
|
274
|
+
[4.15, 3],
|
|
275
|
+
], false);
|
|
276
|
+
const polyA = getRenderableSlabPolygon(legacyA, { walls, siblingSlabs: [legacyB] });
|
|
277
|
+
const polyB = getRenderableSlabPolygon(legacyB, { walls, siblingSlabs: [legacyA] });
|
|
278
|
+
expect(Math.max(...xs(polyA))).toBeCloseTo(4);
|
|
279
|
+
expect(Math.min(...xs(polyB))).toBeCloseTo(4);
|
|
280
|
+
expect(Math.max(...xs(polyA))).toBeLessThanOrEqual(Math.min(...xs(polyB)) + 1e-9);
|
|
281
|
+
});
|
|
282
|
+
test('the higher room carries the wall band to the lower room face', () => {
|
|
283
|
+
// Real user repro shape: two rooms in an L/offset arrangement share the
|
|
284
|
+
// z=0 wall over x ∈ [-1, 0.5] only; the north room's floor is raised
|
|
285
|
+
// (0.34) above the south room's (0.05). Slabs extrude 0 → elevation and
|
|
286
|
+
// the higher slab closes the full wall band while the lower slab meets
|
|
287
|
+
// it at its own wall face. The reach comes from the matched wall's real
|
|
288
|
+
// thickness, not a default-width expansion.
|
|
289
|
+
const walls = [
|
|
290
|
+
wallOf([-1, 3], [-1, 0]),
|
|
291
|
+
wallOf([-1, 0], [0.5, 0]),
|
|
292
|
+
wallOf([0.5, 0], [2, 0]),
|
|
293
|
+
wallOf([2, 0], [2, 3]),
|
|
294
|
+
wallOf([2, 3], [-1, 3]),
|
|
295
|
+
wallOf([0.5, 0], [0.5, -4]),
|
|
296
|
+
wallOf([0.5, -4], [-1, -4]),
|
|
297
|
+
wallOf([-1, -4], [-1, 0]),
|
|
298
|
+
];
|
|
299
|
+
const high = slabOf([
|
|
300
|
+
[-1, 3],
|
|
301
|
+
[-1, 0],
|
|
302
|
+
[2, 0],
|
|
303
|
+
[2, 3],
|
|
304
|
+
], false, 0.34);
|
|
305
|
+
const low = slabOf([
|
|
306
|
+
[0.5, 0],
|
|
307
|
+
[-1, 0],
|
|
308
|
+
[-1, -4],
|
|
309
|
+
[0.5, -4],
|
|
310
|
+
], true, 0.05);
|
|
311
|
+
const polyHigh = getRenderableSlabPolygon(high, { walls, siblingSlabs: [low] });
|
|
312
|
+
const polyLow = getRenderableSlabPolygon(low, { walls, siblingSlabs: [high] });
|
|
313
|
+
// The shared and facade spans fuse because both land on z=-0.05.
|
|
314
|
+
expectRingToInclude(polyHigh, [
|
|
315
|
+
[0.5, -0.05],
|
|
316
|
+
[2.05, -0.05],
|
|
317
|
+
]);
|
|
318
|
+
expect(Math.min(...zs(polyHigh))).toBeCloseTo(-0.05);
|
|
319
|
+
expect(Math.max(...zs(polyLow))).toBeCloseTo(-0.05);
|
|
320
|
+
// The high slab owns the entire band; the lower room begins at its face.
|
|
321
|
+
for (let x = -0.95; x <= 0.4501; x += 0.05) {
|
|
322
|
+
for (let z = -0.045; z <= 0.0451; z += 0.015) {
|
|
323
|
+
expect(pointInPolygon([x, z], polyHigh, { includeBoundary: false })).toBe(true);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
test('legacy face-aligned unequal rooms self-heal to the lower room face', () => {
|
|
328
|
+
// Same stored-at-inner-faces legacy data as above (edges a full 0.3
|
|
329
|
+
// apart across the t=0.3 wall at x=4), but with the west room raised.
|
|
330
|
+
// Both edges classify interior through the band-sibling rule and adopt
|
|
331
|
+
// the east/lower room face despite being stored at opposite faces.
|
|
332
|
+
const walls = [
|
|
333
|
+
wallOf([0, 0], [8, 0]),
|
|
334
|
+
wallOf([8, 0], [8, 3]),
|
|
335
|
+
wallOf([8, 3], [0, 3]),
|
|
336
|
+
wallOf([0, 3], [0, 0]),
|
|
337
|
+
wallOf([4, 0], [4, 3], 0.3),
|
|
338
|
+
];
|
|
339
|
+
const legacyHigh = slabOf([
|
|
340
|
+
[0, 0],
|
|
341
|
+
[3.85, 0],
|
|
342
|
+
[3.85, 3],
|
|
343
|
+
[0, 3],
|
|
344
|
+
], false, 0.4);
|
|
345
|
+
const legacyLow = slabOf([
|
|
346
|
+
[4.15, 0],
|
|
347
|
+
[8, 0],
|
|
348
|
+
[8, 3],
|
|
349
|
+
[4.15, 3],
|
|
350
|
+
], false, 0.05);
|
|
351
|
+
const polyHigh = getRenderableSlabPolygon(legacyHigh, { walls, siblingSlabs: [legacyLow] });
|
|
352
|
+
const polyLow = getRenderableSlabPolygon(legacyLow, { walls, siblingSlabs: [legacyHigh] });
|
|
353
|
+
expect(Math.max(...xs(polyHigh))).toBeCloseTo(4.15);
|
|
354
|
+
expect(Math.min(...xs(polyLow))).toBeCloseTo(4.15);
|
|
355
|
+
expect(Math.max(...xs(polyHigh))).toBeLessThanOrEqual(Math.min(...xs(polyLow)) + 1e-9);
|
|
356
|
+
});
|
|
357
|
+
test('stacked slabs are not mistaken for rooms across a wall', () => {
|
|
358
|
+
const floor = slabOf(roomA, false, 0.05);
|
|
359
|
+
const platform = slabOf(roomA, false, 0.4);
|
|
360
|
+
const walls = [
|
|
361
|
+
wallOf([0, 0], [4, 0]),
|
|
362
|
+
wallOf([4, 0], [4, 3]),
|
|
363
|
+
wallOf([4, 3], [0, 3]),
|
|
364
|
+
wallOf([0, 3], [0, 0]),
|
|
365
|
+
];
|
|
366
|
+
const floorPolygon = getRenderableSlabPolygon(floor, {
|
|
367
|
+
walls,
|
|
368
|
+
siblingSlabs: [platform],
|
|
369
|
+
});
|
|
370
|
+
const platformPolygon = getRenderableSlabPolygon(platform, {
|
|
371
|
+
walls,
|
|
372
|
+
siblingSlabs: [floor],
|
|
373
|
+
});
|
|
374
|
+
for (const polygon of [floorPolygon, platformPolygon]) {
|
|
375
|
+
expect(Math.min(...xs(polygon))).toBeCloseTo(-0.05);
|
|
376
|
+
expect(Math.max(...xs(polygon))).toBeCloseTo(4.05);
|
|
377
|
+
expect(Math.min(...zs(polygon))).toBeCloseTo(-0.05);
|
|
378
|
+
expect(Math.max(...zs(polygon))).toBeCloseTo(3.05);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
test('sibling winding does not change a shared seam decision', () => {
|
|
382
|
+
const slabA = slabOf(roomA);
|
|
383
|
+
const slabB = slabOf([...roomB].reverse());
|
|
384
|
+
const polyA = getRenderableSlabPolygon(slabA, {
|
|
385
|
+
walls: twoRoomWalls,
|
|
386
|
+
siblingSlabs: [slabB],
|
|
387
|
+
});
|
|
388
|
+
const polyB = getRenderableSlabPolygon(slabB, {
|
|
389
|
+
walls: twoRoomWalls,
|
|
390
|
+
siblingSlabs: [slabA],
|
|
391
|
+
});
|
|
392
|
+
expect(Math.max(...xs(polyA))).toBeCloseTo(4);
|
|
393
|
+
expect(Math.min(...xs(polyB))).toBeCloseTo(4);
|
|
394
|
+
});
|
|
395
|
+
test('wall-less butted slabs at different elevations keep the midline seam', () => {
|
|
396
|
+
// No wall backs the seam, so there is no band to hide a pocket under —
|
|
397
|
+
// the exposed step face at the joint is correct. Elevation must not
|
|
398
|
+
// move a wall-less seam off the midline.
|
|
399
|
+
const stepHigh = slabOf([
|
|
400
|
+
[0, 0],
|
|
401
|
+
[4, 0],
|
|
402
|
+
[4, 3],
|
|
403
|
+
[0, 3],
|
|
404
|
+
], false, 0.3);
|
|
405
|
+
const stepLow = slabOf([
|
|
406
|
+
[4, 0],
|
|
407
|
+
[8, 0],
|
|
408
|
+
[8, 3],
|
|
409
|
+
[4, 3],
|
|
410
|
+
], false, 0.05);
|
|
411
|
+
const polyHigh = getRenderableSlabPolygon(stepHigh, { walls: [], siblingSlabs: [stepLow] });
|
|
412
|
+
const polyLow = getRenderableSlabPolygon(stepLow, { walls: [], siblingSlabs: [stepHigh] });
|
|
413
|
+
expect(Math.max(...xs(polyHigh))).toBeCloseTo(4);
|
|
414
|
+
expect(Math.min(...xs(polyLow))).toBeCloseTo(4);
|
|
415
|
+
});
|
|
416
|
+
test('offset rooms sharing a partial wall span: interior beside the sibling, facade elsewhere', () => {
|
|
417
|
+
// Rooms offset diagonally share the x=4 wall only for z ∈ [1.5, 3].
|
|
418
|
+
// Each room's long edge is interior for the shared span and exterior
|
|
419
|
+
// (its own facade) for the rest — the case sub-edge classification
|
|
420
|
+
// exists for.
|
|
421
|
+
const offsetA = slabOf([
|
|
422
|
+
[0, 0],
|
|
423
|
+
[4, 0],
|
|
424
|
+
[4, 3],
|
|
425
|
+
[0, 3],
|
|
426
|
+
]);
|
|
427
|
+
const offsetB = slabOf([
|
|
428
|
+
[4, 1.5],
|
|
429
|
+
[8, 1.5],
|
|
430
|
+
[8, 4.5],
|
|
431
|
+
[4, 4.5],
|
|
432
|
+
]);
|
|
433
|
+
const walls = [
|
|
434
|
+
wallOf([0, 0], [4, 0]),
|
|
435
|
+
wallOf([0, 3], [0, 0]),
|
|
436
|
+
wallOf([0, 3], [4, 3]),
|
|
437
|
+
wallOf([4, 0], [4, 4.5]),
|
|
438
|
+
wallOf([4, 1.5], [8, 1.5]),
|
|
439
|
+
wallOf([8, 1.5], [8, 4.5]),
|
|
440
|
+
wallOf([8, 4.5], [4, 4.5]),
|
|
441
|
+
];
|
|
442
|
+
const polyA = getRenderableSlabPolygon(offsetA, { walls, siblingSlabs: [offsetB] });
|
|
443
|
+
const polyB = getRenderableSlabPolygon(offsetB, { walls, siblingSlabs: [offsetA] });
|
|
444
|
+
// A's right edge: facade-flush below the junction, exactly on the
|
|
445
|
+
// centerline beside B, joined by the step connector at the junction z=1.5.
|
|
446
|
+
expectRingToInclude(polyA, [
|
|
447
|
+
[4.05, -0.05],
|
|
448
|
+
[4.05, 1.5],
|
|
449
|
+
[4, 1.5],
|
|
450
|
+
[4, 3.05],
|
|
451
|
+
]);
|
|
452
|
+
// B's left edge mirrors it: centerline seam beside A, facade-flush
|
|
453
|
+
// above, step at the junction z=3.
|
|
454
|
+
expectRingToInclude(polyB, [
|
|
455
|
+
[4, 1.45],
|
|
456
|
+
[4, 3],
|
|
457
|
+
[3.95, 3],
|
|
458
|
+
[3.95, 4.55],
|
|
459
|
+
]);
|
|
460
|
+
// Along the shared span both slabs reach exactly the wall centerline:
|
|
461
|
+
// the strip under the shared wall is fully covered with no interior
|
|
462
|
+
// overlap — deleting the wall would expose a continuous floor...
|
|
463
|
+
for (let z = 1.6; z <= 2.95; z += 0.1) {
|
|
464
|
+
expect(pointInPolygon([3.99, z], polyA, { includeBoundary: false })).toBe(true);
|
|
465
|
+
expect(pointInPolygon([4.01, z], polyB, { includeBoundary: false })).toBe(true);
|
|
466
|
+
expect(pointInPolygon([4.01, z], polyA, { includeBoundary: false })).toBe(false);
|
|
467
|
+
expect(pointInPolygon([3.99, z], polyB, { includeBoundary: false })).toBe(false);
|
|
468
|
+
for (let x = 3.96; x <= 4.0401; x += 0.01) {
|
|
469
|
+
expect(pointInPolygon([x, z], polyA) || pointInPolygon([x, z], polyB)).toBe(true);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// ...while each unshared portion reaches its own facade face.
|
|
473
|
+
expect(pointInPolygon([4.04, 0.75], polyA, { includeBoundary: false })).toBe(true);
|
|
474
|
+
expect(pointInPolygon([3.96, 3.75], polyB, { includeBoundary: false })).toBe(true);
|
|
475
|
+
// Any residual overlap is confined to the shared wall's footprint
|
|
476
|
+
// right at the junction corners — hidden under the wall bodies, the
|
|
477
|
+
// same corner pockets the outer-face projection has always produced
|
|
478
|
+
// where two rooms' facades meet a wall junction.
|
|
479
|
+
for (let x = 3.5; x <= 4.5; x += 0.02) {
|
|
480
|
+
for (let z = -0.2; z <= 4.7; z += 0.02) {
|
|
481
|
+
const overlapping = pointInPolygon([x, z], polyA, { includeBoundary: false }) &&
|
|
482
|
+
pointInPolygon([x, z], polyB, { includeBoundary: false });
|
|
483
|
+
if (!overlapping)
|
|
484
|
+
continue;
|
|
485
|
+
expect(Math.abs(x - 4)).toBeLessThanOrEqual(0.05 + 1e-9);
|
|
486
|
+
expect(Math.min(Math.abs(z - 1.5), Math.abs(z - 3))).toBeLessThanOrEqual(0.05 + 1e-9);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
test('offset unequal rooms give the higher slab the full shared wall band', () => {
|
|
491
|
+
const high = slabOf([
|
|
492
|
+
[0, 0],
|
|
493
|
+
[4, 0],
|
|
494
|
+
[4, 3],
|
|
495
|
+
[0, 3],
|
|
496
|
+
], true, 0.4);
|
|
497
|
+
const low = slabOf([
|
|
498
|
+
[4, 1.5],
|
|
499
|
+
[8, 1.5],
|
|
500
|
+
[8, 4.5],
|
|
501
|
+
[4, 4.5],
|
|
502
|
+
], true, 0.05);
|
|
503
|
+
const walls = [
|
|
504
|
+
wallOf([0, 0], [4, 0]),
|
|
505
|
+
wallOf([0, 3], [0, 0]),
|
|
506
|
+
wallOf([0, 3], [4, 3]),
|
|
507
|
+
wallOf([4, 0], [4, 4.5]),
|
|
508
|
+
wallOf([4, 1.5], [8, 1.5]),
|
|
509
|
+
wallOf([8, 1.5], [8, 4.5]),
|
|
510
|
+
wallOf([8, 4.5], [4, 4.5]),
|
|
511
|
+
];
|
|
512
|
+
const highPolygon = getRenderableSlabPolygon(high, { walls, siblingSlabs: [low] });
|
|
513
|
+
const lowPolygon = getRenderableSlabPolygon(low, { walls, siblingSlabs: [high] });
|
|
514
|
+
expect(Math.max(...xs(highPolygon))).toBeCloseTo(4.05);
|
|
515
|
+
expectRingToInclude(lowPolygon, [
|
|
516
|
+
[4.05, 1.45],
|
|
517
|
+
[4.05, 3],
|
|
518
|
+
[3.95, 3],
|
|
519
|
+
]);
|
|
520
|
+
for (let z = 1.6; z <= 2.95; z += 0.1) {
|
|
521
|
+
expect(pointInPolygon([3.975, z], highPolygon, { includeBoundary: false })).toBe(true);
|
|
522
|
+
expect(pointInPolygon([3.975, z], lowPolygon, { includeBoundary: false })).toBe(false);
|
|
523
|
+
expect(pointInPolygon([4.025, z], highPolygon, { includeBoundary: false })).toBe(true);
|
|
524
|
+
expect(pointInPolygon([4.025, z], lowPolygon, { includeBoundary: false })).toBe(false);
|
|
525
|
+
expect(pointInPolygon([4.075, z], lowPolygon, { includeBoundary: false })).toBe(true);
|
|
526
|
+
expect(pointInPolygon([4.075, z], highPolygon, { includeBoundary: false })).toBe(false);
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
test('a wall backing only part of an edge: flush over the wall, as drawn beyond it', () => {
|
|
530
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
531
|
+
[0, 0],
|
|
532
|
+
[4, 0],
|
|
533
|
+
[4, 3],
|
|
534
|
+
[0, 3],
|
|
535
|
+
], false), { walls: [wallOf([0, 0], [2, 0])], siblingSlabs: [] });
|
|
536
|
+
expectRingToInclude(poly, [
|
|
537
|
+
[0, -0.05],
|
|
538
|
+
[2, -0.05],
|
|
539
|
+
[2, 0],
|
|
540
|
+
[4, 0],
|
|
541
|
+
]);
|
|
542
|
+
expect(Math.min(...zs(poly))).toBeCloseTo(-0.05);
|
|
543
|
+
expect(Math.max(...xs(poly))).toBeCloseTo(4);
|
|
544
|
+
});
|
|
545
|
+
test('two collinear walls of different thickness along one edge: each face wins its own span', () => {
|
|
546
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
547
|
+
[0, 0],
|
|
548
|
+
[4, 0],
|
|
549
|
+
[4, 3],
|
|
550
|
+
[0, 3],
|
|
551
|
+
], false), { walls: [wallOf([0, 0], [2, 0]), wallOf([2, 0], [4, 0], 0.3)], siblingSlabs: [] });
|
|
552
|
+
// Thin-wall face for x < 2, thick-wall face beyond, stepped at x=2
|
|
553
|
+
// (the old whole-edge rule let one wall win the entire edge).
|
|
554
|
+
expectRingToInclude(poly, [
|
|
555
|
+
[0, -0.05],
|
|
556
|
+
[2, -0.05],
|
|
557
|
+
[2, -0.15],
|
|
558
|
+
[4, -0.15],
|
|
559
|
+
]);
|
|
560
|
+
});
|
|
561
|
+
test('breakpoints within the minimum sub-edge length merge into one step', () => {
|
|
562
|
+
// The wall ends at x=2; the sibling starts at x=2.02 — the two
|
|
563
|
+
// breakpoints are 2cm apart, under the 5cm minimum, so they collapse
|
|
564
|
+
// into a single step at x=2 instead of leaving a sliver sub-edge.
|
|
565
|
+
const sibling = slabOf([
|
|
566
|
+
[2.02, -2],
|
|
567
|
+
[4, -2],
|
|
568
|
+
[4, 0],
|
|
569
|
+
[2.02, 0],
|
|
570
|
+
]);
|
|
571
|
+
const poly = getRenderableSlabPolygon(slabOf([
|
|
572
|
+
[0, 0],
|
|
573
|
+
[4, 0],
|
|
574
|
+
[4, 3],
|
|
575
|
+
[0, 3],
|
|
576
|
+
], false), { walls: [wallOf([0, 0], [2, 0])], siblingSlabs: [sibling] });
|
|
577
|
+
expectRingToInclude(poly, [
|
|
578
|
+
[2, -0.05],
|
|
579
|
+
[2, 0],
|
|
580
|
+
]);
|
|
581
|
+
expect(poly).toHaveLength(6);
|
|
582
|
+
});
|
|
583
|
+
test('nearly collinear corners do not create unbounded wall-offset miters', () => {
|
|
584
|
+
const stored = [
|
|
585
|
+
[3.1318685080276216, -4.3412497001265],
|
|
586
|
+
[11.261660174864861, -4.341249992437693],
|
|
587
|
+
[11.26165987489183, -1.3166851233565569],
|
|
588
|
+
[3.131868208054532, -1.3166857310453555],
|
|
589
|
+
[1.7318390128066048, -1.3166857522417288],
|
|
590
|
+
[1.7318388127796835, -4.341249921322843],
|
|
591
|
+
];
|
|
592
|
+
const poly = getRenderableSlabPolygon(slabOf(stored, false), {
|
|
593
|
+
walls: [
|
|
594
|
+
wallOf([1.7318387751077626, -4.323741654294752], [11.300000049514956, -4.341249931281993]),
|
|
595
|
+
],
|
|
596
|
+
siblingSlabs: [],
|
|
597
|
+
});
|
|
598
|
+
const storedExtent = Math.max(...stored.flat().map(Math.abs));
|
|
599
|
+
const renderedExtent = Math.max(...poly.flat().map(Math.abs));
|
|
600
|
+
const longestEdge = Math.max(...poly.map((point, index) => {
|
|
601
|
+
const next = poly[(index + 1) % poly.length];
|
|
602
|
+
return Math.hypot(point[0] - next[0], point[1] - next[1]);
|
|
603
|
+
}));
|
|
604
|
+
expect(renderedExtent - storedExtent).toBeLessThan(0.2);
|
|
605
|
+
expect(longestEdge).toBeLessThan(8.4);
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
describe('snapSlabEdgeToWallBand', () => {
|
|
609
|
+
test('an edge inside the band snaps onto the wall centerline', () => {
|
|
610
|
+
const snap = snapSlabEdgeToWallBand([0.5, 0.08], [3.5, 0.08], [wallOf([0, 0], [4, 0])]);
|
|
611
|
+
expect(snap).not.toBeNull();
|
|
612
|
+
expect(snap.edge[0][1]).toBeCloseTo(0);
|
|
613
|
+
expect(snap.edge[1][1]).toBeCloseTo(0);
|
|
614
|
+
// Tangential positions are preserved — pure perpendicular translation.
|
|
615
|
+
expect(snap.edge[0][0]).toBeCloseTo(0.5);
|
|
616
|
+
expect(snap.edge[1][0]).toBeCloseTo(3.5);
|
|
617
|
+
});
|
|
618
|
+
test('an edge outside the band does not snap', () => {
|
|
619
|
+
const snap = snapSlabEdgeToWallBand([0.5, 0.2], [3.5, 0.2], [wallOf([0, 0], [4, 0])]);
|
|
620
|
+
expect(snap).toBeNull();
|
|
621
|
+
});
|
|
622
|
+
test('maxLateral tightens the stick distance', () => {
|
|
623
|
+
const walls = [wallOf([0, 0], [4, 0])];
|
|
624
|
+
expect(snapSlabEdgeToWallBand([0.5, 0.08], [3.5, 0.08], walls, { maxLateral: 0.05 })).toBeNull();
|
|
625
|
+
expect(snapSlabEdgeToWallBand([0.5, 0.04], [3.5, 0.04], walls, { maxLateral: 0.05 })).not.toBeNull();
|
|
626
|
+
});
|
|
627
|
+
test('the nearest of two candidate walls wins', () => {
|
|
628
|
+
const near = wallOf([0, 0], [4, 0]);
|
|
629
|
+
const far = wallOf([0, 0.3], [4, 0.3], 0.3);
|
|
630
|
+
const snap = snapSlabEdgeToWallBand([0.5, 0.1], [3.5, 0.1], [far, near]);
|
|
631
|
+
expect(snap).not.toBeNull();
|
|
632
|
+
expect(snap.wallId).toBe(near.id);
|
|
633
|
+
expect(snap.edge[0][1]).toBeCloseTo(0);
|
|
634
|
+
});
|
|
635
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const SLOT_MATERIAL_PREFIX = "slot_";
|
|
2
|
+
/** A glTF material name marks a paintable slot when it starts with `slot_` (case-insensitive). */
|
|
3
|
+
export declare function isSlotMaterialName(name: string): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Derive the stable slot id from a glTF material name:
|
|
6
|
+
* strip the `slot_` prefix (case-insensitive), drop Blender numeric dedupe
|
|
7
|
+
* suffixes like `.001`, lowercase the remainder. Returns null when the name
|
|
8
|
+
* is not a slot material. Used by BOTH the upload scan (later) and the
|
|
9
|
+
* renderer so DB metadata and runtime meshes can never drift.
|
|
10
|
+
*/
|
|
11
|
+
export declare function deriveSlotId(materialName: string): string | null;
|
|
12
|
+
/** slot id -> display label: underscores to spaces, sentence case. e.g. 'bed_frame' -> 'Bed frame'. */
|
|
13
|
+
export declare function slotLabelFromId(slotId: string): string;
|
|
14
|
+
//# sourceMappingURL=slots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../src/lib/slots.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,UAAU,CAAA;AAE3C,kGAAkG;AAClG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKhE;AAED,uGAAuG;AACvG,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAItD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const SLOT_MATERIAL_PREFIX = 'slot_';
|
|
2
|
+
/** A glTF material name marks a paintable slot when it starts with `slot_` (case-insensitive). */
|
|
3
|
+
export function isSlotMaterialName(name) {
|
|
4
|
+
return name.toLowerCase().startsWith(SLOT_MATERIAL_PREFIX);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Derive the stable slot id from a glTF material name:
|
|
8
|
+
* strip the `slot_` prefix (case-insensitive), drop Blender numeric dedupe
|
|
9
|
+
* suffixes like `.001`, lowercase the remainder. Returns null when the name
|
|
10
|
+
* is not a slot material. Used by BOTH the upload scan (later) and the
|
|
11
|
+
* renderer so DB metadata and runtime meshes can never drift.
|
|
12
|
+
*/
|
|
13
|
+
export function deriveSlotId(materialName) {
|
|
14
|
+
if (!isSlotMaterialName(materialName))
|
|
15
|
+
return null;
|
|
16
|
+
let rest = materialName.slice(SLOT_MATERIAL_PREFIX.length);
|
|
17
|
+
rest = rest.replace(/\.\d+$/, '');
|
|
18
|
+
return rest.toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
/** slot id -> display label: underscores to spaces, sentence case. e.g. 'bed_frame' -> 'Bed frame'. */
|
|
21
|
+
export function slotLabelFromId(slotId) {
|
|
22
|
+
const spaced = slotId.replace(/_/g, ' ').trim();
|
|
23
|
+
if (!spaced)
|
|
24
|
+
return spaced;
|
|
25
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"space-detection-pause.test.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection-pause.test.ts"],"names":[],"mappings":""}
|