@pascal-app/core 1.0.0-beta.4 → 1.0.0
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/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/capture/index.d.ts +3 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +2 -0
- package/dist/capture/schema.d.ts +417 -0
- package/dist/capture/schema.d.ts.map +1 -0
- package/dist/capture/schema.js +348 -0
- package/dist/capture/source.d.ts +60 -0
- package/dist/capture/source.d.ts.map +1 -0
- package/dist/capture/source.js +254 -0
- package/dist/events/bus.d.ts +55 -7
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/hidden-wall-pointer-hold.d.ts +28 -0
- package/dist/events/hidden-wall-pointer-hold.d.ts.map +1 -0
- package/dist/events/hidden-wall-pointer-hold.js +37 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +12 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +39 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +2785 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +132 -19
- package/dist/hooks/spatial-grid/support-host-patch.d.ts +45 -1
- package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/support-host-patch.js +171 -6
- package/dist/index.d.ts +15 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -5
- package/dist/lib/asset-storage.d.ts.map +1 -1
- package/dist/lib/asset-storage.js +4 -1
- package/dist/lib/polygon-union.d.ts +4 -0
- package/dist/lib/polygon-union.d.ts.map +1 -0
- package/dist/lib/polygon-union.js +306 -0
- package/dist/lib/roof-overlap.d.ts +32 -0
- package/dist/lib/roof-overlap.d.ts.map +1 -0
- package/dist/lib/roof-overlap.js +72 -0
- package/dist/lib/room-topology-index.d.ts +50 -0
- package/dist/lib/room-topology-index.d.ts.map +1 -0
- package/dist/lib/room-topology-index.js +237 -0
- package/dist/lib/slab-polygon.d.ts +35 -2
- package/dist/lib/slab-polygon.d.ts.map +1 -1
- package/dist/lib/slab-polygon.js +133 -27
- package/dist/lib/space-detection.d.ts +40 -3
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +890 -246
- package/dist/registry/handles.d.ts +24 -3
- package/dist/registry/handles.d.ts.map +1 -1
- package/dist/registry/index.d.ts +3 -3
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +1 -1
- package/dist/registry/registry.d.ts +29 -6
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +111 -4
- package/dist/registry/scene-api.d.ts +16 -0
- package/dist/registry/scene-api.d.ts.map +1 -1
- package/dist/registry/scene-api.js +49 -1
- package/dist/registry/types.d.ts +261 -13
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry/use-registry-version.d.ts +11 -0
- package/dist/registry/use-registry-version.d.ts.map +1 -0
- package/dist/registry/use-registry-version.js +15 -0
- package/dist/schema/__bench__/node-parsers.bench.d.ts +24 -0
- package/dist/schema/__bench__/node-parsers.bench.d.ts.map +1 -0
- package/dist/schema/__bench__/node-parsers.bench.js +314 -0
- package/dist/schema/__fixtures__/node-fixtures.d.ts +28 -0
- package/dist/schema/__fixtures__/node-fixtures.d.ts.map +1 -0
- package/dist/schema/__fixtures__/node-fixtures.js +125 -0
- package/dist/schema/base.d.ts +1 -1
- package/dist/schema/base.d.ts.map +1 -1
- package/dist/schema/base.js +8 -1
- package/dist/schema/compiled-node-parsers.d.ts +40 -0
- package/dist/schema/compiled-node-parsers.d.ts.map +1 -0
- package/dist/schema/compiled-node-parsers.js +95 -0
- package/dist/schema/index.d.ts +20 -15
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +19 -12
- package/dist/schema/material.d.ts +2 -1
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +1 -0
- package/dist/schema/nodes/automatic-downspout.d.ts +17 -0
- package/dist/schema/nodes/automatic-downspout.d.ts.map +1 -0
- package/dist/schema/nodes/automatic-downspout.js +237 -0
- package/dist/schema/nodes/block.d.ts +107 -0
- package/dist/schema/nodes/block.d.ts.map +1 -0
- package/dist/schema/nodes/block.js +207 -0
- package/dist/schema/nodes/box-vent.d.ts +8 -2
- package/dist/schema/nodes/box-vent.d.ts.map +1 -1
- package/dist/schema/nodes/box-vent.js +2 -0
- package/dist/schema/nodes/building.d.ts +1 -1
- package/dist/schema/nodes/cabinet.d.ts +25 -3
- package/dist/schema/nodes/cabinet.d.ts.map +1 -1
- package/dist/schema/nodes/cabinet.js +26 -5
- package/dist/schema/nodes/ceiling.d.ts +1 -1
- package/dist/schema/nodes/chimney.d.ts +2 -2
- package/dist/schema/nodes/column.d.ts +6 -6
- package/dist/schema/nodes/construction-dimension.d.ts +4 -4
- package/dist/schema/nodes/cupola.d.ts +9 -1
- package/dist/schema/nodes/cupola.d.ts.map +1 -1
- package/dist/schema/nodes/cupola.js +2 -0
- package/dist/schema/nodes/door.d.ts +1 -1
- package/dist/schema/nodes/dormer.d.ts +44 -1
- package/dist/schema/nodes/dormer.d.ts.map +1 -1
- package/dist/schema/nodes/dormer.js +131 -7
- package/dist/schema/nodes/downspout.d.ts +11 -1
- package/dist/schema/nodes/downspout.d.ts.map +1 -1
- package/dist/schema/nodes/downspout.js +24 -0
- package/dist/schema/nodes/duct-fitting.d.ts +18 -1
- package/dist/schema/nodes/duct-fitting.d.ts.map +1 -1
- package/dist/schema/nodes/duct-fitting.js +25 -4
- package/dist/schema/nodes/duct-segment.d.ts +23 -1
- package/dist/schema/nodes/duct-segment.d.ts.map +1 -1
- package/dist/schema/nodes/duct-segment.js +20 -0
- package/dist/schema/nodes/duct-terminal.d.ts +1 -1
- package/dist/schema/nodes/elevator.d.ts +1 -1
- package/dist/schema/nodes/eyebrow-vent.d.ts +8 -2
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -1
- package/dist/schema/nodes/eyebrow-vent.js +2 -0
- package/dist/schema/nodes/fence.d.ts +1 -1
- package/dist/schema/nodes/guide.d.ts +1 -1
- package/dist/schema/nodes/gutter.d.ts +36 -2
- package/dist/schema/nodes/gutter.d.ts.map +1 -1
- package/dist/schema/nodes/gutter.js +308 -1
- package/dist/schema/nodes/hanger-overrides.d.ts +7 -0
- package/dist/schema/nodes/hanger-overrides.d.ts.map +1 -0
- package/dist/schema/nodes/hanger-overrides.js +6 -0
- package/dist/schema/nodes/hvac-equipment.d.ts +1 -1
- package/dist/schema/nodes/item.d.ts +4 -3
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +5 -0
- package/dist/schema/nodes/lean-to-extension.d.ts +212 -0
- package/dist/schema/nodes/lean-to-extension.d.ts.map +1 -0
- package/dist/schema/nodes/lean-to-extension.js +114 -0
- package/dist/schema/nodes/level.d.ts +5 -2
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +9 -0
- package/dist/schema/nodes/lineset.d.ts +1 -1
- package/dist/schema/nodes/liquid-line.d.ts +1 -1
- package/dist/schema/nodes/measurement.d.ts +1 -1
- package/dist/schema/nodes/pipe-fitting.d.ts +9 -1
- package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -1
- package/dist/schema/nodes/pipe-fitting.js +6 -2
- package/dist/schema/nodes/pipe-segment.d.ts +23 -1
- package/dist/schema/nodes/pipe-segment.d.ts.map +1 -1
- package/dist/schema/nodes/pipe-segment.js +20 -0
- package/dist/schema/nodes/pipe-trap.d.ts +1 -1
- package/dist/schema/nodes/ridge-vent.d.ts +1 -1
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -1
- package/dist/schema/nodes/ridge-vent.js +4 -1
- package/dist/schema/nodes/roof-segment-shape.d.ts +4 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-shape.js +64 -1
- package/dist/schema/nodes/roof-segment-walls.d.ts +3 -1
- package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-walls.js +1 -1
- package/dist/schema/nodes/roof-segment.d.ts +41 -1
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +79 -2
- package/dist/schema/nodes/roof.d.ts +22 -1
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +14 -0
- package/dist/schema/nodes/scan.d.ts +19 -2
- package/dist/schema/nodes/scan.d.ts.map +1 -1
- package/dist/schema/nodes/scan.js +11 -1
- package/dist/schema/nodes/shelf.d.ts +2 -2
- package/dist/schema/nodes/site.d.ts +1 -1
- package/dist/schema/nodes/skylight.d.ts +1 -1
- package/dist/schema/nodes/slab.d.ts +1 -1
- package/dist/schema/nodes/solar-panel.d.ts +1 -1
- package/dist/schema/nodes/spawn.d.ts +1 -1
- package/dist/schema/nodes/stair-segment.d.ts +1 -1
- package/dist/schema/nodes/stair.d.ts +1 -1
- package/dist/schema/nodes/structural-grid.d.ts +1 -1
- package/dist/schema/nodes/turbine-vent.d.ts +7 -1
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -1
- package/dist/schema/nodes/turbine-vent.js +2 -0
- package/dist/schema/nodes/wall.d.ts +4 -4
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +7 -1
- package/dist/schema/nodes/window.d.ts +10 -3
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +4 -0
- package/dist/schema/nodes/zone.d.ts +2 -2
- package/dist/schema/nodes/zone.js +1 -1
- package/dist/schema/types.d.ts +485 -109
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +27 -1
- package/dist/services/hosting.d.ts +2 -2
- package/dist/services/hosting.d.ts.map +1 -1
- package/dist/services/hosting.js +4 -4
- package/dist/services/index.d.ts +2 -2
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +2 -2
- package/dist/services/storey.d.ts +3 -10
- package/dist/services/storey.d.ts.map +1 -1
- package/dist/services/storey.js +45 -15
- package/dist/services/system-graph.d.ts +11 -0
- package/dist/services/system-graph.d.ts.map +1 -1
- package/dist/services/system-graph.js +35 -5
- package/dist/store/actions/node-actions.d.ts +15 -4
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +458 -19
- package/dist/store/history-control.d.ts +5 -0
- package/dist/store/history-control.d.ts.map +1 -1
- package/dist/store/history-control.js +68 -6
- package/dist/store/history-invalidation.d.ts +3 -0
- package/dist/store/history-invalidation.d.ts.map +1 -0
- package/dist/store/history-invalidation.js +72 -0
- package/dist/store/scene-hydration.d.ts +5 -0
- package/dist/store/scene-hydration.d.ts.map +1 -0
- package/dist/store/scene-hydration.js +57 -0
- package/dist/store/use-scene.d.ts +3 -0
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +407 -227
- package/dist/systems/elevator/elevator-service.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-service.js +16 -11
- package/dist/systems/roof/roof-elevation-system.d.ts +3 -0
- package/dist/systems/roof/roof-elevation-system.d.ts.map +1 -0
- package/dist/systems/roof/roof-elevation-system.js +94 -0
- package/dist/systems/roof/roof-elevation.d.ts +4 -0
- package/dist/systems/roof/roof-elevation.d.ts.map +1 -0
- package/dist/systems/roof/roof-elevation.js +95 -0
- package/dist/systems/roof/roof-footprint.d.ts +17 -0
- package/dist/systems/roof/roof-footprint.d.ts.map +1 -0
- package/dist/systems/roof/roof-footprint.js +83 -0
- package/dist/systems/slab/slab-support.d.ts +0 -31
- package/dist/systems/slab/slab-support.d.ts.map +1 -1
- package/dist/systems/slab/slab-support.js +26 -4
- package/dist/systems/stair/stair-flight.d.ts +17 -0
- package/dist/systems/stair/stair-flight.d.ts.map +1 -0
- package/dist/systems/stair/stair-flight.js +27 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +1556 -1328
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-system.d.ts +1 -0
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-system.js +93 -96
- package/dist/systems/stair/stair-rise.js +21 -20
- package/dist/systems/wall/wall-curve.d.ts +1 -0
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +73 -0
- package/dist/systems/wall/wall-footprint.d.ts +1 -0
- package/dist/systems/wall/wall-footprint.d.ts.map +1 -1
- package/dist/systems/wall/wall-footprint.js +1 -0
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +88 -9
- package/dist/systems/wall/wall-topology.d.ts +53 -0
- package/dist/systems/wall/wall-topology.d.ts.map +1 -0
- package/dist/systems/wall/wall-topology.js +414 -0
- package/dist/utils/clone-scene-graph.d.ts +2 -0
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +39 -2
- package/dist/utils/scene-migrations.d.ts +1 -0
- package/dist/utils/scene-migrations.d.ts.map +1 -1
- package/dist/utils/scene-migrations.js +1 -0
- package/dist/utils/vertical-scene-migration.d.ts +13 -0
- package/dist/utils/vertical-scene-migration.d.ts.map +1 -0
- package/dist/utils/vertical-scene-migration.js +179 -0
- package/dist/validation/validate-build-json.d.ts +3 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -1
- package/dist/validation/validate-build-json.js +49 -2
- package/package.json +10 -3
- package/dist/systems/elevator/elevator-runtime-system.d.ts +0 -2
- package/dist/systems/elevator/elevator-runtime-system.d.ts.map +0 -1
- package/dist/systems/elevator/elevator-runtime-system.js +0 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elevator-service.d.ts","sourceRoot":"","sources":["../../../src/systems/elevator/elevator-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAa,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAG/E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AA2BD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,SAAS,EAAE,CAEb;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,EAAE,CAEV;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,SAAS,EAAE,CAsBb;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B;IACD,OAAO,EAAE,kBAAkB,EAAE,CAAA;IAC7B,YAAY,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB,
|
|
1
|
+
{"version":3,"file":"elevator-service.d.ts","sourceRoot":"","sources":["../../../src/systems/elevator/elevator-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAa,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAG/E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AA2BD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,SAAS,EAAE,CAEb;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,EAAE,CAEV;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,SAAS,EAAE,CAsBb;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B;IACD,OAAO,EAAE,kBAAkB,EAAE,CAAA;IAC7B,YAAY,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB,CA6CA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getLevelElevations } from '../../services/storey';
|
|
2
2
|
function getBuildingLevels(elevator, nodes) {
|
|
3
3
|
const building = elevator.parentId && nodes[elevator.parentId]?.type === 'building'
|
|
4
4
|
? nodes[elevator.parentId]
|
|
@@ -51,17 +51,12 @@ export function resolveElevatorServiceLevels(elevator, nodes) {
|
|
|
51
51
|
}
|
|
52
52
|
export function resolveElevatorLevels(elevator, nodes) {
|
|
53
53
|
const allLevels = resolveElevatorBuildingLevels(elevator, nodes);
|
|
54
|
-
const
|
|
55
|
-
let cumulativeY = 0;
|
|
56
|
-
for (const level of allLevels) {
|
|
57
|
-
baseYByLevelId.set(level.id, cumulativeY);
|
|
58
|
-
cumulativeY += getStoredLevelHeight(level);
|
|
59
|
-
}
|
|
54
|
+
const levelElevations = getLevelElevations(nodes);
|
|
60
55
|
const serviceLevels = resolveElevatorServiceLevels(elevator, nodes);
|
|
61
56
|
const entries = serviceLevels.map((level) => ({
|
|
62
57
|
id: level.id,
|
|
63
58
|
label: String(level.level),
|
|
64
|
-
baseY:
|
|
59
|
+
baseY: levelElevations.get(level.id)?.baseY ?? 0,
|
|
65
60
|
}));
|
|
66
61
|
const defaultEntry = entries.find((entry) => entry.id === elevator.defaultLevelId) ??
|
|
67
62
|
entries.find((entry) => entry.id === elevator.fromLevelId) ??
|
|
@@ -69,15 +64,25 @@ export function resolveElevatorLevels(elevator, nodes) {
|
|
|
69
64
|
null;
|
|
70
65
|
const firstServedLevel = serviceLevels[0] ?? null;
|
|
71
66
|
const lastServedLevel = serviceLevels[serviceLevels.length - 1] ?? null;
|
|
72
|
-
const shaftBaseY = firstServedLevel ? (
|
|
67
|
+
const shaftBaseY = firstServedLevel ? (levelElevations.get(firstServedLevel.id)?.baseY ?? 0) : 0;
|
|
73
68
|
const lastServedIndex = lastServedLevel
|
|
74
69
|
? allLevels.findIndex((level) => level.id === lastServedLevel.id)
|
|
75
70
|
: -1;
|
|
76
71
|
const nextLevel = lastServedIndex >= 0 ? allLevels[lastServedIndex + 1] : null;
|
|
72
|
+
// Highest ceiling in the stack, not the topmost level's: a negative
|
|
73
|
+
// baseElevation on the top level can put its ceiling below the level
|
|
74
|
+
// beneath it, and a shaft top under a served level would clip the cab.
|
|
75
|
+
let stackTopY = 0;
|
|
76
|
+
for (const level of allLevels) {
|
|
77
|
+
const elevation = levelElevations.get(level.id);
|
|
78
|
+
if (!elevation)
|
|
79
|
+
continue;
|
|
80
|
+
stackTopY = Math.max(stackTopY, elevation.baseY + elevation.height);
|
|
81
|
+
}
|
|
77
82
|
const shaftTopY = nextLevel
|
|
78
|
-
? (
|
|
83
|
+
? (levelElevations.get(nextLevel.id)?.baseY ?? stackTopY)
|
|
79
84
|
: lastServedLevel
|
|
80
|
-
?
|
|
85
|
+
? stackTopY
|
|
81
86
|
: elevator.cabHeight + 0.3;
|
|
82
87
|
return {
|
|
83
88
|
entries,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roof-elevation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-elevation-system.tsx"],"names":[],"mappings":"AAgCA,wBAAgB,2BAA2B,IAAI,MAAM,IAAI,CAmExD;AAED,wBAAgB,mBAAmB,SAGlC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { getSceneHistoryPauseDepth, notifySceneCommit, pauseSceneHistory, resumeSceneHistory, } from '../../store/history-control';
|
|
4
|
+
import useScene from '../../store/use-scene';
|
|
5
|
+
import { resolveRoofElevation } from './roof-elevation';
|
|
6
|
+
const ROOF_ELEVATION_EPSILON = 1e-4;
|
|
7
|
+
function sceneSnapshot() {
|
|
8
|
+
const { nodes, rootNodeIds, collections, materials, installedPlugins } = useScene.getState();
|
|
9
|
+
return { nodes, rootNodeIds, collections, materials, installedPlugins };
|
|
10
|
+
}
|
|
11
|
+
function isElevationRelevant(node) {
|
|
12
|
+
return (node?.type === 'roof' ||
|
|
13
|
+
node?.type === 'wall' ||
|
|
14
|
+
node?.type === 'slab' ||
|
|
15
|
+
node?.type === 'level' ||
|
|
16
|
+
node?.type === 'building' ||
|
|
17
|
+
node?.type === 'site');
|
|
18
|
+
}
|
|
19
|
+
export function initializeRoofElevationSync() {
|
|
20
|
+
let disposed = false;
|
|
21
|
+
let queued = false;
|
|
22
|
+
let syncing = false;
|
|
23
|
+
const schedule = () => {
|
|
24
|
+
if (queued)
|
|
25
|
+
return;
|
|
26
|
+
queued = true;
|
|
27
|
+
// Wall bases use the spatial grid, whose listener must finish before this pass.
|
|
28
|
+
queueMicrotask(() => {
|
|
29
|
+
queued = false;
|
|
30
|
+
if (disposed)
|
|
31
|
+
return;
|
|
32
|
+
const { nodes, updateNodes, readOnly } = useScene.getState();
|
|
33
|
+
if (readOnly)
|
|
34
|
+
return;
|
|
35
|
+
const updates = [];
|
|
36
|
+
for (const node of Object.values(nodes)) {
|
|
37
|
+
if (node.type !== 'roof' || node.support?.kind !== 'walls')
|
|
38
|
+
continue;
|
|
39
|
+
const elevation = resolveRoofElevation(node, nodes);
|
|
40
|
+
if (Math.abs(node.position[1] - elevation) <= ROOF_ELEVATION_EPSILON)
|
|
41
|
+
continue;
|
|
42
|
+
updates.push({
|
|
43
|
+
id: node.id,
|
|
44
|
+
data: { position: [node.position[0], elevation, node.position[2]] },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (updates.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
const before = sceneSnapshot();
|
|
50
|
+
const publishCommit = useScene.temporal.getState().isTracking && getSceneHistoryPauseDepth() === 0;
|
|
51
|
+
syncing = true;
|
|
52
|
+
pauseSceneHistory(useScene);
|
|
53
|
+
try {
|
|
54
|
+
updateNodes(updates);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
resumeSceneHistory(useScene);
|
|
58
|
+
syncing = false;
|
|
59
|
+
}
|
|
60
|
+
// Deferred writes miss the originating commit; publish the settled roof without
|
|
61
|
+
// another undo step. An outer pause belongs to a gesture that owns its commit.
|
|
62
|
+
if (publishCommit) {
|
|
63
|
+
notifySceneCommit({
|
|
64
|
+
origin: 'local',
|
|
65
|
+
before,
|
|
66
|
+
current: sceneSnapshot(),
|
|
67
|
+
changedNodeIds: new Set(updates.map(({ id }) => id)),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
const unsubscribe = useScene.subscribe((state, previous) => {
|
|
73
|
+
if (syncing || state.nodes === previous.nodes)
|
|
74
|
+
return;
|
|
75
|
+
const ids = new Set([...Object.keys(state.nodes), ...Object.keys(previous.nodes)]);
|
|
76
|
+
for (const id of ids) {
|
|
77
|
+
const next = state.nodes[id];
|
|
78
|
+
const prev = previous.nodes[id];
|
|
79
|
+
if (next !== prev && (isElevationRelevant(next) || isElevationRelevant(prev))) {
|
|
80
|
+
schedule();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
schedule();
|
|
86
|
+
return () => {
|
|
87
|
+
disposed = true;
|
|
88
|
+
unsubscribe();
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function RoofElevationSystem() {
|
|
92
|
+
useEffect(initializeRoofElevationSync, []);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AnyNode, LevelNode, RoofNode, WallNode } from '../../schema';
|
|
2
|
+
export declare function resolveRoofWallTopElevation(targetLevelId: LevelNode['id'], wall: WallNode, nodes: Readonly<Record<string, AnyNode>>, elevations?: Map<string, import("../..").LevelElevation>): number;
|
|
3
|
+
export declare function resolveRoofElevation(roof: RoofNode, nodes: Readonly<Record<string, AnyNode>>): number;
|
|
4
|
+
//# sourceMappingURL=roof-elevation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roof-elevation.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-elevation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAmB,QAAQ,EAAE,MAAM,cAAc,CAAA;AAM3F,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,EAC9B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,UAAU,8CAA4B,GACrC,MAAM,CASR;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,MAAM,CAwFR"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { getWallBaseElevationForNodes, getWallEffectiveHeightForNodes, } from '../../hooks/spatial-grid/spatial-grid-manager';
|
|
2
|
+
import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync';
|
|
3
|
+
import { findLevelBelowId, getLevelElevations } from '../../services/storey';
|
|
4
|
+
import { wallOverlapsSlabFootprint } from '../slab/slab-support';
|
|
5
|
+
import { getWallArcData } from '../wall/wall-curve';
|
|
6
|
+
import { resolveRoomRoofFootprintOnLevel } from './roof-footprint';
|
|
7
|
+
export function resolveRoofWallTopElevation(targetLevelId, wall, nodes, elevations = getLevelElevations(nodes)) {
|
|
8
|
+
const sourceLevelY = elevations.get(resolveLevelId(wall, nodes))?.baseY ?? 0;
|
|
9
|
+
const targetLevelY = elevations.get(targetLevelId)?.baseY ?? 0;
|
|
10
|
+
return (sourceLevelY +
|
|
11
|
+
getWallBaseElevationForNodes(wall, nodes) +
|
|
12
|
+
getWallEffectiveHeightForNodes(wall, nodes) -
|
|
13
|
+
targetLevelY);
|
|
14
|
+
}
|
|
15
|
+
export function resolveRoofElevation(roof, nodes) {
|
|
16
|
+
if (roof.support?.kind !== 'walls')
|
|
17
|
+
return roof.position[1];
|
|
18
|
+
const levelId = resolveLevelId(roof, nodes);
|
|
19
|
+
if (nodes[levelId]?.type !== 'level')
|
|
20
|
+
return roof.position[1];
|
|
21
|
+
const elevations = getLevelElevations(nodes);
|
|
22
|
+
// A roof usually sits on the storey above its walls, but the top floor (or a
|
|
23
|
+
// roof armed from the walls' own level) keeps roof and walls on one level.
|
|
24
|
+
const belowId = findLevelBelowId(levelId, elevations);
|
|
25
|
+
const candidateWallIds = [levelId, belowId]
|
|
26
|
+
.map((id) => (id ? nodes[id] : undefined))
|
|
27
|
+
.filter((node) => node?.type === 'level')
|
|
28
|
+
.flatMap((level) => level.children);
|
|
29
|
+
const conicalSegments = roof.children
|
|
30
|
+
.map((id) => nodes[id])
|
|
31
|
+
.filter((node) => node?.type === 'roof-segment' && node.roofType === 'conical');
|
|
32
|
+
const cos = Math.cos(roof.rotation);
|
|
33
|
+
const sin = Math.sin(roof.rotation);
|
|
34
|
+
const toLevel = (x, z) => [
|
|
35
|
+
roof.position[0] + x * cos + z * sin,
|
|
36
|
+
roof.position[2] - x * sin + z * cos,
|
|
37
|
+
];
|
|
38
|
+
// Walls under the footprint, closed room or not: a room missing a wall, an
|
|
39
|
+
// L-shaped room whose centre falls outside, or a redrawn enclosure all still
|
|
40
|
+
// hold the roof up. The band test is curve- and thickness-aware and
|
|
41
|
+
// boundary-inclusive, so perimeter walls on the footprint edge count.
|
|
42
|
+
const footprints = roof.children
|
|
43
|
+
.map((id) => nodes[id])
|
|
44
|
+
.filter((node) => node?.type === 'roof-segment')
|
|
45
|
+
.map((segment) => {
|
|
46
|
+
const c = Math.cos(segment.rotation);
|
|
47
|
+
const s = Math.sin(segment.rotation);
|
|
48
|
+
const halfW = segment.width / 2;
|
|
49
|
+
const halfD = segment.depth / 2;
|
|
50
|
+
const corners = [
|
|
51
|
+
[-halfW, -halfD],
|
|
52
|
+
[halfW, -halfD],
|
|
53
|
+
[halfW, halfD],
|
|
54
|
+
[-halfW, halfD],
|
|
55
|
+
];
|
|
56
|
+
return corners.map(([x, z]) => toLevel(segment.position[0] + x * c + z * s, segment.position[2] - x * s + z * c));
|
|
57
|
+
});
|
|
58
|
+
const wallIds = conicalSegments.length
|
|
59
|
+
? candidateWallIds.filter((id) => {
|
|
60
|
+
const wall = nodes[id];
|
|
61
|
+
if (wall?.type !== 'wall')
|
|
62
|
+
return false;
|
|
63
|
+
const arc = getWallArcData(wall);
|
|
64
|
+
if (!arc)
|
|
65
|
+
return false;
|
|
66
|
+
return conicalSegments.some((segment) => {
|
|
67
|
+
const [centerX, centerZ] = toLevel(segment.position[0], segment.position[2]);
|
|
68
|
+
return (Math.hypot(arc.center.x - centerX, arc.center.y - centerZ) <= 1e-4 &&
|
|
69
|
+
Math.abs(arc.radius - segment.width / 2) <= 1e-4);
|
|
70
|
+
});
|
|
71
|
+
})
|
|
72
|
+
: footprints.length
|
|
73
|
+
? candidateWallIds.filter((id) => {
|
|
74
|
+
const wall = nodes[id];
|
|
75
|
+
return (wall?.type === 'wall' &&
|
|
76
|
+
footprints.some((polygon) => wallOverlapsSlabFootprint(wall, polygon)));
|
|
77
|
+
})
|
|
78
|
+
: ([levelId, belowId]
|
|
79
|
+
.map((id) => id
|
|
80
|
+
? resolveRoomRoofFootprintOnLevel(id, nodes, [
|
|
81
|
+
roof.position[0],
|
|
82
|
+
roof.position[2],
|
|
83
|
+
])
|
|
84
|
+
: null)
|
|
85
|
+
.find((target) => target !== null)?.wallIds ?? []);
|
|
86
|
+
let highest;
|
|
87
|
+
for (const id of wallIds) {
|
|
88
|
+
const wall = nodes[id];
|
|
89
|
+
if (wall?.type !== 'wall')
|
|
90
|
+
continue;
|
|
91
|
+
const top = resolveRoofWallTopElevation(levelId, wall, nodes, elevations);
|
|
92
|
+
highest = highest === undefined ? top : Math.max(highest, top);
|
|
93
|
+
}
|
|
94
|
+
return highest ?? roof.position[1];
|
|
95
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AnyNode, LevelNode, WallNode } from '../../schema';
|
|
2
|
+
export type RoofFootprintTarget = {
|
|
3
|
+
id: string;
|
|
4
|
+
polygon: Array<[number, number]>;
|
|
5
|
+
wallIds: WallNode['id'][];
|
|
6
|
+
center: [number, number];
|
|
7
|
+
width: number;
|
|
8
|
+
depth: number;
|
|
9
|
+
rotation: number;
|
|
10
|
+
rectangular: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function fitRoofFootprint(id: string, polygon: Array<[number, number]>, wallIds: WallNode['id'][]): RoofFootprintTarget | null;
|
|
13
|
+
export declare function resolveRoomRoofFootprint(levelId: LevelNode['id'], nodes: Readonly<Record<string, AnyNode>>, point: [number, number], options?: {
|
|
14
|
+
rectangularOnly?: boolean;
|
|
15
|
+
}): RoofFootprintTarget | null;
|
|
16
|
+
export declare function resolveRoomRoofFootprintOnLevel(levelId: LevelNode['id'], nodes: Readonly<Record<string, AnyNode>>, point: [number, number]): RoofFootprintTarget | null;
|
|
17
|
+
//# sourceMappingURL=roof-footprint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roof-footprint.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-footprint.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAGhE,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAChC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAA;IACzB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,OAAO,CAAA;CACrB,CAAA;AAWD,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAChC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,GACxB,mBAAmB,GAAG,IAAI,CAqD5B;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,EACxB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EACvB,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1C,mBAAmB,GAAG,IAAI,CAW5B;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,EACxB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GACtB,mBAAmB,GAAG,IAAI,CAW5B"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { pointInPolygon as pointInPolygon2D } from '../../lib/polygon-relations';
|
|
2
|
+
import { detectSpacesForLevel } from '../../lib/space-detection';
|
|
3
|
+
import { getLevelBelow } from '../../services/storey';
|
|
4
|
+
function polygonArea(polygon) {
|
|
5
|
+
return Math.abs(polygon.reduce((area, point, index) => {
|
|
6
|
+
const next = polygon[(index + 1) % polygon.length];
|
|
7
|
+
return next ? area + point[0] * next[1] - next[0] * point[1] : area;
|
|
8
|
+
}, 0) / 2);
|
|
9
|
+
}
|
|
10
|
+
export function fitRoofFootprint(id, polygon, wallIds) {
|
|
11
|
+
if (polygon.length < 3)
|
|
12
|
+
return null;
|
|
13
|
+
let best;
|
|
14
|
+
for (let index = 0; index < polygon.length; index++) {
|
|
15
|
+
const point = polygon[index];
|
|
16
|
+
const next = polygon[(index + 1) % polygon.length];
|
|
17
|
+
if (!(point && next))
|
|
18
|
+
continue;
|
|
19
|
+
const rotation = Math.atan2(next[1] - point[1], next[0] - point[0]);
|
|
20
|
+
const cos = Math.cos(rotation);
|
|
21
|
+
const sin = Math.sin(rotation);
|
|
22
|
+
const rotated = polygon.map(([x, z]) => [x * cos + z * sin, -x * sin + z * cos]);
|
|
23
|
+
const xs = rotated.map(([x]) => x);
|
|
24
|
+
const zs = rotated.map(([, z]) => z);
|
|
25
|
+
const minX = Math.min(...xs);
|
|
26
|
+
const maxX = Math.max(...xs);
|
|
27
|
+
const minZ = Math.min(...zs);
|
|
28
|
+
const maxZ = Math.max(...zs);
|
|
29
|
+
const width = maxX - minX;
|
|
30
|
+
const depth = maxZ - minZ;
|
|
31
|
+
const area = width * depth;
|
|
32
|
+
if (area <= 0 || (best && best.area <= area))
|
|
33
|
+
continue;
|
|
34
|
+
const localCenterX = (minX + maxX) / 2;
|
|
35
|
+
const localCenterZ = (minZ + maxZ) / 2;
|
|
36
|
+
best = {
|
|
37
|
+
center: [localCenterX * cos - localCenterZ * sin, localCenterX * sin + localCenterZ * cos],
|
|
38
|
+
width,
|
|
39
|
+
depth,
|
|
40
|
+
rotation: -rotation,
|
|
41
|
+
area,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (!best)
|
|
45
|
+
return null;
|
|
46
|
+
return {
|
|
47
|
+
id,
|
|
48
|
+
polygon,
|
|
49
|
+
wallIds,
|
|
50
|
+
center: best.center,
|
|
51
|
+
width: best.width,
|
|
52
|
+
depth: best.depth,
|
|
53
|
+
rotation: best.rotation,
|
|
54
|
+
rectangular: polygonArea(polygon) / best.area >= 0.96,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function resolveRoomRoofFootprint(levelId, nodes, point, options = {}) {
|
|
58
|
+
const activeTarget = resolveRoomRoofFootprintOnLevel(levelId, nodes, point);
|
|
59
|
+
if (activeTarget && (!options.rectangularOnly || activeTarget.rectangular))
|
|
60
|
+
return activeTarget;
|
|
61
|
+
if (activeTarget)
|
|
62
|
+
return null;
|
|
63
|
+
const levelBelow = getLevelBelow(levelId, nodes);
|
|
64
|
+
const levelBelowTarget = levelBelow
|
|
65
|
+
? resolveRoomRoofFootprintOnLevel(levelBelow.id, nodes, point)
|
|
66
|
+
: null;
|
|
67
|
+
return levelBelowTarget && (!options.rectangularOnly || levelBelowTarget.rectangular)
|
|
68
|
+
? levelBelowTarget
|
|
69
|
+
: null;
|
|
70
|
+
}
|
|
71
|
+
export function resolveRoomRoofFootprintOnLevel(levelId, nodes, point) {
|
|
72
|
+
const level = nodes[levelId];
|
|
73
|
+
if (level?.type !== 'level')
|
|
74
|
+
return null;
|
|
75
|
+
const walls = level.children
|
|
76
|
+
.map((id) => nodes[id])
|
|
77
|
+
.filter((node) => node?.type === 'wall');
|
|
78
|
+
const spaces = detectSpacesForLevel(levelId, walls)
|
|
79
|
+
.spaces.filter((space) => !space.isExterior && pointInPolygon2D(point, space.polygon))
|
|
80
|
+
.sort((left, right) => polygonArea(left.polygon) - polygonArea(right.polygon));
|
|
81
|
+
const space = spaces[0];
|
|
82
|
+
return space ? fitRoofFootprint(space.id, space.polygon, space.wallIds) : null;
|
|
83
|
+
}
|
|
@@ -114,37 +114,6 @@ export type WallSlabSupportSegment = {
|
|
|
114
114
|
end: number;
|
|
115
115
|
elevation: number;
|
|
116
116
|
};
|
|
117
|
-
/**
|
|
118
|
-
* `preferredSlabId` is a persisted support host (`wall.supportSlabId`):
|
|
119
|
-
* while that slab is still in the candidate set (still overlaps the wall
|
|
120
|
-
* band with enough covered length), the elected `elevation` is pinned to
|
|
121
|
-
* it instead of the majority/best-coverage election. `baseSegments` /
|
|
122
|
-
* `baseElevation` (fill-down) still derive from ALL supporting slabs
|
|
123
|
-
* unchanged. A preferred slab that no longer qualifies is silently
|
|
124
|
-
* ignored — deliberately never cleared here, so the host resumes if the
|
|
125
|
-
* slab's polygon returns (only slab deletion strips the stored field).
|
|
126
|
-
*
|
|
127
|
-
* `maxElevation` is the pointer-decided support cap (level-local Y, same
|
|
128
|
-
* semantics as the item election): when set, elevation groups whose
|
|
129
|
-
* walking surface sits above `maxElevation + SUPPORT_ELEVATION_EPSILON`
|
|
130
|
-
* are excluded from the majority/best election — a deck hanging above the
|
|
131
|
-
* surface the cursor ray actually hit never captures the elected base.
|
|
132
|
-
* `baseSegments` / `baseElevation` stay uncapped (geometry fill-down), and
|
|
133
|
-
* an explicit `preferredSlabId` still wins over the cap.
|
|
134
|
-
*
|
|
135
|
-
* `levelBase` is what "no slab supports this here" evaluates to — the
|
|
136
|
-
* sculpted ground under the wall (`levelBaseElevationAt`), or 0 for a level
|
|
137
|
-
* with no terrain under it. A caller-resolved scalar rather than a terrain
|
|
138
|
-
* lookup in here, so this stays pure and the sample stays at the wall's own
|
|
139
|
-
* XZ. It substitutes for every place this function used to write a literal
|
|
140
|
-
* `0`, and nowhere else: a slab that supports the wall's faces still wins
|
|
141
|
-
* outright, so a slab pad on a hillside keeps its wall at the pad's
|
|
142
|
-
* elevation instead of being overruled by the ground around it. The one
|
|
143
|
-
* clamp — center-only support, where the old code already clamped a
|
|
144
|
-
* recessed slab up to `0` so a wall over a pool didn't sink — generalizes to
|
|
145
|
-
* "never below the ground", which is the same rule with the ground no longer
|
|
146
|
-
* assumed flat.
|
|
147
|
-
*/
|
|
148
117
|
export declare function computeWallSlabSupport(wallLike: WallOverlapInput, slabs: readonly SlabNode[], levelWalls: WallNode[], preferredSlabId?: string | null, maxElevation?: number | null, levelBase?: number): WallSlabSupport;
|
|
149
118
|
export declare function computeWallSlabElevation(wallLike: WallOverlapInput, slabs: readonly SlabNode[], levelWalls: WallNode[]): number;
|
|
150
119
|
//# sourceMappingURL=slab-support.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slab-support.d.ts","sourceRoot":"","sources":["../../../src/systems/slab/slab-support.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAKtD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CACxC,iBAAiB,EAAE,MAAM,EACzB,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,QAAQ,EAAE,EACtB,UAAU,EAAE,SAAS,QAAQ,EAAE,EAC/B,YAAY,EAAE,MAAM,GACnB,kBAAkB,CA6BpB;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,QAAQ,EAAE,EACtB,UAAU,EAAE,SAAS,QAAQ,EAAE,EAC/B,YAAY,EAAE,MAAM,GACnB,MAAM,CAMR;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,OAAO,CAchG;AA0BD,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC/B,OAAO,CAQT;AAsID,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAgED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,gBAAgB,EAChD,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACxD,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACnC,OAAO,CAqBT;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAChC,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAC7C,OAAO,CAoBT;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAA;AAa7C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAA;IACjB,mFAAmF;IACnF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,mFAAmF;IACnF,aAAa,EAAE,MAAM,CAAA;IACrB,4FAA4F;IAC5F,YAAY,EAAE,sBAAsB,EAAE,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;
|
|
1
|
+
{"version":3,"file":"slab-support.d.ts","sourceRoot":"","sources":["../../../src/systems/slab/slab-support.ts"],"names":[],"mappings":"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"}
|
|
@@ -396,6 +396,31 @@ const WALL_SLAB_ELEVATION_POOL_EPSILON = 1e-4;
|
|
|
396
396
|
* "never below the ground", which is the same rule with the ground no longer
|
|
397
397
|
* assumed flat.
|
|
398
398
|
*/
|
|
399
|
+
// A rendered slab polygon depends only on the slab set and the level's walls,
|
|
400
|
+
// never on the wall being tested — but a per-frame pass asks for support once
|
|
401
|
+
// per wall, so the identical polygons were rebuilt for every wall on the level
|
|
402
|
+
// (and each rebuild scans all of `levelWalls`). Keyed on array identity: the
|
|
403
|
+
// caller derives those arrays once and only rebuilds them when the scene or a
|
|
404
|
+
// live preview changes, so a hit means the inputs are the same objects.
|
|
405
|
+
let polygonMemoSlabs = null;
|
|
406
|
+
let polygonMemoWalls = null;
|
|
407
|
+
let polygonMemo = new Map();
|
|
408
|
+
function renderedSlabPolygon(slab, slabs, levelWalls) {
|
|
409
|
+
if (polygonMemoSlabs !== slabs || polygonMemoWalls !== levelWalls) {
|
|
410
|
+
polygonMemoSlabs = slabs;
|
|
411
|
+
polygonMemoWalls = levelWalls;
|
|
412
|
+
polygonMemo = new Map();
|
|
413
|
+
}
|
|
414
|
+
const cached = polygonMemo.get(slab.id);
|
|
415
|
+
if (cached)
|
|
416
|
+
return cached;
|
|
417
|
+
const polygon = getRenderableSlabPolygon(slab, {
|
|
418
|
+
walls: levelWalls,
|
|
419
|
+
siblingSlabs: slabs.filter((other) => other.id !== slab.id),
|
|
420
|
+
});
|
|
421
|
+
polygonMemo.set(slab.id, polygon);
|
|
422
|
+
return polygon;
|
|
423
|
+
}
|
|
399
424
|
export function computeWallSlabSupport(wallLike, slabs, levelWalls, preferredSlabId, maxElevation, levelBase = 0) {
|
|
400
425
|
const { start, end, curveOffset = 0, thickness = DEFAULT_WALL_THICKNESS } = wallLike;
|
|
401
426
|
const halfThickness = Math.max(thickness / 2, 0);
|
|
@@ -417,10 +442,7 @@ export function computeWallSlabSupport(wallLike, slabs, levelWalls, preferredSla
|
|
|
417
442
|
for (const slab of slabs) {
|
|
418
443
|
if (slab.polygon.length < 3)
|
|
419
444
|
continue;
|
|
420
|
-
const renderedPolygon =
|
|
421
|
-
walls: levelWalls,
|
|
422
|
-
siblingSlabs: slabs.filter((other) => other.id !== slab.id),
|
|
423
|
-
});
|
|
445
|
+
const renderedPolygon = renderedSlabPolygon(slab, slabs, levelWalls);
|
|
424
446
|
let supported = 0;
|
|
425
447
|
const perPolyline = polylines.map((line) => {
|
|
426
448
|
let intervals = polylineInsideIntervals(line, renderedPolygon);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AnyNode, StairNode } from '../../schema';
|
|
2
|
+
import { StairSegmentNode } from '../../schema';
|
|
3
|
+
export type StairFlightOverrides = Partial<Pick<StairSegmentNode, 'width' | 'length' | 'height' | 'stepCount' | 'attachmentSide' | 'fillToFloor' | 'thickness'>>;
|
|
4
|
+
/**
|
|
5
|
+
* The single definition of a default straight flight. Anything left out falls
|
|
6
|
+
* through to the `StairSegmentNode` schema defaults (length 3 m, 10 steps,
|
|
7
|
+
* filled to floor) rather than being spelled again per call site, so the stair
|
|
8
|
+
* tool's seed segment, the flight the panel materializes when a curved stair
|
|
9
|
+
* becomes straight, and the viewer's fallback body all describe one stair.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createDefaultStairSegment(overrides?: StairFlightOverrides): StairSegmentNode;
|
|
12
|
+
/**
|
|
13
|
+
* The flight a straight stair implies from its own fields — used wherever a
|
|
14
|
+
* straight stair has to stand in for missing `stair-segment` children.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createStairFlightFromStair(stair: StairNode, nodes: Record<string, AnyNode>): StairSegmentNode;
|
|
17
|
+
//# sourceMappingURL=stair-flight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stair-flight.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-flight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAM/C,MAAM,MAAM,oBAAoB,GAAG,OAAO,CACxC,IAAI,CACF,gBAAgB,EAChB,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,gBAAgB,GAAG,aAAa,GAAG,WAAW,CAC7F,CACF,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,GAAE,oBAAyB,GAAG,gBAAgB,CAEhG;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,gBAAgB,CAQlB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { StairSegmentNode } from '../../schema';
|
|
2
|
+
import { resolveStairTotalRise } from './stair-rise';
|
|
3
|
+
const MIN_STAIR_FLIGHT_RISE = 0.1;
|
|
4
|
+
const MIN_STAIR_FLIGHT_STEP_COUNT = 2;
|
|
5
|
+
/**
|
|
6
|
+
* The single definition of a default straight flight. Anything left out falls
|
|
7
|
+
* through to the `StairSegmentNode` schema defaults (length 3 m, 10 steps,
|
|
8
|
+
* filled to floor) rather than being spelled again per call site, so the stair
|
|
9
|
+
* tool's seed segment, the flight the panel materializes when a curved stair
|
|
10
|
+
* becomes straight, and the viewer's fallback body all describe one stair.
|
|
11
|
+
*/
|
|
12
|
+
export function createDefaultStairSegment(overrides = {}) {
|
|
13
|
+
return StairSegmentNode.parse({ segmentType: 'stair', position: [0, 0, 0], ...overrides });
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The flight a straight stair implies from its own fields — used wherever a
|
|
17
|
+
* straight stair has to stand in for missing `stair-segment` children.
|
|
18
|
+
*/
|
|
19
|
+
export function createStairFlightFromStair(stair, nodes) {
|
|
20
|
+
return createDefaultStairSegment({
|
|
21
|
+
width: stair.width,
|
|
22
|
+
height: Math.max(resolveStairTotalRise(stair, nodes), MIN_STAIR_FLIGHT_RISE),
|
|
23
|
+
stepCount: Math.max(MIN_STAIR_FLIGHT_STEP_COUNT, Math.round(stair.stepCount ?? 10)),
|
|
24
|
+
thickness: stair.thickness,
|
|
25
|
+
fillToFloor: stair.fillToFloor,
|
|
26
|
+
});
|
|
27
|
+
}
|