@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,88 @@
|
|
|
1
|
+
import type { SceneApi, SurfacesConfig } from '../registry/types';
|
|
2
|
+
import type { AnyNode, AnyNodeId } from '../schema/types';
|
|
3
|
+
/**
|
|
4
|
+
* Maximum depth a node tree can host. Guards items-on-items-on-items chains
|
|
5
|
+
* from growing pathological — pre-Phase-1 the editor had no cap. Set high
|
|
6
|
+
* enough to allow legitimate stacking (chair on platform on truck on floor)
|
|
7
|
+
* while preventing AI/plugin-generated runaway.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MAX_HOST_DEPTH = 6;
|
|
10
|
+
export type Vec3 = readonly [number, number, number];
|
|
11
|
+
export type AttachError = {
|
|
12
|
+
kind: 'self-host';
|
|
13
|
+
nodeId: AnyNodeId;
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'cycle';
|
|
16
|
+
nodeId: AnyNodeId;
|
|
17
|
+
hostId: AnyNodeId;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'depth-exceeded';
|
|
20
|
+
depth: number;
|
|
21
|
+
max: number;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'host-missing';
|
|
24
|
+
hostId: AnyNodeId;
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'kind-not-allowed';
|
|
27
|
+
hostKind: string;
|
|
28
|
+
allowed: readonly string[];
|
|
29
|
+
};
|
|
30
|
+
export type AttachResult = {
|
|
31
|
+
ok: true;
|
|
32
|
+
} | {
|
|
33
|
+
ok: false;
|
|
34
|
+
error: AttachError;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Validates that attaching `child` to `host` is safe and either returns an
|
|
38
|
+
* actionable error or signals OK. Does NOT mutate the scene — callers apply
|
|
39
|
+
* the patch after a successful check.
|
|
40
|
+
*
|
|
41
|
+
* Rules:
|
|
42
|
+
* - A node cannot host itself.
|
|
43
|
+
* - The hosting chain (child → host → host.parent → ...) must not contain
|
|
44
|
+
* `child` (cycle prevention).
|
|
45
|
+
* - The resulting chain must not exceed {@link MAX_HOST_DEPTH}.
|
|
46
|
+
* - If the child's NodeDefinition declares `capabilities.hostable.parents`,
|
|
47
|
+
* `host.type` must appear in that list.
|
|
48
|
+
*/
|
|
49
|
+
export declare function canAttach(childId: AnyNodeId, hostId: AnyNodeId, scene: SceneApi): AttachResult;
|
|
50
|
+
/**
|
|
51
|
+
* Returns the surfaces declared by a host's NodeDefinition. Surfaces describe
|
|
52
|
+
* where other nodes can stack/mount — the `top` of a slab, the `sides` of a
|
|
53
|
+
* wall, or a custom callback. Returns null when the host's def declares no
|
|
54
|
+
* surfaces (or no def is registered).
|
|
55
|
+
*/
|
|
56
|
+
export declare function getSurface(host: AnyNode): SurfacesConfig | null;
|
|
57
|
+
/**
|
|
58
|
+
* Resolves the stackable top height of a host (e.g. table surface, slab top,
|
|
59
|
+
* stair landing). Returns `null` when the host has no `surfaces.top`.
|
|
60
|
+
*/
|
|
61
|
+
export declare function getTopSurfaceHeight(host: AnyNode): number | null;
|
|
62
|
+
/**
|
|
63
|
+
* Whether `host` can receive a surface-resting (top-stacked) child. A
|
|
64
|
+
* ceiling-mounted item hangs from the ceiling, so its visible "top" is not a
|
|
65
|
+
* usable resting surface — nothing should stack on a ceiling fan. The check
|
|
66
|
+
* reads the instance-level `asset.attachTo` (not the host KIND, which is shared
|
|
67
|
+
* across all items) so a single gate covers every interaction path.
|
|
68
|
+
*/
|
|
69
|
+
export declare function canHostOnTop(host: AnyNode): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Pure host-discovery helper. Given a list of candidate hosts (already
|
|
72
|
+
* narrowed by spatial query) and a point, returns the first whose
|
|
73
|
+
* `capabilities.hostable` lists `placedKind` AND whose surface contains the
|
|
74
|
+
* point. The runtime is responsible for providing pre-filtered candidates;
|
|
75
|
+
* this function does not perform spatial queries itself.
|
|
76
|
+
*/
|
|
77
|
+
export declare function pickHost(args: {
|
|
78
|
+
point: Vec3;
|
|
79
|
+
candidates: readonly AnyNode[];
|
|
80
|
+
placedKind: string;
|
|
81
|
+
hitTest?: (host: AnyNode, point: Vec3) => boolean;
|
|
82
|
+
}): AnyNode | null;
|
|
83
|
+
/**
|
|
84
|
+
* Convenience: clamps a Y coordinate to the top of a host surface, when one
|
|
85
|
+
* is declared. Returns the original Y if the host has no top surface.
|
|
86
|
+
*/
|
|
87
|
+
export declare function clampYToHostTop(host: AnyNode, originalY: number): number;
|
|
88
|
+
//# sourceMappingURL=hosting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosting.d.ts","sourceRoot":"","sources":["../../src/services/hosting.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,IAAI,CAAA;AAE/B,MAAM,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAEpD,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAA;AAE9E,MAAM,MAAM,YAAY,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAA;AAE3E;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,GAAG,YAAY,CAoC9F;AAoBD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAG/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAKhE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAC7B,KAAK,EAAE,IAAI,CAAA;IACX,UAAU,EAAE,SAAS,OAAO,EAAE,CAAA;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,OAAO,CAAA;CAClD,GAAG,OAAO,GAAG,IAAI,CAUjB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGxE"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { nodeRegistry } from '../registry/registry';
|
|
2
|
+
/**
|
|
3
|
+
* Maximum depth a node tree can host. Guards items-on-items-on-items chains
|
|
4
|
+
* from growing pathological — pre-Phase-1 the editor had no cap. Set high
|
|
5
|
+
* enough to allow legitimate stacking (chair on platform on truck on floor)
|
|
6
|
+
* while preventing AI/plugin-generated runaway.
|
|
7
|
+
*/
|
|
8
|
+
export const MAX_HOST_DEPTH = 6;
|
|
9
|
+
/**
|
|
10
|
+
* Validates that attaching `child` to `host` is safe and either returns an
|
|
11
|
+
* actionable error or signals OK. Does NOT mutate the scene — callers apply
|
|
12
|
+
* the patch after a successful check.
|
|
13
|
+
*
|
|
14
|
+
* Rules:
|
|
15
|
+
* - A node cannot host itself.
|
|
16
|
+
* - The hosting chain (child → host → host.parent → ...) must not contain
|
|
17
|
+
* `child` (cycle prevention).
|
|
18
|
+
* - The resulting chain must not exceed {@link MAX_HOST_DEPTH}.
|
|
19
|
+
* - If the child's NodeDefinition declares `capabilities.hostable.parents`,
|
|
20
|
+
* `host.type` must appear in that list.
|
|
21
|
+
*/
|
|
22
|
+
export function canAttach(childId, hostId, scene) {
|
|
23
|
+
if (childId === hostId) {
|
|
24
|
+
return { ok: false, error: { kind: 'self-host', nodeId: childId } };
|
|
25
|
+
}
|
|
26
|
+
const host = scene.get(hostId);
|
|
27
|
+
if (!host) {
|
|
28
|
+
return { ok: false, error: { kind: 'host-missing', hostId } };
|
|
29
|
+
}
|
|
30
|
+
const child = scene.get(childId);
|
|
31
|
+
if (!child) {
|
|
32
|
+
// No child node yet — likely a placement preview. Allow attach to proceed;
|
|
33
|
+
// the caller is responsible for ensuring child exists before commit.
|
|
34
|
+
return checkDepth(hostId, scene);
|
|
35
|
+
}
|
|
36
|
+
const childDef = nodeRegistry.get(child.type);
|
|
37
|
+
const allowed = childDef?.capabilities.hostable?.parents;
|
|
38
|
+
if (allowed && allowed.length > 0 && !allowed.includes(host.type)) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
error: { kind: 'kind-not-allowed', hostKind: host.type, allowed },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// Cycle: walk host's ancestors and reject if we hit the child.
|
|
45
|
+
let cursor = host;
|
|
46
|
+
while (cursor) {
|
|
47
|
+
if (cursor.id === childId) {
|
|
48
|
+
return { ok: false, error: { kind: 'cycle', nodeId: childId, hostId } };
|
|
49
|
+
}
|
|
50
|
+
cursor = cursor.parentId ? scene.get(cursor.parentId) : undefined;
|
|
51
|
+
}
|
|
52
|
+
return checkDepth(hostId, scene);
|
|
53
|
+
}
|
|
54
|
+
function checkDepth(hostId, scene) {
|
|
55
|
+
// Count host's own depth (root = 0); attaching adds 1 to the child's depth.
|
|
56
|
+
let depth = 0;
|
|
57
|
+
let cursor = scene.get(hostId);
|
|
58
|
+
while (cursor?.parentId) {
|
|
59
|
+
cursor = scene.get(cursor.parentId);
|
|
60
|
+
depth += 1;
|
|
61
|
+
if (depth > MAX_HOST_DEPTH) {
|
|
62
|
+
return { ok: false, error: { kind: 'depth-exceeded', depth, max: MAX_HOST_DEPTH } };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Child sits one below host.
|
|
66
|
+
if (depth + 1 > MAX_HOST_DEPTH) {
|
|
67
|
+
return { ok: false, error: { kind: 'depth-exceeded', depth: depth + 1, max: MAX_HOST_DEPTH } };
|
|
68
|
+
}
|
|
69
|
+
return { ok: true };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns the surfaces declared by a host's NodeDefinition. Surfaces describe
|
|
73
|
+
* where other nodes can stack/mount — the `top` of a slab, the `sides` of a
|
|
74
|
+
* wall, or a custom callback. Returns null when the host's def declares no
|
|
75
|
+
* surfaces (or no def is registered).
|
|
76
|
+
*/
|
|
77
|
+
export function getSurface(host) {
|
|
78
|
+
const def = nodeRegistry.get(host.type);
|
|
79
|
+
return def?.capabilities.surfaces ?? null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolves the stackable top height of a host (e.g. table surface, slab top,
|
|
83
|
+
* stair landing). Returns `null` when the host has no `surfaces.top`.
|
|
84
|
+
*/
|
|
85
|
+
export function getTopSurfaceHeight(host) {
|
|
86
|
+
const surfaces = getSurface(host);
|
|
87
|
+
if (!surfaces?.top)
|
|
88
|
+
return null;
|
|
89
|
+
const { height } = surfaces.top;
|
|
90
|
+
return typeof height === 'function' ? height(host) : height;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Whether `host` can receive a surface-resting (top-stacked) child. A
|
|
94
|
+
* ceiling-mounted item hangs from the ceiling, so its visible "top" is not a
|
|
95
|
+
* usable resting surface — nothing should stack on a ceiling fan. The check
|
|
96
|
+
* reads the instance-level `asset.attachTo` (not the host KIND, which is shared
|
|
97
|
+
* across all items) so a single gate covers every interaction path.
|
|
98
|
+
*/
|
|
99
|
+
export function canHostOnTop(host) {
|
|
100
|
+
const attachTo = host.asset?.attachTo;
|
|
101
|
+
return attachTo !== 'ceiling';
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Pure host-discovery helper. Given a list of candidate hosts (already
|
|
105
|
+
* narrowed by spatial query) and a point, returns the first whose
|
|
106
|
+
* `capabilities.hostable` lists `placedKind` AND whose surface contains the
|
|
107
|
+
* point. The runtime is responsible for providing pre-filtered candidates;
|
|
108
|
+
* this function does not perform spatial queries itself.
|
|
109
|
+
*/
|
|
110
|
+
export function pickHost(args) {
|
|
111
|
+
for (const host of args.candidates) {
|
|
112
|
+
const def = nodeRegistry.get(host.type);
|
|
113
|
+
const hostable = def?.capabilities.hostable;
|
|
114
|
+
if (!hostable)
|
|
115
|
+
continue;
|
|
116
|
+
if (!canHostOnTop(host))
|
|
117
|
+
continue;
|
|
118
|
+
if (args.hitTest && !args.hitTest(host, args.point))
|
|
119
|
+
continue;
|
|
120
|
+
return host;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Convenience: clamps a Y coordinate to the top of a host surface, when one
|
|
126
|
+
* is declared. Returns the original Y if the host has no top surface.
|
|
127
|
+
*/
|
|
128
|
+
export function clampYToHostTop(host, originalY) {
|
|
129
|
+
const top = getTopSurfaceHeight(host);
|
|
130
|
+
return top == null ? originalY : top;
|
|
131
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosting.test.d.ts","sourceRoot":"","sources":["../../src/services/hosting.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { nodeRegistry, registerNode } from '../registry/registry';
|
|
4
|
+
import { canAttach, canHostOnTop, clampYToHostTop, getSurface, getTopSurfaceHeight, MAX_HOST_DEPTH, pickHost, } from './hosting';
|
|
5
|
+
const id = (s) => s;
|
|
6
|
+
function makeItem(idStr, attachTo) {
|
|
7
|
+
return {
|
|
8
|
+
id: id(idStr),
|
|
9
|
+
type: 'item',
|
|
10
|
+
parentId: null,
|
|
11
|
+
visible: true,
|
|
12
|
+
asset: attachTo ? { attachTo } : {},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function makeDef(kind, capabilities = {}, overrides = {}) {
|
|
16
|
+
return {
|
|
17
|
+
kind,
|
|
18
|
+
schemaVersion: 1,
|
|
19
|
+
schema: z.object({ type: z.literal(kind) }),
|
|
20
|
+
category: 'utility',
|
|
21
|
+
defaults: () => ({}),
|
|
22
|
+
capabilities,
|
|
23
|
+
renderer: { kind: 'parametric', module: async () => ({ default: () => null }) },
|
|
24
|
+
...overrides,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function makeNode(kind, idStr, parentId = null) {
|
|
28
|
+
return {
|
|
29
|
+
id: id(idStr),
|
|
30
|
+
type: kind,
|
|
31
|
+
parentId: parentId ? id(parentId) : null,
|
|
32
|
+
visible: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function makeFakeScene(nodes) {
|
|
36
|
+
return {
|
|
37
|
+
get: ((nid) => nodes[nid]),
|
|
38
|
+
nodes: () => nodes,
|
|
39
|
+
update: () => { },
|
|
40
|
+
upsert: () => id(''),
|
|
41
|
+
delete: () => { },
|
|
42
|
+
restore: () => { },
|
|
43
|
+
restoreAll: () => { },
|
|
44
|
+
markDirty: () => { },
|
|
45
|
+
pauseHistory: () => { },
|
|
46
|
+
resumeHistory: () => { },
|
|
47
|
+
getSubtree: () => null,
|
|
48
|
+
cloneNodesInto: () => null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
describe('canAttach', () => {
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
nodeRegistry._reset();
|
|
54
|
+
});
|
|
55
|
+
test('rejects self-host', () => {
|
|
56
|
+
const scene = makeFakeScene({ a: makeNode('thing', 'a') });
|
|
57
|
+
const result = canAttach(id('a'), id('a'), scene);
|
|
58
|
+
expect(result.ok).toBe(false);
|
|
59
|
+
if (!result.ok)
|
|
60
|
+
expect(result.error.kind).toBe('self-host');
|
|
61
|
+
});
|
|
62
|
+
test('rejects missing host', () => {
|
|
63
|
+
const scene = makeFakeScene({ a: makeNode('thing', 'a') });
|
|
64
|
+
const result = canAttach(id('a'), id('missing'), scene);
|
|
65
|
+
expect(result.ok).toBe(false);
|
|
66
|
+
if (!result.ok)
|
|
67
|
+
expect(result.error.kind).toBe('host-missing');
|
|
68
|
+
});
|
|
69
|
+
test('detects cycle when host is a descendant of child', () => {
|
|
70
|
+
// child=a, host=b, b's parent chain leads back to a → cycle.
|
|
71
|
+
const scene = makeFakeScene({
|
|
72
|
+
a: makeNode('thing', 'a'),
|
|
73
|
+
b: makeNode('thing', 'b', 'c'),
|
|
74
|
+
c: makeNode('thing', 'c', 'a'), // c.parent = a, b.parent = c → cycle if a → b
|
|
75
|
+
});
|
|
76
|
+
const result = canAttach(id('a'), id('b'), scene);
|
|
77
|
+
expect(result.ok).toBe(false);
|
|
78
|
+
if (!result.ok)
|
|
79
|
+
expect(result.error.kind).toBe('cycle');
|
|
80
|
+
});
|
|
81
|
+
test('rejects when chain would exceed MAX_HOST_DEPTH', () => {
|
|
82
|
+
const nodes = {};
|
|
83
|
+
for (let i = 0; i <= MAX_HOST_DEPTH; i++) {
|
|
84
|
+
nodes[`n${i}`] = makeNode('thing', `n${i}`, i === 0 ? null : `n${i - 1}`);
|
|
85
|
+
}
|
|
86
|
+
// n6 is already MAX_HOST_DEPTH deep — attaching n_new beneath it would
|
|
87
|
+
// push the child to MAX_HOST_DEPTH + 1.
|
|
88
|
+
nodes.candidate = makeNode('thing', 'candidate');
|
|
89
|
+
const scene = makeFakeScene(nodes);
|
|
90
|
+
const result = canAttach(id('candidate'), id(`n${MAX_HOST_DEPTH}`), scene);
|
|
91
|
+
expect(result.ok).toBe(false);
|
|
92
|
+
if (!result.ok)
|
|
93
|
+
expect(result.error.kind).toBe('depth-exceeded');
|
|
94
|
+
});
|
|
95
|
+
test('accepts attach when chain stays within MAX_HOST_DEPTH', () => {
|
|
96
|
+
const scene = makeFakeScene({
|
|
97
|
+
root: makeNode('thing', 'root'),
|
|
98
|
+
candidate: makeNode('thing', 'candidate'),
|
|
99
|
+
});
|
|
100
|
+
expect(canAttach(id('candidate'), id('root'), scene).ok).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
test('rejects host kind not in child def.hostable.parents', () => {
|
|
103
|
+
registerNode(makeDef('shelf', { hostable: { parents: ['wall', 'slab'] } }));
|
|
104
|
+
const scene = makeFakeScene({
|
|
105
|
+
s: makeNode('shelf', 's'),
|
|
106
|
+
ceiling: makeNode('ceiling', 'ceiling'),
|
|
107
|
+
});
|
|
108
|
+
const result = canAttach(id('s'), id('ceiling'), scene);
|
|
109
|
+
expect(result.ok).toBe(false);
|
|
110
|
+
if (!result.ok)
|
|
111
|
+
expect(result.error.kind).toBe('kind-not-allowed');
|
|
112
|
+
});
|
|
113
|
+
test('accepts when host kind is in parents', () => {
|
|
114
|
+
registerNode(makeDef('shelf', { hostable: { parents: ['wall', 'slab'] } }));
|
|
115
|
+
const scene = makeFakeScene({
|
|
116
|
+
s: makeNode('shelf', 's'),
|
|
117
|
+
w: makeNode('wall', 'w'),
|
|
118
|
+
});
|
|
119
|
+
expect(canAttach(id('s'), id('w'), scene).ok).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
test('no def or no hostable.parents = no kind restriction', () => {
|
|
122
|
+
// Some kinds (e.g. items via catalog) defer to runtime checks instead of
|
|
123
|
+
// declaring parents up front. canAttach should not block them.
|
|
124
|
+
const scene = makeFakeScene({
|
|
125
|
+
i: makeNode('item', 'i'),
|
|
126
|
+
w: makeNode('wall', 'w'),
|
|
127
|
+
});
|
|
128
|
+
expect(canAttach(id('i'), id('w'), scene).ok).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
test('allows missing child (placement preview before commit)', () => {
|
|
131
|
+
const scene = makeFakeScene({ w: makeNode('wall', 'w') });
|
|
132
|
+
expect(canAttach(id('future-child'), id('w'), scene).ok).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe('getSurface / getTopSurfaceHeight', () => {
|
|
136
|
+
beforeEach(() => {
|
|
137
|
+
nodeRegistry._reset();
|
|
138
|
+
});
|
|
139
|
+
test('returns null when host has no registered def', () => {
|
|
140
|
+
const node = makeNode('mystery', 'm');
|
|
141
|
+
expect(getSurface(node)).toBeNull();
|
|
142
|
+
expect(getTopSurfaceHeight(node)).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
test('returns surface config when declared', () => {
|
|
145
|
+
registerNode(makeDef('table', {
|
|
146
|
+
surfaces: { top: { height: 0.74 } },
|
|
147
|
+
}));
|
|
148
|
+
const t = makeNode('table', 't');
|
|
149
|
+
expect(getSurface(t)?.top?.height).toBe(0.74);
|
|
150
|
+
expect(getTopSurfaceHeight(t)).toBe(0.74);
|
|
151
|
+
});
|
|
152
|
+
test('evaluates function-valued height with the node', () => {
|
|
153
|
+
registerNode(makeDef('shelf', {
|
|
154
|
+
surfaces: {
|
|
155
|
+
top: {
|
|
156
|
+
height: (n) => (n.id === id('high') ? 1.8 : 0.3),
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
}));
|
|
160
|
+
expect(getTopSurfaceHeight(makeNode('shelf', 'high'))).toBe(1.8);
|
|
161
|
+
expect(getTopSurfaceHeight(makeNode('shelf', 'low'))).toBe(0.3);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe('clampYToHostTop', () => {
|
|
165
|
+
beforeEach(() => {
|
|
166
|
+
nodeRegistry._reset();
|
|
167
|
+
});
|
|
168
|
+
test('clamps to top when host has one', () => {
|
|
169
|
+
registerNode(makeDef('table', { surfaces: { top: { height: 0.74 } } }));
|
|
170
|
+
expect(clampYToHostTop(makeNode('table', 't'), 5)).toBe(0.74);
|
|
171
|
+
});
|
|
172
|
+
test('passes through when host has no top surface', () => {
|
|
173
|
+
registerNode(makeDef('plain'));
|
|
174
|
+
expect(clampYToHostTop(makeNode('plain', 'p'), 5)).toBe(5);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe('pickHost', () => {
|
|
178
|
+
beforeEach(() => {
|
|
179
|
+
nodeRegistry._reset();
|
|
180
|
+
});
|
|
181
|
+
test('returns first candidate that has hostable capability', () => {
|
|
182
|
+
registerNode(makeDef('slab', { hostable: { parents: ['*'] } }));
|
|
183
|
+
registerNode(makeDef('item')); // no hostable
|
|
184
|
+
const candidates = [makeNode('item', 'i'), makeNode('slab', 's')];
|
|
185
|
+
const picked = pickHost({
|
|
186
|
+
point: [0, 0, 0],
|
|
187
|
+
candidates,
|
|
188
|
+
placedKind: 'chair',
|
|
189
|
+
});
|
|
190
|
+
expect(picked?.id).toBe(id('s'));
|
|
191
|
+
});
|
|
192
|
+
test('returns null when nothing in candidates is hostable', () => {
|
|
193
|
+
registerNode(makeDef('item'));
|
|
194
|
+
const candidates = [makeNode('item', 'i')];
|
|
195
|
+
expect(pickHost({ point: [0, 0, 0], candidates, placedKind: 'chair' })).toBeNull();
|
|
196
|
+
});
|
|
197
|
+
test('hitTest can reject hostable candidates', () => {
|
|
198
|
+
registerNode(makeDef('slab', { hostable: { parents: ['*'] } }));
|
|
199
|
+
const candidates = [makeNode('slab', 's1'), makeNode('slab', 's2')];
|
|
200
|
+
const picked = pickHost({
|
|
201
|
+
point: [0, 0, 0],
|
|
202
|
+
candidates,
|
|
203
|
+
placedKind: 'chair',
|
|
204
|
+
hitTest: (host) => host.id === id('s2'),
|
|
205
|
+
});
|
|
206
|
+
expect(picked?.id).toBe(id('s2'));
|
|
207
|
+
});
|
|
208
|
+
test('excludes ceiling-mounted hosts (ceiling fan cannot be a top surface)', () => {
|
|
209
|
+
registerNode(makeDef('item', { hostable: { parents: ['*'] } }));
|
|
210
|
+
const candidates = [makeItem('fan', 'ceiling'), makeItem('table')];
|
|
211
|
+
const picked = pickHost({ point: [0, 0, 0], candidates, placedKind: 'item' });
|
|
212
|
+
expect(picked?.id).toBe(id('table'));
|
|
213
|
+
});
|
|
214
|
+
test('keeps wall-mounted hosts (wall shelf still hosts)', () => {
|
|
215
|
+
registerNode(makeDef('item', { hostable: { parents: ['*'] } }));
|
|
216
|
+
const candidates = [makeItem('shelf', 'wall')];
|
|
217
|
+
const picked = pickHost({ point: [0, 0, 0], candidates, placedKind: 'item' });
|
|
218
|
+
expect(picked?.id).toBe(id('shelf'));
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe('canHostOnTop', () => {
|
|
222
|
+
test('rejects ceiling-attachTo hosts', () => {
|
|
223
|
+
expect(canHostOnTop(makeItem('fan', 'ceiling'))).toBe(false);
|
|
224
|
+
});
|
|
225
|
+
test('accepts wall / wall-side / floor (undefined) hosts', () => {
|
|
226
|
+
expect(canHostOnTop(makeItem('shelf', 'wall'))).toBe(true);
|
|
227
|
+
expect(canHostOnTop(makeItem('sconce', 'wall-side'))).toBe(true);
|
|
228
|
+
expect(canHostOnTop(makeItem('table'))).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { type AlignmentAnchor, type AlignmentGuide, type AlignmentGuideAxis, type AnchorKind, type BuildingPose, bboxAnchors, bboxCornerAnchors, type ResolveAlignmentInBuildingResult, type ResolveAlignmentInput, type ResolveAlignmentResult, resolveAlignment, resolveAlignmentInBuildingWorld, } from './alignment';
|
|
2
|
+
export { collectAlignmentAnchors, type FootprintAABB, footprintAABB, footprintAABBAt, footprintAABBFrom, movingAlignmentAnchors, movingFootprintAnchors, nodeAlignmentAnchors, polygonAnchors, wallSegmentAnchors, } from './alignment-anchors';
|
|
3
|
+
export { createDragSession, type DragSession, type DragSessionInput, type DragSessionOptions, } from './drag-session';
|
|
4
|
+
export { type AttachError, type AttachResult, canAttach, canHostOnTop, clampYToHostTop, getSurface, getTopSurfaceHeight, MAX_HOST_DEPTH, pickHost, type Vec3, } from './hosting';
|
|
5
|
+
export { DEFAULT_LEVEL_HEIGHT, getCeilingAt, getCeilingHeightAt, getLevelHeight, } from './level-height';
|
|
6
|
+
export { type AxisLock, applyAxisLock, isMovable, movePlanToward, moveToward, resolveMovable, } from './movement';
|
|
7
|
+
export { type AlongWallAlignment, type AlongWallFeature, computeEdgeGaps, computeOpeningGuides, DEFAULT_OPENING_GUIDE_TOLERANCES, detectAlongWallAlignment, detectEqualSpacing, detectVerticalAlignment, type EdgeGap, type EqualSpacingRun, type OpeningGuideInput, type OpeningGuides, type OpeningGuideTolerances, type OpeningSpan, type SillHeadGuide, type VerticalAlignment, type VerticalFeature, type WallExtent, } from './opening-guides';
|
|
8
|
+
export { analyzePortConnectivity, type PortConnection, type PortConnectivity, resolveConnectivityUpdates, } from './port-connectivity';
|
|
9
|
+
export { buildRiserDiagram, projectIso, type RiserDiagram, type RiserLine, type RiserMarker, } from './riser-diagram';
|
|
10
|
+
export { DEFAULT_ANGLE_STEP, DEFAULT_GRID_STEP, type SnapServices, snapAngleToList, snapPointAlongAngleRay, snapPointToAngle, snapPointToGrid, snapScalar, snapServices, snapVec3ToGrid, snapWorldXZToBuildingLocal, } from './snap';
|
|
11
|
+
export { buildPortComponents, type SystemSummary, summarizeSystemFor, } from './system-graph';
|
|
12
|
+
export { type DwvFinding, type DwvSeverity, validateDwv, } from './validate-dwv';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,WAAW,EACX,iBAAiB,EACjB,KAAK,gCAAgC,EACrC,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,+BAA+B,GAChC,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,uBAAuB,EACvB,KAAK,aAAa,EAClB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,GACnB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,SAAS,EACT,YAAY,EACZ,eAAe,EACf,UAAU,EACV,mBAAmB,EACnB,cAAc,EACd,QAAQ,EACR,KAAK,IAAI,GACV,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,EAClB,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,KAAK,QAAQ,EACb,aAAa,EACb,SAAS,EACT,cAAc,EACd,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,eAAe,EACf,oBAAoB,EACpB,gCAAgC,EAChC,wBAAwB,EACxB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,YAAY,EACjB,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,UAAU,EACV,YAAY,EACZ,cAAc,EACd,0BAA0B,GAC3B,MAAM,QAAQ,CAAA;AACf,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,kBAAkB,GACnB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,WAAW,GACZ,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { bboxAnchors, bboxCornerAnchors, resolveAlignment, resolveAlignmentInBuildingWorld, } from './alignment';
|
|
2
|
+
export { collectAlignmentAnchors, footprintAABB, footprintAABBAt, footprintAABBFrom, movingAlignmentAnchors, movingFootprintAnchors, nodeAlignmentAnchors, polygonAnchors, wallSegmentAnchors, } from './alignment-anchors';
|
|
3
|
+
export { createDragSession, } from './drag-session';
|
|
4
|
+
export { canAttach, canHostOnTop, clampYToHostTop, getSurface, getTopSurfaceHeight, MAX_HOST_DEPTH, pickHost, } from './hosting';
|
|
5
|
+
export { DEFAULT_LEVEL_HEIGHT, getCeilingAt, getCeilingHeightAt, getLevelHeight, } from './level-height';
|
|
6
|
+
export { applyAxisLock, isMovable, movePlanToward, moveToward, resolveMovable, } from './movement';
|
|
7
|
+
export { computeEdgeGaps, computeOpeningGuides, DEFAULT_OPENING_GUIDE_TOLERANCES, detectAlongWallAlignment, detectEqualSpacing, detectVerticalAlignment, } from './opening-guides';
|
|
8
|
+
export { analyzePortConnectivity, resolveConnectivityUpdates, } from './port-connectivity';
|
|
9
|
+
export { buildRiserDiagram, projectIso, } from './riser-diagram';
|
|
10
|
+
export { DEFAULT_ANGLE_STEP, DEFAULT_GRID_STEP, snapAngleToList, snapPointAlongAngleRay, snapPointToAngle, snapPointToGrid, snapScalar, snapServices, snapVec3ToGrid, snapWorldXZToBuildingLocal, } from './snap';
|
|
11
|
+
export { buildPortComponents, summarizeSystemFor, } from './system-graph';
|
|
12
|
+
export { validateDwv, } from './validate-dwv';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CeilingNode } from '../schema';
|
|
2
|
+
import type { AnyNode, AnyNodeId } from '../schema/types';
|
|
3
|
+
export declare const DEFAULT_LEVEL_HEIGHT = 2.5;
|
|
4
|
+
/**
|
|
5
|
+
* Optional resolver for a wall's rendered base Y (mesh elevation).
|
|
6
|
+
*
|
|
7
|
+
* `packages/core` is pure domain logic and must not read viewer/Three.js
|
|
8
|
+
* state (see AGENTS.md “Layer Boundaries”). Callers that legitimately have
|
|
9
|
+
* registry access (viewer systems, node tools) may pass a resolver so the
|
|
10
|
+
* mesh elevation is factored in; pure/headless callers (MCP, tests, server)
|
|
11
|
+
* omit it and get a deterministic result from serialized node data alone.
|
|
12
|
+
*/
|
|
13
|
+
export type WallBaseYResolver = (wallId: AnyNodeId) => number | undefined;
|
|
14
|
+
export declare function getLevelHeight(levelId: string, nodes: Record<AnyNodeId, AnyNode>, resolveWallBaseY?: WallBaseYResolver): number;
|
|
15
|
+
/**
|
|
16
|
+
* The ceiling covering level-local point `[x, z]`, or `null` when none
|
|
17
|
+
* sits over it. Points inside a ceiling's hole are treated as uncovered.
|
|
18
|
+
* When ceilings overlap, the lowest one wins — that's the surface a duct
|
|
19
|
+
* would actually hang from.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getCeilingAt(levelId: string, nodes: Record<AnyNodeId, AnyNode>, x: number, z: number): CeilingNode | null;
|
|
22
|
+
/**
|
|
23
|
+
* Underside elevation (meters above the level floor) of the ceiling
|
|
24
|
+
* covering level-local point `[x, z]`, or `null` when no ceiling sits
|
|
25
|
+
* over that point. See {@link getCeilingAt}.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getCeilingHeightAt(levelId: string, nodes: Record<AnyNodeId, AnyNode>, x: number, z: number): number | null;
|
|
28
|
+
//# sourceMappingURL=level-height.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"level-height.d.ts","sourceRoot":"","sources":["../../src/services/level-height.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,WAAW,CAAA;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEzD,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,SAAS,KAAK,MAAM,GAAG,SAAS,CAAA;AAEzE,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,gBAAgB,CAAC,EAAE,iBAAiB,GACnC,MAAM,CAqBR;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,WAAW,GAAG,IAAI,CAepB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,MAAM,GAAG,IAAI,CAGf"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { pointInPolygon } from '../hooks/spatial-grid/spatial-grid-manager';
|
|
2
|
+
export const DEFAULT_LEVEL_HEIGHT = 2.5;
|
|
3
|
+
export function getLevelHeight(levelId, nodes, resolveWallBaseY) {
|
|
4
|
+
const level = nodes[levelId];
|
|
5
|
+
if (!level)
|
|
6
|
+
return DEFAULT_LEVEL_HEIGHT;
|
|
7
|
+
let maxTop = 0;
|
|
8
|
+
for (const childId of level.children) {
|
|
9
|
+
const child = nodes[childId];
|
|
10
|
+
if (!child)
|
|
11
|
+
continue;
|
|
12
|
+
if (child.type === 'ceiling') {
|
|
13
|
+
const ch = child.height ?? DEFAULT_LEVEL_HEIGHT;
|
|
14
|
+
if (ch > maxTop)
|
|
15
|
+
maxTop = ch;
|
|
16
|
+
}
|
|
17
|
+
else if (child.type === 'wall') {
|
|
18
|
+
let baseY = resolveWallBaseY?.(childId) ?? 0;
|
|
19
|
+
if (baseY < 0)
|
|
20
|
+
baseY = 0;
|
|
21
|
+
const top = baseY + (child.height ?? DEFAULT_LEVEL_HEIGHT);
|
|
22
|
+
if (top > maxTop)
|
|
23
|
+
maxTop = top;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return maxTop > 0 ? maxTop : DEFAULT_LEVEL_HEIGHT;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The ceiling covering level-local point `[x, z]`, or `null` when none
|
|
30
|
+
* sits over it. Points inside a ceiling's hole are treated as uncovered.
|
|
31
|
+
* When ceilings overlap, the lowest one wins — that's the surface a duct
|
|
32
|
+
* would actually hang from.
|
|
33
|
+
*/
|
|
34
|
+
export function getCeilingAt(levelId, nodes, x, z) {
|
|
35
|
+
const level = nodes[levelId];
|
|
36
|
+
if (!level)
|
|
37
|
+
return null;
|
|
38
|
+
let best = null;
|
|
39
|
+
for (const childId of level.children) {
|
|
40
|
+
const child = nodes[childId];
|
|
41
|
+
if (child?.type !== 'ceiling')
|
|
42
|
+
continue;
|
|
43
|
+
const ceiling = child;
|
|
44
|
+
if (ceiling.polygon.length < 3 || !pointInPolygon(x, z, ceiling.polygon))
|
|
45
|
+
continue;
|
|
46
|
+
if (ceiling.holes.some((hole) => hole.length >= 3 && pointInPolygon(x, z, hole)))
|
|
47
|
+
continue;
|
|
48
|
+
const h = ceiling.height ?? DEFAULT_LEVEL_HEIGHT;
|
|
49
|
+
if (best === null || h < (best.height ?? DEFAULT_LEVEL_HEIGHT))
|
|
50
|
+
best = ceiling;
|
|
51
|
+
}
|
|
52
|
+
return best;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Underside elevation (meters above the level floor) of the ceiling
|
|
56
|
+
* covering level-local point `[x, z]`, or `null` when no ceiling sits
|
|
57
|
+
* over that point. See {@link getCeilingAt}.
|
|
58
|
+
*/
|
|
59
|
+
export function getCeilingHeightAt(levelId, nodes, x, z) {
|
|
60
|
+
const ceiling = getCeilingAt(levelId, nodes, x, z);
|
|
61
|
+
return ceiling ? (ceiling.height ?? DEFAULT_LEVEL_HEIGHT) : null;
|
|
62
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { MovableConfig } from '../registry/types';
|
|
2
|
+
import type { AnyNode } from '../schema/types';
|
|
3
|
+
import { type Vec3 } from './snap';
|
|
4
|
+
/**
|
|
5
|
+
* Pure movement constraint helpers. Given a node and a target position, apply
|
|
6
|
+
* the constraints declared in `def.capabilities.movable` (axis lock, grid
|
|
7
|
+
* snap, override callback) and return the constrained target.
|
|
8
|
+
*
|
|
9
|
+
* No scene access, no React, no Three.js — caller passes the node, this
|
|
10
|
+
* returns the math result.
|
|
11
|
+
*/
|
|
12
|
+
export type AxisLock = ReadonlyArray<'x' | 'y' | 'z'>;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the MovableConfig effective for `node` after running its `override`
|
|
15
|
+
* callback if declared. Returns `null` if the node's def doesn't declare
|
|
16
|
+
* `movable` (i.e. the node is not movable).
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveMovable(node: AnyNode): MovableConfig | null;
|
|
19
|
+
/**
|
|
20
|
+
* Projects a target X/Y/Z onto the axes a node is allowed to move on. Components
|
|
21
|
+
* outside the lock fall back to the node's current values, so caller-supplied
|
|
22
|
+
* positions can come from any 3D source without breaking axis-locked motion.
|
|
23
|
+
*/
|
|
24
|
+
export declare function applyAxisLock(current: Vec3, target: Vec3, axes: AxisLock): Vec3;
|
|
25
|
+
/**
|
|
26
|
+
* Top-level helper: takes a node and a desired position, returns the position
|
|
27
|
+
* filtered through the node's movable capability (axis lock + optional grid
|
|
28
|
+
* snap). Returns `null` when the node is not movable.
|
|
29
|
+
*/
|
|
30
|
+
export declare function moveToward(node: AnyNode, current: Vec3, target: Vec3, options?: {
|
|
31
|
+
gridStep?: number;
|
|
32
|
+
gridSnap?: boolean;
|
|
33
|
+
}): Vec3 | null;
|
|
34
|
+
/**
|
|
35
|
+
* 2D convenience: same as moveToward but for plan-view (X/Z) operations like
|
|
36
|
+
* floor placement. Returns a tuple in the X/Z plane so callers don't have to
|
|
37
|
+
* pack/unpack the dropped Y.
|
|
38
|
+
*/
|
|
39
|
+
export declare function movePlanToward(node: AnyNode, currentY: number, current: readonly [number, number], target: readonly [number, number], options?: {
|
|
40
|
+
gridStep?: number;
|
|
41
|
+
gridSnap?: boolean;
|
|
42
|
+
}): readonly [number, number] | null;
|
|
43
|
+
/**
|
|
44
|
+
* Returns true when a node's def declares it as movable on any axis.
|
|
45
|
+
* Quick predicate for tools/UI that gate on movability.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isMovable(node: AnyNode): boolean;
|
|
48
|
+
//# sourceMappingURL=movement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"movement.d.ts","sourceRoot":"","sources":["../../src/services/movement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAkB,KAAK,IAAI,EAAE,MAAM,QAAQ,CAAA;AAElD;;;;;;;GAOG;AAEH,MAAM,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAA;AAErD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CASlE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,CAM/E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,IAAI,EACb,MAAM,EAAE,IAAI,EACZ,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,IAAI,GAAG,IAAI,CAYb;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CASlC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAGhD"}
|