@pascal-app/core 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/events/bus.d.ts +39 -13
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts +6 -22
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +33 -26
- 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 +273 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +13 -2
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +95 -9
- 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 +91 -18
- package/dist/index.d.ts +22 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- 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/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 +30 -1
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +215 -36
- 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 +77 -0
- package/dist/material-library.d.ts +1 -1
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +1940 -143
- 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 +321 -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 +93 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +201 -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 +183 -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 +119 -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 +133 -0
- package/dist/registry/types.d.ts +1420 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +1 -0
- package/dist/schema/index.d.ts +17 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +17 -3
- package/dist/schema/material.d.ts +10 -0
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +10 -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/ceiling.d.ts +2 -0
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +1 -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 +597 -5
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +586 -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/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/elevator.d.ts +105 -0
- package/dist/schema/nodes/elevator.d.ts.map +1 -0
- package/dist/schema/nodes/elevator.js +54 -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 +1 -0
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +2 -0
- 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/item.d.ts +4 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +9 -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 +2 -0
- package/dist/schema/nodes/ridge-vent.d.ts +67 -0
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent.js +30 -0
- package/dist/schema/nodes/roof-segment.d.ts +195 -1
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +254 -3
- 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 +105 -0
- package/dist/schema/nodes/shelf.d.ts.map +1 -0
- package/dist/schema/nodes/shelf.js +84 -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 +2 -0
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +1 -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/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/types.d.ts +1325 -125
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +26 -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 +283 -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 +126 -0
- package/dist/services/alignment.d.ts.map +1 -0
- package/dist/services/alignment.js +169 -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 +80 -0
- package/dist/services/hosting.d.ts.map +1 -0
- package/dist/services/hosting.js +122 -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 +199 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +6 -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/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 +72 -0
- package/dist/services/snap.d.ts.map +1 -0
- package/dist/services/snap.js +120 -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 +95 -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 +492 -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/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-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.d.ts +11 -0
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +196 -6
- 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/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 +168 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +1777 -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 +172 -147
- package/dist/systems/stair/stair-opening-sync.test.js +385 -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-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 +6 -4
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +6 -4
- 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 +59 -0
- package/dist/validation/validate-build-json.d.ts +43 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -0
- package/dist/validation/validate-build-json.js +227 -0
- package/package.json +9 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stair-opening-preview.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7E,OAA6B,EAAE,KAAK,iBAAiB,EAAE,MAAM,qCAAqC,CAAA;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAGpE,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAA;CACtC,CAAA;AAoBD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,WAYvC;AAED,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBvC;AAED,wBAAgB,qCAAqC;;mBAUlC,oBAAoB,EAAE;;EAiCxC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import useLiveNodeOverrides, {} from '../../store/use-live-node-overrides';
|
|
2
|
+
import useScene from '../../store/use-scene';
|
|
3
|
+
const SURFACE_OPENING_FIELDS = ['holes', 'holeMetadata'];
|
|
4
|
+
function isSurface(node) {
|
|
5
|
+
return node?.type === 'slab' || node?.type === 'ceiling';
|
|
6
|
+
}
|
|
7
|
+
function isStairOpeningInputNode(node) {
|
|
8
|
+
return node?.type === 'stair' || node?.type === 'stair-segment';
|
|
9
|
+
}
|
|
10
|
+
function omitPreviewSurfaceFields(override) {
|
|
11
|
+
const next = { ...override };
|
|
12
|
+
for (const field of SURFACE_OPENING_FIELDS) {
|
|
13
|
+
delete next[field];
|
|
14
|
+
}
|
|
15
|
+
return next;
|
|
16
|
+
}
|
|
17
|
+
export function hasLiveStairOpeningInputs(nodes, liveTransforms, liveOverrides, previewSurfaceIds) {
|
|
18
|
+
for (const nodeId of liveTransforms.keys()) {
|
|
19
|
+
if (nodes[nodeId]?.type === 'stair')
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
for (const [nodeId, override] of liveOverrides) {
|
|
23
|
+
if (previewSurfaceIds.has(nodeId))
|
|
24
|
+
continue;
|
|
25
|
+
if (Object.keys(override).length > 0 && isStairOpeningInputNode(nodes[nodeId]))
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
export function getNodesWithLiveStairOpeningInputs(nodes, liveTransforms, liveOverrides, previewSurfaceIds) {
|
|
31
|
+
const nextNodes = { ...nodes };
|
|
32
|
+
for (const [nodeId, override] of liveOverrides) {
|
|
33
|
+
const node = nextNodes[nodeId];
|
|
34
|
+
if (!node)
|
|
35
|
+
continue;
|
|
36
|
+
const values = previewSurfaceIds.has(nodeId) ? omitPreviewSurfaceFields(override) : override;
|
|
37
|
+
if (Object.keys(values).length === 0)
|
|
38
|
+
continue;
|
|
39
|
+
nextNodes[nodeId] = { ...node, ...values };
|
|
40
|
+
}
|
|
41
|
+
for (const [nodeId, transform] of liveTransforms) {
|
|
42
|
+
const node = nextNodes[nodeId];
|
|
43
|
+
if (node?.type !== 'stair')
|
|
44
|
+
continue;
|
|
45
|
+
nextNodes[nodeId] = {
|
|
46
|
+
...node,
|
|
47
|
+
position: transform.position,
|
|
48
|
+
rotation: transform.rotation,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return nextNodes;
|
|
52
|
+
}
|
|
53
|
+
export function createSurfaceOpeningPreviewController() {
|
|
54
|
+
const previewSurfaceIds = new Set();
|
|
55
|
+
const clearSurface = (id) => {
|
|
56
|
+
useLiveNodeOverrides.getState().clearFields(id, SURFACE_OPENING_FIELDS);
|
|
57
|
+
useScene.getState().markDirty(id);
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
previewSurfaceIds,
|
|
61
|
+
apply(updates) {
|
|
62
|
+
const scene = useScene.getState();
|
|
63
|
+
const nextSurfaceIds = new Set();
|
|
64
|
+
for (const update of updates) {
|
|
65
|
+
const node = scene.nodes[update.id];
|
|
66
|
+
if (!isSurface(node))
|
|
67
|
+
continue;
|
|
68
|
+
if (!('holes' in update.data || 'holeMetadata' in update.data))
|
|
69
|
+
continue;
|
|
70
|
+
nextSurfaceIds.add(update.id);
|
|
71
|
+
useLiveNodeOverrides.getState().set(update.id, {
|
|
72
|
+
holes: update.data.holes ?? [],
|
|
73
|
+
holeMetadata: update.data.holeMetadata ?? [],
|
|
74
|
+
});
|
|
75
|
+
scene.markDirty(update.id);
|
|
76
|
+
}
|
|
77
|
+
for (const id of previewSurfaceIds) {
|
|
78
|
+
if (!nextSurfaceIds.has(id))
|
|
79
|
+
clearSurface(id);
|
|
80
|
+
}
|
|
81
|
+
previewSurfaceIds.clear();
|
|
82
|
+
for (const id of nextSurfaceIds) {
|
|
83
|
+
previewSurfaceIds.add(id);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
clear() {
|
|
87
|
+
for (const id of previewSurfaceIds) {
|
|
88
|
+
clearSurface(id);
|
|
89
|
+
}
|
|
90
|
+
previewSurfaceIds.clear();
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stair-opening-preview.test.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-preview.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { BuildingNode, LevelNode, SlabNode, StairNode, StairSegmentNode } from '../../schema';
|
|
3
|
+
import { getNodesWithLiveStairOpeningInputs, hasLiveStairOpeningInputs, } from './stair-opening-preview';
|
|
4
|
+
import { syncAutoStairOpenings } from './stair-opening-sync';
|
|
5
|
+
describe('stair opening previews', () => {
|
|
6
|
+
test('computes auto openings from live stair transforms', () => {
|
|
7
|
+
const building = BuildingNode.parse({ name: 'Building' });
|
|
8
|
+
const ground = LevelNode.parse({ name: 'Ground', level: 0, parentId: building.id });
|
|
9
|
+
const upper = LevelNode.parse({ name: 'Upper', level: 1, parentId: building.id });
|
|
10
|
+
const slab = SlabNode.parse({
|
|
11
|
+
name: 'Upper Slab',
|
|
12
|
+
parentId: upper.id,
|
|
13
|
+
polygon: [
|
|
14
|
+
[0, 0],
|
|
15
|
+
[5, 0],
|
|
16
|
+
[5, 4],
|
|
17
|
+
[0, 4],
|
|
18
|
+
],
|
|
19
|
+
});
|
|
20
|
+
const segment = StairSegmentNode.parse({
|
|
21
|
+
parentId: 'stair_live',
|
|
22
|
+
width: 1,
|
|
23
|
+
length: 3,
|
|
24
|
+
height: 2.5,
|
|
25
|
+
stepCount: 12,
|
|
26
|
+
});
|
|
27
|
+
const stair = StairNode.parse({
|
|
28
|
+
id: 'stair_live',
|
|
29
|
+
name: 'Live Stair',
|
|
30
|
+
parentId: ground.id,
|
|
31
|
+
position: [1, 0, 0.2],
|
|
32
|
+
stairType: 'straight',
|
|
33
|
+
fromLevelId: ground.id,
|
|
34
|
+
toLevelId: upper.id,
|
|
35
|
+
slabOpeningMode: 'destination',
|
|
36
|
+
children: [segment.id],
|
|
37
|
+
});
|
|
38
|
+
const nodes = Object.fromEntries([building, ground, upper, slab, stair, { ...segment, parentId: stair.id }].map((node) => [
|
|
39
|
+
node.id,
|
|
40
|
+
node,
|
|
41
|
+
]));
|
|
42
|
+
const liveTransforms = new Map([
|
|
43
|
+
[stair.id, { position: [3, 0, 0.2], rotation: 0 }],
|
|
44
|
+
]);
|
|
45
|
+
const liveOverrides = new Map();
|
|
46
|
+
expect(hasLiveStairOpeningInputs(nodes, liveTransforms, liveOverrides, new Set())).toBe(true);
|
|
47
|
+
const previewNodes = getNodesWithLiveStairOpeningInputs(nodes, liveTransforms, liveOverrides, new Set());
|
|
48
|
+
const updates = syncAutoStairOpenings(previewNodes);
|
|
49
|
+
const hole = updates.find((update) => update.id === slab.id)?.data.holes?.[0];
|
|
50
|
+
expect(hole).toBeDefined();
|
|
51
|
+
expect(Math.max(...hole.map(([x]) => x))).toBeGreaterThan(3.4);
|
|
52
|
+
});
|
|
53
|
+
test('ignores its own live surface overrides as preview inputs', () => {
|
|
54
|
+
const slab = SlabNode.parse({
|
|
55
|
+
polygon: [
|
|
56
|
+
[0, 0],
|
|
57
|
+
[4, 0],
|
|
58
|
+
[4, 4],
|
|
59
|
+
[0, 4],
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
const nodes = { [slab.id]: slab };
|
|
63
|
+
const liveOverrides = new Map([
|
|
64
|
+
[
|
|
65
|
+
slab.id,
|
|
66
|
+
{
|
|
67
|
+
holes: [
|
|
68
|
+
[
|
|
69
|
+
[1, 1],
|
|
70
|
+
[2, 1],
|
|
71
|
+
[2, 2],
|
|
72
|
+
[1, 2],
|
|
73
|
+
],
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
]);
|
|
78
|
+
expect(hasLiveStairOpeningInputs(nodes, new Map(), liveOverrides, new Set([slab.id]))).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stair-opening-sync.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stair-opening-sync.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,WAAW,EACX,QAAQ,EAIT,MAAM,cAAc,CAAA;AA8rBrB,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;QAQvC,SAAS;UAAQ,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC;IAqG5E"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync';
|
|
1
|
+
import { resolveBuildingForLevel, resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync';
|
|
2
|
+
import { polygonContainsPolygon, polygonsOverlap } from '../../lib/polygon-relations';
|
|
2
3
|
import { DEFAULT_WALL_HEIGHT } from '../wall/wall-footprint';
|
|
4
|
+
import { computeSegmentTransforms, rotateXZ } from './stair-footprint';
|
|
3
5
|
const CURVED_STAIR_SLAB_OPENING_RATIO = 0.8;
|
|
4
6
|
const STRAIGHT_STAIR_TARGET_THRESHOLD_MIN = 0.35;
|
|
5
7
|
const STAIR_SLAB_OPENING_TIGHTENING = 0;
|
|
@@ -30,99 +32,100 @@ function metadataEqual(left, right) {
|
|
|
30
32
|
if (left.length !== right.length)
|
|
31
33
|
return false;
|
|
32
34
|
return left.every((entry, index) => entry.source === right[index]?.source &&
|
|
35
|
+
(entry.elevatorId ?? null) === (right[index]?.elevatorId ?? null) &&
|
|
33
36
|
(entry.stairId ?? null) === (right[index]?.stairId ?? null));
|
|
34
37
|
}
|
|
35
38
|
function normalizeExistingMetadata(holes, metadata) {
|
|
36
39
|
return holes.map((_, index) => metadata?.[index] ?? { source: 'manual' });
|
|
37
40
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
const centroid = polygon.reduce((acc, [x, z]) => {
|
|
43
|
-
acc.x += x;
|
|
44
|
-
acc.z += z;
|
|
45
|
-
return acc;
|
|
46
|
-
}, { x: 0, z: 0 });
|
|
47
|
-
centroid.x /= Math.max(polygon.length, 1);
|
|
48
|
-
centroid.z /= Math.max(polygon.length, 1);
|
|
49
|
-
return polygon.map(([x, z]) => {
|
|
50
|
-
const dx = x - centroid.x;
|
|
51
|
-
const dz = z - centroid.z;
|
|
52
|
-
const length = Math.hypot(dx, dz);
|
|
53
|
-
if (length < 1e-6) {
|
|
54
|
-
return [x, z];
|
|
55
|
-
}
|
|
56
|
-
const scale = Math.max(0.1, (length + offset) / length);
|
|
57
|
-
return [centroid.x + dx * scale, centroid.z + dz * scale];
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
function rotateXZ(x, z, angle) {
|
|
61
|
-
const cos = Math.cos(angle);
|
|
62
|
-
const sin = Math.sin(angle);
|
|
63
|
-
return [x * cos + z * sin, -x * sin + z * cos];
|
|
64
|
-
}
|
|
65
|
-
function computeSegmentTransforms(segments) {
|
|
66
|
-
const transforms = [];
|
|
67
|
-
let currentX = 0;
|
|
68
|
-
let currentY = 0;
|
|
69
|
-
let currentZ = 0;
|
|
70
|
-
let currentRot = 0;
|
|
71
|
-
for (let index = 0; index < segments.length; index++) {
|
|
72
|
-
const segment = segments[index];
|
|
73
|
-
if (!segment)
|
|
74
|
-
continue;
|
|
75
|
-
if (index === 0) {
|
|
76
|
-
transforms.push({
|
|
77
|
-
position: [currentX, currentY, currentZ],
|
|
78
|
-
rotation: currentRot,
|
|
79
|
-
});
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
const previous = segments[index - 1];
|
|
83
|
-
if (!previous)
|
|
84
|
-
continue;
|
|
85
|
-
let attachX = 0;
|
|
86
|
-
let attachZ = 0;
|
|
87
|
-
let rotationDelta = 0;
|
|
88
|
-
switch (segment.attachmentSide) {
|
|
89
|
-
case 'front':
|
|
90
|
-
attachX = 0;
|
|
91
|
-
attachZ = previous.length;
|
|
92
|
-
break;
|
|
93
|
-
case 'left':
|
|
94
|
-
attachX = previous.width / 2;
|
|
95
|
-
attachZ = previous.length / 2;
|
|
96
|
-
rotationDelta = Math.PI / 2;
|
|
97
|
-
break;
|
|
98
|
-
case 'right':
|
|
99
|
-
attachX = -previous.width / 2;
|
|
100
|
-
attachZ = previous.length / 2;
|
|
101
|
-
rotationDelta = -Math.PI / 2;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
const [deltaX, deltaZ] = rotateXZ(attachX, attachZ, currentRot);
|
|
105
|
-
currentX += deltaX;
|
|
106
|
-
currentY += previous.height;
|
|
107
|
-
currentZ += deltaZ;
|
|
108
|
-
currentRot += rotationDelta;
|
|
109
|
-
transforms.push({
|
|
110
|
-
position: [currentX, currentY, currentZ],
|
|
111
|
-
rotation: currentRot,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
return transforms;
|
|
115
|
-
}
|
|
41
|
+
// (Removing expandPolygonRadially in favor of geometric expansion inside the polygon generators)
|
|
42
|
+
// `rotateXZ` + `computeSegmentTransforms` are shared with the alignment-anchor
|
|
43
|
+
// footprint via `./stair-footprint` so both derive the chain identically.
|
|
116
44
|
function getLevelNumber(levelId, nodes) {
|
|
117
45
|
if (!levelId)
|
|
118
46
|
return undefined;
|
|
119
47
|
const node = nodes[levelId];
|
|
120
48
|
return node?.type === 'level' ? node.level : undefined;
|
|
121
49
|
}
|
|
50
|
+
function getLevelBuildingId(levelId, nodes) {
|
|
51
|
+
if (!levelId)
|
|
52
|
+
return null;
|
|
53
|
+
return resolveBuildingForLevel(levelId, nodes);
|
|
54
|
+
}
|
|
55
|
+
function normalizeLevelId(levelId, nodes) {
|
|
56
|
+
if (!levelId)
|
|
57
|
+
return null;
|
|
58
|
+
return nodes[levelId]?.type === 'level' ? levelId : null;
|
|
59
|
+
}
|
|
60
|
+
function getBuildingLevels(buildingId, nodes) {
|
|
61
|
+
const building = buildingId ? nodes[buildingId] : null;
|
|
62
|
+
if (building?.type !== 'building')
|
|
63
|
+
return [];
|
|
64
|
+
const levels = new Map();
|
|
65
|
+
for (const childId of building.children ?? []) {
|
|
66
|
+
const child = nodes[childId];
|
|
67
|
+
if (child?.type === 'level')
|
|
68
|
+
levels.set(child.id, child);
|
|
69
|
+
}
|
|
70
|
+
for (const candidate of Object.values(nodes)) {
|
|
71
|
+
if (candidate?.type === 'level' && candidate.parentId === building.id) {
|
|
72
|
+
levels.set(candidate.id, candidate);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return Array.from(levels.values()).sort((left, right) => left.level - right.level);
|
|
76
|
+
}
|
|
77
|
+
function inferSourceLevelForDestination(destinationLevelId, nodes) {
|
|
78
|
+
if (!destinationLevelId)
|
|
79
|
+
return null;
|
|
80
|
+
const destination = nodes[destinationLevelId];
|
|
81
|
+
if (destination?.type !== 'level')
|
|
82
|
+
return null;
|
|
83
|
+
const buildingId = getLevelBuildingId(destinationLevelId, nodes);
|
|
84
|
+
return (getBuildingLevels(buildingId, nodes)
|
|
85
|
+
.filter((level) => level.level < destination.level)
|
|
86
|
+
.at(-1)?.id ?? null);
|
|
87
|
+
}
|
|
88
|
+
function inferDestinationLevelForSource(sourceLevelId, nodes) {
|
|
89
|
+
if (!sourceLevelId)
|
|
90
|
+
return null;
|
|
91
|
+
const source = nodes[sourceLevelId];
|
|
92
|
+
if (source?.type !== 'level')
|
|
93
|
+
return null;
|
|
94
|
+
const buildingId = getLevelBuildingId(sourceLevelId, nodes);
|
|
95
|
+
return (getBuildingLevels(buildingId, nodes).find((level) => level.level > source.level)?.id ?? null);
|
|
96
|
+
}
|
|
97
|
+
function levelsShareBuilding(leftLevelId, rightLevelId, nodes) {
|
|
98
|
+
if (!(leftLevelId && rightLevelId))
|
|
99
|
+
return true;
|
|
100
|
+
const leftBuildingId = getLevelBuildingId(leftLevelId, nodes);
|
|
101
|
+
const rightBuildingId = getLevelBuildingId(rightLevelId, nodes);
|
|
102
|
+
return !(leftBuildingId && rightBuildingId && leftBuildingId !== rightBuildingId);
|
|
103
|
+
}
|
|
104
|
+
function isInStairBuildingScope(stair, surfaceLevelId, nodes) {
|
|
105
|
+
const { fromLevelId, toLevelId } = getResolvedStairLevelIds(stair, nodes);
|
|
106
|
+
const fromBuildingId = getLevelBuildingId(fromLevelId, nodes);
|
|
107
|
+
const toBuildingId = getLevelBuildingId(toLevelId, nodes);
|
|
108
|
+
const surfaceBuildingId = getLevelBuildingId(surfaceLevelId, nodes);
|
|
109
|
+
if (fromBuildingId && toBuildingId && fromBuildingId !== toBuildingId)
|
|
110
|
+
return false;
|
|
111
|
+
if (fromBuildingId && surfaceBuildingId && fromBuildingId !== surfaceBuildingId)
|
|
112
|
+
return false;
|
|
113
|
+
if (toBuildingId && surfaceBuildingId && toBuildingId !== surfaceBuildingId)
|
|
114
|
+
return false;
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
122
117
|
function getResolvedStairLevelIds(stair, nodes) {
|
|
123
|
-
const parentLevelId = resolveLevelId(stair, nodes);
|
|
124
|
-
const
|
|
125
|
-
const
|
|
118
|
+
const parentLevelId = normalizeLevelId(resolveLevelId(stair, nodes), nodes);
|
|
119
|
+
const explicitToLevelId = normalizeLevelId(stair.toLevelId, nodes);
|
|
120
|
+
const fromLevelId = normalizeLevelId(stair.fromLevelId, nodes) ??
|
|
121
|
+
parentLevelId ??
|
|
122
|
+
inferSourceLevelForDestination(explicitToLevelId, nodes);
|
|
123
|
+
const explicitToLevelIsUsable = explicitToLevelId &&
|
|
124
|
+
explicitToLevelId !== fromLevelId &&
|
|
125
|
+
levelsShareBuilding(fromLevelId, explicitToLevelId, nodes);
|
|
126
|
+
const toLevelId = explicitToLevelIsUsable
|
|
127
|
+
? explicitToLevelId
|
|
128
|
+
: inferDestinationLevelForSource(fromLevelId, nodes);
|
|
126
129
|
return { fromLevelId, toLevelId };
|
|
127
130
|
}
|
|
128
131
|
function resolveStraightSegments(stair, nodes) {
|
|
@@ -179,7 +182,7 @@ function getStraightSegmentSlicePolygon(stair, layout, startAlong, endAlong) {
|
|
|
179
182
|
}
|
|
180
183
|
function getStraightFlightOpeningDepth(stair, segment) {
|
|
181
184
|
const treadDepth = Math.max(0.2, segment.length / Math.max(segment.stepCount || stair.stepCount || 10, 1));
|
|
182
|
-
return Math.min(segment.length, Math.max(treadDepth *
|
|
185
|
+
return Math.min(segment.length, Math.max(treadDepth * 10, segment.length * 0.8, 3.0));
|
|
183
186
|
}
|
|
184
187
|
function polygonArea(points) {
|
|
185
188
|
let area = 0;
|
|
@@ -192,34 +195,8 @@ function polygonArea(points) {
|
|
|
192
195
|
}
|
|
193
196
|
return area / 2;
|
|
194
197
|
}
|
|
195
|
-
function
|
|
196
|
-
|
|
197
|
-
if (Math.abs(cross) > tolerance)
|
|
198
|
-
return false;
|
|
199
|
-
const dot = (point[0] - a[0]) * (b[0] - a[0]) + (point[1] - a[1]) * (b[1] - a[1]);
|
|
200
|
-
if (dot < -tolerance)
|
|
201
|
-
return false;
|
|
202
|
-
const lenSq = (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2;
|
|
203
|
-
return dot <= lenSq + tolerance;
|
|
204
|
-
}
|
|
205
|
-
function pointInPolygon(point, polygon) {
|
|
206
|
-
if (polygon.length < 3)
|
|
207
|
-
return false;
|
|
208
|
-
let inside = false;
|
|
209
|
-
const [x, z] = point;
|
|
210
|
-
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
211
|
-
const a = polygon[i];
|
|
212
|
-
const b = polygon[j];
|
|
213
|
-
if (pointOnSegment(point, a, b))
|
|
214
|
-
return true;
|
|
215
|
-
const intersects = a[1] > z !== b[1] > z && x < ((b[0] - a[0]) * (z - a[1])) / (b[1] - a[1]) + a[0];
|
|
216
|
-
if (intersects)
|
|
217
|
-
inside = !inside;
|
|
218
|
-
}
|
|
219
|
-
return inside;
|
|
220
|
-
}
|
|
221
|
-
function polygonContainsPolygon(outer, inner) {
|
|
222
|
-
return inner.every((point) => pointInPolygon(point, outer));
|
|
198
|
+
function isCoveredByExistingHole(existingHoles, autoHole) {
|
|
199
|
+
return existingHoles.some((existingHole) => polygonContainsPolygon(existingHole, autoHole));
|
|
223
200
|
}
|
|
224
201
|
function getAxisAlignedRectFromPolygon(polygon) {
|
|
225
202
|
if (polygon.length < 4)
|
|
@@ -318,13 +295,15 @@ function buildUnionPolygonsFromRects(rects) {
|
|
|
318
295
|
}
|
|
319
296
|
return polygons;
|
|
320
297
|
}
|
|
321
|
-
function getCurvedOpeningPolygon(stair) {
|
|
298
|
+
function getCurvedOpeningPolygon(stair, offset = 0) {
|
|
322
299
|
const width = Math.max(stair.width ?? 1, 0.4);
|
|
323
|
-
const innerRadius = Math.max(0.
|
|
324
|
-
const outerRadius = innerRadius + width;
|
|
300
|
+
const innerRadius = Math.max(0.01, (stair.innerRadius ?? 0.9) - offset);
|
|
301
|
+
const outerRadius = (stair.innerRadius ?? 0.9) + width + offset;
|
|
325
302
|
const totalSweep = stair.sweepAngle ?? Math.PI / 2;
|
|
326
|
-
const
|
|
327
|
-
Math.max(
|
|
303
|
+
const baseOpeningSweep = Math.abs(totalSweep) *
|
|
304
|
+
Math.max(CURVED_STAIR_SLAB_OPENING_RATIO, 1 / Math.max(stair.stepCount ?? 1, 1));
|
|
305
|
+
const angleOffset = offset / Math.max(innerRadius, 0.1);
|
|
306
|
+
const openingSweep = Math.sign(totalSweep || 1) * Math.min(Math.abs(totalSweep), baseOpeningSweep + angleOffset * 2);
|
|
328
307
|
const startAngle = totalSweep / 2 - openingSweep;
|
|
329
308
|
const endAngle = totalSweep / 2;
|
|
330
309
|
const segmentCount = Math.max(10, Math.min(32, Math.ceil(Math.abs(openingSweep) / (Math.PI / 24) + Math.max(stair.stepCount ?? 1, 1) * 0.5)));
|
|
@@ -342,21 +321,34 @@ function getCurvedOpeningPolygon(stair) {
|
|
|
342
321
|
}
|
|
343
322
|
return [...outerPoints, ...innerPoints];
|
|
344
323
|
}
|
|
345
|
-
function getSpiralOpeningPolygon(stair) {
|
|
346
|
-
const radius = Math.max(0.05, stair.innerRadius ?? 0.9) + Math.max(stair.width ?? 1, 0.4);
|
|
324
|
+
function getSpiralOpeningPolygon(stair, offset = 0) {
|
|
325
|
+
const radius = Math.max(0.05, stair.innerRadius ?? 0.9) + Math.max(stair.width ?? 1, 0.4) + offset;
|
|
347
326
|
const segmentCount = 48;
|
|
348
327
|
return Array.from({ length: segmentCount }).map((_, index) => {
|
|
349
328
|
const angle = (index / segmentCount) * Math.PI * 2;
|
|
350
329
|
return toWorldPlanPoint(stair, Math.cos(angle) * radius, Math.sin(angle) * radius);
|
|
351
330
|
});
|
|
352
331
|
}
|
|
353
|
-
function
|
|
332
|
+
function getSpiralLandingPolygon(stair, offset = 0) {
|
|
333
|
+
const width = Math.max(stair.width ?? 1, 0.4);
|
|
334
|
+
const outerRadius = Math.max(0.05, (stair.innerRadius ?? 0.9) + width);
|
|
335
|
+
const depth = Math.max(stair.topLandingDepth ?? 0.9, 0.1);
|
|
336
|
+
const halfWidth = width / 2;
|
|
337
|
+
const localPoints = [
|
|
338
|
+
[outerRadius - offset, -halfWidth - offset],
|
|
339
|
+
[outerRadius + depth + offset, -halfWidth - offset],
|
|
340
|
+
[outerRadius + depth + offset, halfWidth + offset],
|
|
341
|
+
[outerRadius - offset, halfWidth + offset],
|
|
342
|
+
];
|
|
343
|
+
return localPoints.map(([x, z]) => toWorldPlanPoint(stair, x, z));
|
|
344
|
+
}
|
|
345
|
+
function getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation, openingOffsetOverride) {
|
|
354
346
|
const layouts = getStraightStairLayouts(stair, nodes);
|
|
355
347
|
if (layouts.length === 0)
|
|
356
348
|
return [];
|
|
357
349
|
const riserHeight = (stair.totalRise ?? 2.5) / Math.max(stair.stepCount ?? 10, 1);
|
|
358
350
|
const targetThreshold = Math.max(riserHeight * 2, STRAIGHT_STAIR_TARGET_THRESHOLD_MIN);
|
|
359
|
-
const openingOffset = Math.max(stair.openingOffset ?? 0, 0);
|
|
351
|
+
const openingOffset = Math.max(openingOffsetOverride ?? stair.openingOffset ?? 0, 0);
|
|
360
352
|
const openingRects = [];
|
|
361
353
|
for (let index = 0; index < layouts.length; index += 1) {
|
|
362
354
|
const layout = layouts[index];
|
|
@@ -409,20 +401,41 @@ function getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation) {
|
|
|
409
401
|
}
|
|
410
402
|
return fallbackLayout ? [getStraightSegmentFootprintPolygon(stair, fallbackLayout)] : [];
|
|
411
403
|
}
|
|
412
|
-
function getStairOpeningPolygons(stair, nodes, targetElevation) {
|
|
404
|
+
function getStairOpeningPolygons(stair, nodes, targetElevation, openingOffsetOverride) {
|
|
413
405
|
if ((stair.slabOpeningMode ?? 'none') !== 'destination') {
|
|
414
406
|
return [];
|
|
415
407
|
}
|
|
408
|
+
const openingOffset = Math.max(openingOffsetOverride ?? stair.openingOffset ?? 0, 0);
|
|
416
409
|
if (stair.stairType === 'curved') {
|
|
417
|
-
return [
|
|
410
|
+
return [
|
|
411
|
+
getCurvedOpeningPolygon(stair, Math.max(openingOffset - STAIR_SLAB_OPENING_TIGHTENING, 0)),
|
|
412
|
+
];
|
|
418
413
|
}
|
|
419
414
|
if (stair.stairType === 'spiral') {
|
|
420
|
-
|
|
415
|
+
const offset = Math.max(openingOffset - STAIR_SLAB_OPENING_TIGHTENING, 0);
|
|
416
|
+
const polygons = [getSpiralOpeningPolygon(stair, offset)];
|
|
417
|
+
if (stair.topLandingMode === 'integrated') {
|
|
418
|
+
polygons.push(getSpiralLandingPolygon(stair, offset));
|
|
419
|
+
}
|
|
420
|
+
return polygons;
|
|
421
421
|
}
|
|
422
422
|
if (typeof targetElevation === 'number') {
|
|
423
|
-
return getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation);
|
|
423
|
+
return getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation, openingOffset);
|
|
424
424
|
}
|
|
425
|
-
return getStraightOpeningPolygonsForSurface(stair, nodes, Math.max(...getStraightStairLayouts(stair, nodes).map((layout) => layout.topElevation), 0));
|
|
425
|
+
return getStraightOpeningPolygonsForSurface(stair, nodes, Math.max(...getStraightStairLayouts(stair, nodes).map((layout) => layout.topElevation), 0), openingOffset);
|
|
426
|
+
}
|
|
427
|
+
function getApplicableStairOpeningPolygons(stair, nodes, targetElevation, surfacePolygon) {
|
|
428
|
+
const configuredOffset = Math.max(stair.openingOffset ?? 0, 0);
|
|
429
|
+
const polygons = getStairOpeningPolygons(stair, nodes, targetElevation, configuredOffset);
|
|
430
|
+
const overlappingPolygons = polygons.filter((polygon) => polygonsOverlap(surfacePolygon, polygon));
|
|
431
|
+
if (overlappingPolygons.length === polygons.length || configuredOffset <= 1e-6) {
|
|
432
|
+
return overlappingPolygons;
|
|
433
|
+
}
|
|
434
|
+
const fallbackPolygons = getStairOpeningPolygons(stair, nodes, targetElevation, 0);
|
|
435
|
+
const overlappingFallbackPolygons = fallbackPolygons.filter((polygon) => polygonsOverlap(surfacePolygon, polygon));
|
|
436
|
+
return overlappingFallbackPolygons.length === fallbackPolygons.length
|
|
437
|
+
? overlappingFallbackPolygons
|
|
438
|
+
: overlappingPolygons;
|
|
426
439
|
}
|
|
427
440
|
function getTargetSlabElevationForStair(stair, slab, slabLevelId, nodes) {
|
|
428
441
|
const { fromLevelId } = getResolvedStairLevelIds(stair, nodes);
|
|
@@ -451,6 +464,8 @@ function shouldApplyStairToSlab(stair, slabLevelId, nodes) {
|
|
|
451
464
|
const fromLevel = getLevelNumber(fromLevelId, nodes);
|
|
452
465
|
const toLevel = getLevelNumber(toLevelId, nodes);
|
|
453
466
|
const slabLevel = getLevelNumber(slabLevelId, nodes);
|
|
467
|
+
if (!isInStairBuildingScope(stair, slabLevelId, nodes))
|
|
468
|
+
return false;
|
|
454
469
|
if (slabLevel === undefined) {
|
|
455
470
|
return toLevelId === slabLevelId;
|
|
456
471
|
}
|
|
@@ -466,6 +481,8 @@ function shouldApplyStairToCeiling(stair, ceilingLevelId, nodes) {
|
|
|
466
481
|
const fromLevel = getLevelNumber(fromLevelId, nodes);
|
|
467
482
|
const toLevel = getLevelNumber(toLevelId, nodes);
|
|
468
483
|
const ceilingLevel = getLevelNumber(ceilingLevelId, nodes);
|
|
484
|
+
if (!isInStairBuildingScope(stair, ceilingLevelId, nodes))
|
|
485
|
+
return false;
|
|
469
486
|
if (ceilingLevel === undefined) {
|
|
470
487
|
return fromLevelId === ceilingLevelId;
|
|
471
488
|
}
|
|
@@ -485,24 +502,28 @@ export function syncAutoStairOpenings(nodes) {
|
|
|
485
502
|
const slabLevelId = resolveLevelId(slab, nodes);
|
|
486
503
|
const existingHoles = slab.holes ?? [];
|
|
487
504
|
const existingMetadata = normalizeExistingMetadata(existingHoles, slab.holeMetadata);
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
.filter((entry) => entry.source !== 'stair')
|
|
491
|
-
|
|
505
|
+
const preservedHoles = existingHoles
|
|
506
|
+
.map((polygon, index) => ({ metadata: existingMetadata[index], polygon }))
|
|
507
|
+
.filter((entry) => entry.metadata.source !== 'stair');
|
|
508
|
+
const preservedHolePolygons = preservedHoles.map((entry) => entry.polygon);
|
|
492
509
|
const stairHoles = stairs
|
|
493
510
|
.filter((stair) => shouldApplyStairToSlab(stair, slabLevelId, nodes))
|
|
494
|
-
.flatMap((stair) =>
|
|
495
|
-
polygon
|
|
496
|
-
? polygon
|
|
497
|
-
: expandPolygonFromCentroid(polygon, Math.max((stair.openingOffset ?? 0) - STAIR_SLAB_OPENING_TIGHTENING, 0)),
|
|
511
|
+
.flatMap((stair) => getApplicableStairOpeningPolygons(stair, nodes, getTargetSlabElevationForStair(stair, slab, slabLevelId, nodes), slab.polygon).map((polygon) => ({
|
|
512
|
+
polygon,
|
|
498
513
|
metadata: {
|
|
499
514
|
source: 'stair',
|
|
500
515
|
stairId: stair.id,
|
|
501
516
|
},
|
|
502
517
|
})))
|
|
503
|
-
.filter((hole) =>
|
|
504
|
-
const nextHoles = [
|
|
505
|
-
|
|
518
|
+
.filter((hole) => !isCoveredByExistingHole(preservedHolePolygons, hole.polygon));
|
|
519
|
+
const nextHoles = [
|
|
520
|
+
...preservedHoles.map((hole) => hole.polygon),
|
|
521
|
+
...stairHoles.map((hole) => hole.polygon),
|
|
522
|
+
];
|
|
523
|
+
const nextMetadata = [
|
|
524
|
+
...preservedHoles.map((hole) => ({ ...hole.metadata })),
|
|
525
|
+
...stairHoles.map((hole) => hole.metadata),
|
|
526
|
+
];
|
|
506
527
|
if (!(polygonsEqual(existingHoles, nextHoles) && metadataEqual(existingMetadata, nextMetadata))) {
|
|
507
528
|
updates.push({
|
|
508
529
|
id: slab.id,
|
|
@@ -517,24 +538,28 @@ export function syncAutoStairOpenings(nodes) {
|
|
|
517
538
|
const ceilingLevelId = resolveLevelId(ceiling, nodes);
|
|
518
539
|
const existingHoles = ceiling.holes ?? [];
|
|
519
540
|
const existingMetadata = normalizeExistingMetadata(existingHoles, ceiling.holeMetadata);
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
.filter((entry) => entry.source !== 'stair')
|
|
523
|
-
|
|
541
|
+
const preservedHoles = existingHoles
|
|
542
|
+
.map((polygon, index) => ({ metadata: existingMetadata[index], polygon }))
|
|
543
|
+
.filter((entry) => entry.metadata.source !== 'stair');
|
|
544
|
+
const preservedHolePolygons = preservedHoles.map((entry) => entry.polygon);
|
|
524
545
|
const stairHoles = stairs
|
|
525
546
|
.filter((stair) => shouldApplyStairToCeiling(stair, ceilingLevelId, nodes))
|
|
526
|
-
.flatMap((stair) =>
|
|
527
|
-
polygon
|
|
528
|
-
? polygon
|
|
529
|
-
: expandPolygonFromCentroid(polygon, Math.max((stair.openingOffset ?? 0) - STAIR_SLAB_OPENING_TIGHTENING, 0)),
|
|
547
|
+
.flatMap((stair) => getApplicableStairOpeningPolygons(stair, nodes, getTargetCeilingElevationForStair(stair, ceiling, ceilingLevelId, nodes), ceiling.polygon).map((polygon) => ({
|
|
548
|
+
polygon,
|
|
530
549
|
metadata: {
|
|
531
550
|
source: 'stair',
|
|
532
551
|
stairId: stair.id,
|
|
533
552
|
},
|
|
534
553
|
})))
|
|
535
|
-
.filter((hole) =>
|
|
536
|
-
const nextHoles = [
|
|
537
|
-
|
|
554
|
+
.filter((hole) => !isCoveredByExistingHole(preservedHolePolygons, hole.polygon));
|
|
555
|
+
const nextHoles = [
|
|
556
|
+
...preservedHoles.map((hole) => hole.polygon),
|
|
557
|
+
...stairHoles.map((hole) => hole.polygon),
|
|
558
|
+
];
|
|
559
|
+
const nextMetadata = [
|
|
560
|
+
...preservedHoles.map((hole) => ({ ...hole.metadata })),
|
|
561
|
+
...stairHoles.map((hole) => hole.metadata),
|
|
562
|
+
];
|
|
538
563
|
if (!(polygonsEqual(existingHoles, nextHoles) && metadataEqual(existingMetadata, nextMetadata))) {
|
|
539
564
|
updates.push({
|
|
540
565
|
id: ceiling.id,
|