@pascal-app/core 0.7.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 +47 -18
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/bus.js +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-geometry.d.ts.map +1 -1
- 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 +222 -41
- 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 +1957 -141
- 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/asset-url.test.js +0 -4
- 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 +12 -2
- 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 +3 -1
- 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 +598 -6
- 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/door.d.ts +1 -1
- 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 +3 -2
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +4 -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/item.d.ts +16 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +21 -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 +198 -4
- 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 +6 -6
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +9 -21
- 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 -116
- 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 +3 -1
- 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/stair-segment.d.ts +1 -1
- package/dist/schema/nodes/stair.d.ts +6 -6
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +9 -7
- 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 +3 -3
- package/dist/schema/nodes/window.d.ts +1 -1
- package/dist/schema/types.d.ts +1448 -236
- 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 +499 -20
- 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 +207 -11
- 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 +183 -185
- package/dist/systems/stair/stair-opening-sync.test.js +385 -4
- 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-curve.d.ts +1 -1
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +1 -1
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +2 -6
- 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 +11 -3
- package/dist/materials.d.ts +0 -10
- package/dist/materials.d.ts.map +0 -1
- package/dist/materials.js +0 -22
- package/dist/systems/ceiling/ceiling-system.d.ts +0 -8
- package/dist/systems/ceiling/ceiling-system.d.ts.map +0 -1
- package/dist/systems/ceiling/ceiling-system.js +0 -92
- package/dist/systems/door/door-system.d.ts +0 -2
- package/dist/systems/door/door-system.d.ts.map +0 -1
- package/dist/systems/door/door-system.js +0 -195
- package/dist/systems/fence/fence-system.d.ts +0 -2
- package/dist/systems/fence/fence-system.d.ts.map +0 -1
- package/dist/systems/fence/fence-system.js +0 -187
- package/dist/systems/item/item-system.d.ts +0 -2
- package/dist/systems/item/item-system.d.ts.map +0 -1
- package/dist/systems/item/item-system.js +0 -48
- package/dist/systems/roof/roof-system.d.ts +0 -16
- package/dist/systems/roof/roof-system.d.ts.map +0 -1
- package/dist/systems/roof/roof-system.js +0 -797
- package/dist/systems/slab/slab-system.d.ts +0 -8
- package/dist/systems/slab/slab-system.d.ts.map +0 -1
- package/dist/systems/slab/slab-system.js +0 -214
- package/dist/systems/stair/stair-system.d.ts +0 -2
- package/dist/systems/stair/stair-system.d.ts.map +0 -1
- package/dist/systems/stair/stair-system.js +0 -776
- package/dist/systems/wall/wall-system.d.ts +0 -12
- package/dist/systems/wall/wall-system.d.ts.map +0 -1
- package/dist/systems/wall/wall-system.js +0 -455
- package/dist/systems/window/window-system.d.ts +0 -2
- package/dist/systems/window/window-system.d.ts.map +0 -1
- package/dist/systems/window/window-system.js +0 -131
|
@@ -1,9 +1,10 @@
|
|
|
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';
|
|
3
|
-
|
|
4
|
+
import { computeSegmentTransforms, rotateXZ } from './stair-footprint';
|
|
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;
|
|
6
|
-
const CURVED_STAIR_OPENING_STEP_PADDING = 3;
|
|
7
8
|
function clamp(value, min, max) {
|
|
8
9
|
return Math.min(max, Math.max(min, value));
|
|
9
10
|
}
|
|
@@ -31,99 +32,100 @@ function metadataEqual(left, right) {
|
|
|
31
32
|
if (left.length !== right.length)
|
|
32
33
|
return false;
|
|
33
34
|
return left.every((entry, index) => entry.source === right[index]?.source &&
|
|
35
|
+
(entry.elevatorId ?? null) === (right[index]?.elevatorId ?? null) &&
|
|
34
36
|
(entry.stairId ?? null) === (right[index]?.stairId ?? null));
|
|
35
37
|
}
|
|
36
38
|
function normalizeExistingMetadata(holes, metadata) {
|
|
37
39
|
return holes.map((_, index) => metadata?.[index] ?? { source: 'manual' });
|
|
38
40
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
const centroid = polygon.reduce((acc, [x, z]) => {
|
|
44
|
-
acc.x += x;
|
|
45
|
-
acc.z += z;
|
|
46
|
-
return acc;
|
|
47
|
-
}, { x: 0, z: 0 });
|
|
48
|
-
centroid.x /= Math.max(polygon.length, 1);
|
|
49
|
-
centroid.z /= Math.max(polygon.length, 1);
|
|
50
|
-
return polygon.map(([x, z]) => {
|
|
51
|
-
const dx = x - centroid.x;
|
|
52
|
-
const dz = z - centroid.z;
|
|
53
|
-
const length = Math.hypot(dx, dz);
|
|
54
|
-
if (length < 1e-6) {
|
|
55
|
-
return [x, z];
|
|
56
|
-
}
|
|
57
|
-
const scale = Math.max(0.1, (length + offset) / length);
|
|
58
|
-
return [centroid.x + dx * scale, centroid.z + dz * scale];
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
function rotateXZ(x, z, angle) {
|
|
62
|
-
const cos = Math.cos(angle);
|
|
63
|
-
const sin = Math.sin(angle);
|
|
64
|
-
return [x * cos + z * sin, -x * sin + z * cos];
|
|
65
|
-
}
|
|
66
|
-
function computeSegmentTransforms(segments) {
|
|
67
|
-
const transforms = [];
|
|
68
|
-
let currentX = 0;
|
|
69
|
-
let currentY = 0;
|
|
70
|
-
let currentZ = 0;
|
|
71
|
-
let currentRot = 0;
|
|
72
|
-
for (let index = 0; index < segments.length; index++) {
|
|
73
|
-
const segment = segments[index];
|
|
74
|
-
if (!segment)
|
|
75
|
-
continue;
|
|
76
|
-
if (index === 0) {
|
|
77
|
-
transforms.push({
|
|
78
|
-
position: [currentX, currentY, currentZ],
|
|
79
|
-
rotation: currentRot,
|
|
80
|
-
});
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
const previous = segments[index - 1];
|
|
84
|
-
if (!previous)
|
|
85
|
-
continue;
|
|
86
|
-
let attachX = 0;
|
|
87
|
-
let attachZ = 0;
|
|
88
|
-
let rotationDelta = 0;
|
|
89
|
-
switch (segment.attachmentSide) {
|
|
90
|
-
case 'front':
|
|
91
|
-
attachX = 0;
|
|
92
|
-
attachZ = previous.length;
|
|
93
|
-
break;
|
|
94
|
-
case 'left':
|
|
95
|
-
attachX = previous.width / 2;
|
|
96
|
-
attachZ = previous.length / 2;
|
|
97
|
-
rotationDelta = Math.PI / 2;
|
|
98
|
-
break;
|
|
99
|
-
case 'right':
|
|
100
|
-
attachX = -previous.width / 2;
|
|
101
|
-
attachZ = previous.length / 2;
|
|
102
|
-
rotationDelta = -Math.PI / 2;
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
const [deltaX, deltaZ] = rotateXZ(attachX, attachZ, currentRot);
|
|
106
|
-
currentX += deltaX;
|
|
107
|
-
currentY += previous.height;
|
|
108
|
-
currentZ += deltaZ;
|
|
109
|
-
currentRot += rotationDelta;
|
|
110
|
-
transforms.push({
|
|
111
|
-
position: [currentX, currentY, currentZ],
|
|
112
|
-
rotation: currentRot,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
return transforms;
|
|
116
|
-
}
|
|
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.
|
|
117
44
|
function getLevelNumber(levelId, nodes) {
|
|
118
45
|
if (!levelId)
|
|
119
46
|
return undefined;
|
|
120
47
|
const node = nodes[levelId];
|
|
121
48
|
return node?.type === 'level' ? node.level : undefined;
|
|
122
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
|
+
}
|
|
123
117
|
function getResolvedStairLevelIds(stair, nodes) {
|
|
124
|
-
const parentLevelId = resolveLevelId(stair, nodes);
|
|
125
|
-
const
|
|
126
|
-
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);
|
|
127
129
|
return { fromLevelId, toLevelId };
|
|
128
130
|
}
|
|
129
131
|
function resolveStraightSegments(stair, nodes) {
|
|
@@ -180,47 +182,21 @@ function getStraightSegmentSlicePolygon(stair, layout, startAlong, endAlong) {
|
|
|
180
182
|
}
|
|
181
183
|
function getStraightFlightOpeningDepth(stair, segment) {
|
|
182
184
|
const treadDepth = Math.max(0.2, segment.length / Math.max(segment.stepCount || stair.stepCount || 10, 1));
|
|
183
|
-
return Math.min(segment.length, Math.max(treadDepth *
|
|
185
|
+
return Math.min(segment.length, Math.max(treadDepth * 10, segment.length * 0.8, 3.0));
|
|
184
186
|
}
|
|
185
187
|
function polygonArea(points) {
|
|
186
188
|
let area = 0;
|
|
187
189
|
for (let index = 0; index < points.length; index += 1) {
|
|
188
190
|
const current = points[index];
|
|
189
191
|
const next = points[(index + 1) % points.length];
|
|
190
|
-
if (!current
|
|
192
|
+
if (!(current && next))
|
|
191
193
|
continue;
|
|
192
194
|
area += current[0] * next[1] - next[0] * current[1];
|
|
193
195
|
}
|
|
194
196
|
return area / 2;
|
|
195
197
|
}
|
|
196
|
-
function
|
|
197
|
-
|
|
198
|
-
if (Math.abs(cross) > tolerance)
|
|
199
|
-
return false;
|
|
200
|
-
const dot = (point[0] - a[0]) * (b[0] - a[0]) + (point[1] - a[1]) * (b[1] - a[1]);
|
|
201
|
-
if (dot < -tolerance)
|
|
202
|
-
return false;
|
|
203
|
-
const lenSq = (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2;
|
|
204
|
-
return dot <= lenSq + tolerance;
|
|
205
|
-
}
|
|
206
|
-
function pointInPolygon(point, polygon) {
|
|
207
|
-
if (polygon.length < 3)
|
|
208
|
-
return false;
|
|
209
|
-
let inside = false;
|
|
210
|
-
const [x, z] = point;
|
|
211
|
-
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
212
|
-
const a = polygon[i];
|
|
213
|
-
const b = polygon[j];
|
|
214
|
-
if (pointOnSegment(point, a, b))
|
|
215
|
-
return true;
|
|
216
|
-
const intersects = a[1] > z !== b[1] > z && x < ((b[0] - a[0]) * (z - a[1])) / (b[1] - a[1]) + a[0];
|
|
217
|
-
if (intersects)
|
|
218
|
-
inside = !inside;
|
|
219
|
-
}
|
|
220
|
-
return inside;
|
|
221
|
-
}
|
|
222
|
-
function polygonContainsPolygon(outer, inner) {
|
|
223
|
-
return inner.every((point) => pointInPolygon(point, outer));
|
|
198
|
+
function isCoveredByExistingHole(existingHoles, autoHole) {
|
|
199
|
+
return existingHoles.some((existingHole) => polygonContainsPolygon(existingHole, autoHole));
|
|
224
200
|
}
|
|
225
201
|
function getAxisAlignedRectFromPolygon(polygon) {
|
|
226
202
|
if (polygon.length < 4)
|
|
@@ -319,69 +295,60 @@ function buildUnionPolygonsFromRects(rects) {
|
|
|
319
295
|
}
|
|
320
296
|
return polygons;
|
|
321
297
|
}
|
|
322
|
-
function
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
const
|
|
298
|
+
function getCurvedOpeningPolygon(stair, offset = 0) {
|
|
299
|
+
const width = Math.max(stair.width ?? 1, 0.4);
|
|
300
|
+
const innerRadius = Math.max(0.01, (stair.innerRadius ?? 0.9) - offset);
|
|
301
|
+
const outerRadius = (stair.innerRadius ?? 0.9) + width + offset;
|
|
302
|
+
const totalSweep = stair.sweepAngle ?? Math.PI / 2;
|
|
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);
|
|
307
|
+
const startAngle = totalSweep / 2 - openingSweep;
|
|
308
|
+
const endAngle = totalSweep / 2;
|
|
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)));
|
|
332
310
|
const outerPoints = [];
|
|
333
311
|
const innerPoints = [];
|
|
334
312
|
for (let index = 0; index <= segmentCount; index++) {
|
|
335
313
|
const t = index / segmentCount;
|
|
336
|
-
const angle = startAngle +
|
|
314
|
+
const angle = startAngle + (endAngle - startAngle) * t;
|
|
337
315
|
outerPoints.push(toWorldPlanPoint(stair, Math.cos(angle) * outerRadius, Math.sin(angle) * outerRadius));
|
|
338
316
|
}
|
|
339
317
|
for (let index = segmentCount; index >= 0; index--) {
|
|
340
318
|
const t = index / segmentCount;
|
|
341
|
-
const angle = startAngle +
|
|
319
|
+
const angle = startAngle + (endAngle - startAngle) * t;
|
|
342
320
|
innerPoints.push(toWorldPlanPoint(stair, Math.cos(angle) * innerRadius, Math.sin(angle) * innerRadius));
|
|
343
321
|
}
|
|
344
322
|
return [...outerPoints, ...innerPoints];
|
|
345
323
|
}
|
|
346
|
-
function
|
|
347
|
-
const
|
|
348
|
-
const innerRadius = Math.max(0.2, stair.innerRadius ?? 0.9);
|
|
349
|
-
const outerRadius = innerRadius + width;
|
|
350
|
-
const totalSweep = stair.sweepAngle ?? Math.PI / 2;
|
|
351
|
-
const stepCount = Math.max(2, Math.round(stair.stepCount ?? 10));
|
|
352
|
-
const stepHeight = Math.max(stair.totalRise ?? 2.5, 0.1) / stepCount;
|
|
353
|
-
const stepSweep = totalSweep / stepCount;
|
|
354
|
-
const targetThreshold = Math.max(stepHeight * 2, STRAIGHT_STAIR_TARGET_THRESHOLD_MIN);
|
|
355
|
-
const endAngle = totalSweep / 2;
|
|
356
|
-
const fallbackStartStepIndex = Math.max(0, stepCount - getCurvedOpeningStepCount(stair, innerRadius, outerRadius, totalSweep));
|
|
357
|
-
let startStepIndex = fallbackStartStepIndex;
|
|
358
|
-
if (typeof targetElevation === 'number') {
|
|
359
|
-
for (let index = 0; index < stepCount; index += 1) {
|
|
360
|
-
const stepTopElevation = stepHeight * (index + 1);
|
|
361
|
-
if (stepTopElevation >= targetElevation - targetThreshold) {
|
|
362
|
-
startStepIndex = Math.max(0, Math.min(fallbackStartStepIndex, index - CURVED_STAIR_OPENING_STEP_PADDING));
|
|
363
|
-
break;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
const startAngle = -totalSweep / 2 + stepSweep * startStepIndex;
|
|
368
|
-
return buildArcOpeningPolygon(stair, innerRadius, outerRadius, startAngle, endAngle);
|
|
369
|
-
}
|
|
370
|
-
function getSpiralOpeningPolygon(stair) {
|
|
371
|
-
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;
|
|
372
326
|
const segmentCount = 48;
|
|
373
327
|
return Array.from({ length: segmentCount }).map((_, index) => {
|
|
374
328
|
const angle = (index / segmentCount) * Math.PI * 2;
|
|
375
329
|
return toWorldPlanPoint(stair, Math.cos(angle) * radius, Math.sin(angle) * radius);
|
|
376
330
|
});
|
|
377
331
|
}
|
|
378
|
-
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) {
|
|
379
346
|
const layouts = getStraightStairLayouts(stair, nodes);
|
|
380
347
|
if (layouts.length === 0)
|
|
381
348
|
return [];
|
|
382
349
|
const riserHeight = (stair.totalRise ?? 2.5) / Math.max(stair.stepCount ?? 10, 1);
|
|
383
350
|
const targetThreshold = Math.max(riserHeight * 2, STRAIGHT_STAIR_TARGET_THRESHOLD_MIN);
|
|
384
|
-
const openingOffset = Math.max(stair.openingOffset ?? 0, 0);
|
|
351
|
+
const openingOffset = Math.max(openingOffsetOverride ?? stair.openingOffset ?? 0, 0);
|
|
385
352
|
const openingRects = [];
|
|
386
353
|
for (let index = 0; index < layouts.length; index += 1) {
|
|
387
354
|
const layout = layouts[index];
|
|
@@ -434,20 +401,41 @@ function getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation) {
|
|
|
434
401
|
}
|
|
435
402
|
return fallbackLayout ? [getStraightSegmentFootprintPolygon(stair, fallbackLayout)] : [];
|
|
436
403
|
}
|
|
437
|
-
function getStairOpeningPolygons(stair, nodes, targetElevation) {
|
|
404
|
+
function getStairOpeningPolygons(stair, nodes, targetElevation, openingOffsetOverride) {
|
|
438
405
|
if ((stair.slabOpeningMode ?? 'none') !== 'destination') {
|
|
439
406
|
return [];
|
|
440
407
|
}
|
|
408
|
+
const openingOffset = Math.max(openingOffsetOverride ?? stair.openingOffset ?? 0, 0);
|
|
441
409
|
if (stair.stairType === 'curved') {
|
|
442
|
-
return [
|
|
410
|
+
return [
|
|
411
|
+
getCurvedOpeningPolygon(stair, Math.max(openingOffset - STAIR_SLAB_OPENING_TIGHTENING, 0)),
|
|
412
|
+
];
|
|
443
413
|
}
|
|
444
414
|
if (stair.stairType === 'spiral') {
|
|
445
|
-
|
|
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;
|
|
446
421
|
}
|
|
447
422
|
if (typeof targetElevation === 'number') {
|
|
448
|
-
return getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation);
|
|
423
|
+
return getStraightOpeningPolygonsForSurface(stair, nodes, targetElevation, openingOffset);
|
|
424
|
+
}
|
|
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;
|
|
449
433
|
}
|
|
450
|
-
|
|
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;
|
|
451
439
|
}
|
|
452
440
|
function getTargetSlabElevationForStair(stair, slab, slabLevelId, nodes) {
|
|
453
441
|
const { fromLevelId } = getResolvedStairLevelIds(stair, nodes);
|
|
@@ -476,6 +464,8 @@ function shouldApplyStairToSlab(stair, slabLevelId, nodes) {
|
|
|
476
464
|
const fromLevel = getLevelNumber(fromLevelId, nodes);
|
|
477
465
|
const toLevel = getLevelNumber(toLevelId, nodes);
|
|
478
466
|
const slabLevel = getLevelNumber(slabLevelId, nodes);
|
|
467
|
+
if (!isInStairBuildingScope(stair, slabLevelId, nodes))
|
|
468
|
+
return false;
|
|
479
469
|
if (slabLevel === undefined) {
|
|
480
470
|
return toLevelId === slabLevelId;
|
|
481
471
|
}
|
|
@@ -491,6 +481,8 @@ function shouldApplyStairToCeiling(stair, ceilingLevelId, nodes) {
|
|
|
491
481
|
const fromLevel = getLevelNumber(fromLevelId, nodes);
|
|
492
482
|
const toLevel = getLevelNumber(toLevelId, nodes);
|
|
493
483
|
const ceilingLevel = getLevelNumber(ceilingLevelId, nodes);
|
|
484
|
+
if (!isInStairBuildingScope(stair, ceilingLevelId, nodes))
|
|
485
|
+
return false;
|
|
494
486
|
if (ceilingLevel === undefined) {
|
|
495
487
|
return fromLevelId === ceilingLevelId;
|
|
496
488
|
}
|
|
@@ -510,26 +502,29 @@ export function syncAutoStairOpenings(nodes) {
|
|
|
510
502
|
const slabLevelId = resolveLevelId(slab, nodes);
|
|
511
503
|
const existingHoles = slab.holes ?? [];
|
|
512
504
|
const existingMetadata = normalizeExistingMetadata(existingHoles, slab.holeMetadata);
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
.filter((entry) => entry.source !== 'stair')
|
|
516
|
-
|
|
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);
|
|
517
509
|
const stairHoles = stairs
|
|
518
510
|
.filter((stair) => shouldApplyStairToSlab(stair, slabLevelId, nodes))
|
|
519
|
-
.flatMap((stair) =>
|
|
520
|
-
polygon
|
|
521
|
-
? polygon
|
|
522
|
-
: 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,
|
|
523
513
|
metadata: {
|
|
524
514
|
source: 'stair',
|
|
525
515
|
stairId: stair.id,
|
|
526
516
|
},
|
|
527
517
|
})))
|
|
528
|
-
.filter((hole) =>
|
|
529
|
-
const nextHoles = [
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
+
];
|
|
527
|
+
if (!(polygonsEqual(existingHoles, nextHoles) && metadataEqual(existingMetadata, nextMetadata))) {
|
|
533
528
|
updates.push({
|
|
534
529
|
id: slab.id,
|
|
535
530
|
data: {
|
|
@@ -543,26 +538,29 @@ export function syncAutoStairOpenings(nodes) {
|
|
|
543
538
|
const ceilingLevelId = resolveLevelId(ceiling, nodes);
|
|
544
539
|
const existingHoles = ceiling.holes ?? [];
|
|
545
540
|
const existingMetadata = normalizeExistingMetadata(existingHoles, ceiling.holeMetadata);
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
.filter((entry) => entry.source !== 'stair')
|
|
549
|
-
|
|
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);
|
|
550
545
|
const stairHoles = stairs
|
|
551
546
|
.filter((stair) => shouldApplyStairToCeiling(stair, ceilingLevelId, nodes))
|
|
552
|
-
.flatMap((stair) =>
|
|
553
|
-
polygon
|
|
554
|
-
? polygon
|
|
555
|
-
: 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,
|
|
556
549
|
metadata: {
|
|
557
550
|
source: 'stair',
|
|
558
551
|
stairId: stair.id,
|
|
559
552
|
},
|
|
560
553
|
})))
|
|
561
|
-
.filter((hole) =>
|
|
562
|
-
const nextHoles = [
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
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
|
+
];
|
|
563
|
+
if (!(polygonsEqual(existingHoles, nextHoles) && metadataEqual(existingMetadata, nextMetadata))) {
|
|
566
564
|
updates.push({
|
|
567
565
|
id: ceiling.id,
|
|
568
566
|
data: {
|