@pascal-app/core 1.0.6 → 1.1.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/dist/agent-operations/__fixtures__/add-object-cases.d.ts +31 -0
- package/dist/agent-operations/__fixtures__/add-object-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/build-room-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/build-room-cases.js +27 -0
- package/dist/agent-operations/__fixtures__/cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/cases.js +19 -11
- package/dist/agent-operations/__fixtures__/design-brief-cases.d.ts +25 -2
- package/dist/agent-operations/__fixtures__/design-brief-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/design-brief-cases.js +509 -66
- package/dist/agent-operations/__fixtures__/dressing-scenes.d.ts +30 -0
- package/dist/agent-operations/__fixtures__/dressing-scenes.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/dressing-scenes.js +82 -0
- package/dist/agent-operations/__fixtures__/native-materials.d.ts +411 -9
- package/dist/agent-operations/__fixtures__/native-materials.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/notation-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/notation-cases.js +14 -0
- package/dist/agent-operations/__fixtures__/roof-bearing-cases.d.ts +13804 -10600
- package/dist/agent-operations/__fixtures__/roof-bearing-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/room-cases.d.ts +1 -0
- package/dist/agent-operations/__fixtures__/room-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/room-cases.js +57 -1
- package/dist/agent-operations/__fixtures__/room-check-scenes.d.ts +51 -0
- package/dist/agent-operations/__fixtures__/room-check-scenes.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/room-check-scenes.js +121 -0
- package/dist/agent-operations/__fixtures__/scene-check-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/scene-check-cases.js +152 -18
- package/dist/agent-operations/__fixtures__/search-assets-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/search-assets-cases.js +91 -0
- package/dist/agent-operations/__fixtures__/site-cases.d.ts +40 -1
- package/dist/agent-operations/__fixtures__/site-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/structure-cases.d.ts +3 -0
- package/dist/agent-operations/__fixtures__/structure-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/structure-cases.js +199 -18
- package/dist/agent-operations/__fixtures__/unit-cases.d.ts +6 -0
- package/dist/agent-operations/__fixtures__/unit-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/unit-cases.js +266 -0
- package/dist/agent-operations/__fixtures__/verify-scene-cases.d.ts +37 -1
- package/dist/agent-operations/__fixtures__/verify-scene-cases.d.ts.map +1 -1
- package/dist/agent-operations/add-column.d.ts.map +1 -1
- package/dist/agent-operations/add-column.js +32 -3
- package/dist/agent-operations/add-fence.d.ts.map +1 -1
- package/dist/agent-operations/add-fence.js +2 -1
- package/dist/agent-operations/add-level.d.ts.map +1 -1
- package/dist/agent-operations/add-level.js +4 -4
- package/dist/agent-operations/add-object.d.ts +32 -6
- package/dist/agent-operations/add-object.d.ts.map +1 -1
- package/dist/agent-operations/add-object.js +59 -12
- package/dist/agent-operations/add-wall.d.ts +3 -8
- package/dist/agent-operations/add-wall.d.ts.map +1 -1
- package/dist/agent-operations/add-wall.js +31 -8
- package/dist/agent-operations/apply-changes.d.ts.map +1 -1
- package/dist/agent-operations/apply-changes.js +2 -1
- package/dist/agent-operations/asset-catalog.d.ts +63 -0
- package/dist/agent-operations/asset-catalog.d.ts.map +1 -0
- package/dist/agent-operations/asset-catalog.js +182 -0
- package/dist/agent-operations/asset-views.d.ts +4 -2
- package/dist/agent-operations/asset-views.d.ts.map +1 -1
- package/dist/agent-operations/asset-views.js +1 -3
- package/dist/agent-operations/author-scene-checks.d.ts +38 -0
- package/dist/agent-operations/author-scene-checks.d.ts.map +1 -0
- package/dist/agent-operations/author-scene-checks.js +266 -0
- package/dist/agent-operations/author-scene.d.ts +54 -20
- package/dist/agent-operations/author-scene.d.ts.map +1 -1
- package/dist/agent-operations/author-scene.js +1163 -196
- package/dist/agent-operations/authored-scene-merge.d.ts.map +1 -1
- package/dist/agent-operations/authored-scene-merge.js +61 -3
- package/dist/agent-operations/batch.d.ts +1 -1
- package/dist/agent-operations/batch.d.ts.map +1 -1
- package/dist/agent-operations/batch.js +38 -39
- package/dist/agent-operations/ceiling-object-observations.d.ts.map +1 -1
- package/dist/agent-operations/ceiling-object-observations.js +5 -4
- package/dist/agent-operations/circulation-geometry.d.ts +14 -0
- package/dist/agent-operations/circulation-geometry.d.ts.map +1 -0
- package/dist/agent-operations/circulation-geometry.js +151 -0
- package/dist/agent-operations/circulation-graph.d.ts +72 -0
- package/dist/agent-operations/circulation-graph.d.ts.map +1 -0
- package/dist/agent-operations/circulation-graph.js +713 -0
- package/dist/agent-operations/circulation-non-spaces.d.ts +10 -0
- package/dist/agent-operations/circulation-non-spaces.d.ts.map +1 -0
- package/dist/agent-operations/circulation-non-spaces.js +88 -0
- package/dist/agent-operations/circulation-spaces.d.ts +5881 -0
- package/dist/agent-operations/circulation-spaces.d.ts.map +1 -0
- package/dist/agent-operations/circulation-spaces.js +368 -0
- package/dist/agent-operations/collections.d.ts.map +1 -1
- package/dist/agent-operations/collections.js +9 -7
- package/dist/agent-operations/corner-window.d.ts.map +1 -1
- package/dist/agent-operations/corner-window.js +2 -4
- package/dist/agent-operations/create-roof.d.ts.map +1 -1
- package/dist/agent-operations/create-roof.js +5 -7
- package/dist/agent-operations/create-room.d.ts +4 -1
- package/dist/agent-operations/create-room.d.ts.map +1 -1
- package/dist/agent-operations/create-room.js +7 -4
- package/dist/agent-operations/create-stair.js +2 -2
- package/dist/agent-operations/design-brief.d.ts +13 -7
- package/dist/agent-operations/design-brief.d.ts.map +1 -1
- package/dist/agent-operations/design-brief.js +172 -92
- package/dist/agent-operations/door-clearance.d.ts +68 -8
- package/dist/agent-operations/door-clearance.d.ts.map +1 -1
- package/dist/agent-operations/door-clearance.js +127 -20
- package/dist/agent-operations/dress-targets.d.ts +23 -0
- package/dist/agent-operations/dress-targets.d.ts.map +1 -0
- package/dist/agent-operations/dress-targets.js +34 -0
- package/dist/agent-operations/duplicate-node.d.ts +14 -0
- package/dist/agent-operations/duplicate-node.d.ts.map +1 -0
- package/dist/agent-operations/duplicate-node.js +173 -0
- package/dist/agent-operations/find-by-type.d.ts.map +1 -1
- package/dist/agent-operations/find-by-type.js +3 -0
- package/dist/agent-operations/furnish-room.d.ts +2 -0
- package/dist/agent-operations/furnish-room.d.ts.map +1 -1
- package/dist/agent-operations/furnish-room.js +15 -4
- package/dist/agent-operations/furniture-kind.d.ts +28 -1
- package/dist/agent-operations/furniture-kind.d.ts.map +1 -1
- package/dist/agent-operations/furniture-kind.js +105 -6
- package/dist/agent-operations/ground-reader.d.ts +10 -0
- package/dist/agent-operations/ground-reader.d.ts.map +1 -0
- package/dist/agent-operations/ground-reader.js +25 -0
- package/dist/agent-operations/index.d.ts +118 -12
- package/dist/agent-operations/index.d.ts.map +1 -1
- package/dist/agent-operations/index.js +26 -1
- package/dist/agent-operations/item-solids.d.ts +39 -0
- package/dist/agent-operations/item-solids.d.ts.map +1 -0
- package/dist/agent-operations/item-solids.js +128 -0
- package/dist/agent-operations/layout-clearance.d.ts +91 -11
- package/dist/agent-operations/layout-clearance.d.ts.map +1 -1
- package/dist/agent-operations/layout-clearance.js +280 -73
- package/dist/agent-operations/level-height-fill.d.ts.map +1 -1
- package/dist/agent-operations/level-height-fill.js +7 -1
- package/dist/agent-operations/level-reads.d.ts +5 -0
- package/dist/agent-operations/level-reads.d.ts.map +1 -1
- package/dist/agent-operations/level-reads.js +39 -3
- package/dist/agent-operations/list-levels.d.ts.map +1 -1
- package/dist/agent-operations/list-levels.js +2 -4
- package/dist/agent-operations/move-node.d.ts +12 -0
- package/dist/agent-operations/move-node.d.ts.map +1 -0
- package/dist/agent-operations/move-node.js +28 -0
- package/dist/agent-operations/move-plan.d.ts +28 -0
- package/dist/agent-operations/move-plan.d.ts.map +1 -0
- package/dist/agent-operations/move-plan.js +259 -0
- package/dist/agent-operations/node-patch.d.ts.map +1 -1
- package/dist/agent-operations/node-patch.js +15 -0
- package/dist/agent-operations/paint.d.ts +3 -1
- package/dist/agent-operations/paint.d.ts.map +1 -1
- package/dist/agent-operations/paint.js +14 -1
- package/dist/agent-operations/photo-elevation.d.ts.map +1 -1
- package/dist/agent-operations/photo-elevation.js +2 -1
- package/dist/agent-operations/place-in-room.d.ts.map +1 -1
- package/dist/agent-operations/place-in-room.js +1 -0
- package/dist/agent-operations/place-items.d.ts.map +1 -1
- package/dist/agent-operations/place-items.js +3 -4
- package/dist/agent-operations/plan-clearance.d.ts +28 -0
- package/dist/agent-operations/plan-clearance.d.ts.map +1 -0
- package/dist/agent-operations/plan-clearance.js +47 -0
- package/dist/agent-operations/plan-geometry.d.ts +2 -0
- package/dist/agent-operations/plan-geometry.d.ts.map +1 -1
- package/dist/agent-operations/plan-geometry.js +10 -0
- package/dist/agent-operations/project-files.d.ts +10 -0
- package/dist/agent-operations/project-files.d.ts.map +1 -1
- package/dist/agent-operations/reachability.d.ts +41 -0
- package/dist/agent-operations/reachability.d.ts.map +1 -0
- package/dist/agent-operations/reachability.js +199 -0
- package/dist/agent-operations/room-checks.d.ts +60 -0
- package/dist/agent-operations/room-checks.d.ts.map +1 -0
- package/dist/agent-operations/room-checks.js +226 -0
- package/dist/agent-operations/room-faces.d.ts +30 -0
- package/dist/agent-operations/room-faces.d.ts.map +1 -0
- package/dist/agent-operations/room-faces.js +31 -0
- package/dist/agent-operations/room-findings.d.ts +5 -2
- package/dist/agent-operations/room-findings.d.ts.map +1 -1
- package/dist/agent-operations/room-findings.js +30 -0
- package/dist/agent-operations/room-finishes.d.ts +87 -0
- package/dist/agent-operations/room-finishes.d.ts.map +1 -0
- package/dist/agent-operations/room-finishes.js +193 -0
- package/dist/agent-operations/room-floor-script.d.ts +3 -3
- package/dist/agent-operations/room-floor-script.js +3 -3
- package/dist/agent-operations/room-readings.d.ts +90 -0
- package/dist/agent-operations/room-readings.d.ts.map +1 -0
- package/dist/agent-operations/room-readings.js +369 -0
- package/dist/agent-operations/room-tile.d.ts +15 -0
- package/dist/agent-operations/room-tile.d.ts.map +1 -0
- package/dist/agent-operations/room-tile.js +30 -0
- package/dist/agent-operations/room-wet.d.ts +55 -0
- package/dist/agent-operations/room-wet.d.ts.map +1 -0
- package/dist/agent-operations/room-wet.js +203 -0
- package/dist/agent-operations/scene-check-policy.d.ts +5 -0
- package/dist/agent-operations/scene-check-policy.d.ts.map +1 -0
- package/dist/agent-operations/scene-check-policy.js +24 -0
- package/dist/agent-operations/scene-checkpoint.d.ts +10 -0
- package/dist/agent-operations/scene-checkpoint.d.ts.map +1 -1
- package/dist/agent-operations/scene-checkpoint.js +31 -0
- package/dist/agent-operations/scene-issue-groups.d.ts +17 -0
- package/dist/agent-operations/scene-issue-groups.d.ts.map +1 -0
- package/dist/agent-operations/scene-issue-groups.js +71 -0
- package/dist/agent-operations/scene-materials.d.ts +23 -0
- package/dist/agent-operations/scene-materials.d.ts.map +1 -1
- package/dist/agent-operations/scene-materials.js +41 -0
- package/dist/agent-operations/scene-queries.d.ts +2525 -1723
- package/dist/agent-operations/scene-queries.d.ts.map +1 -1
- package/dist/agent-operations/scene-queries.js +3 -3
- package/dist/agent-operations/scene-review.d.ts.map +1 -1
- package/dist/agent-operations/scene-review.js +3 -2
- package/dist/agent-operations/scene-view.d.ts.map +1 -1
- package/dist/agent-operations/scene-view.js +91 -21
- package/dist/agent-operations/search-assets.d.ts +10 -6
- package/dist/agent-operations/search-assets.d.ts.map +1 -1
- package/dist/agent-operations/search-assets.js +37 -22
- package/dist/agent-operations/set-checkpoint.d.ts.map +1 -1
- package/dist/agent-operations/set-checkpoint.js +1 -0
- package/dist/agent-operations/set-ground-level.d.ts +1 -1
- package/dist/agent-operations/set-ground-level.d.ts.map +1 -1
- package/dist/agent-operations/set-ground-level.js +3 -14
- package/dist/agent-operations/site.js +3 -2
- package/dist/agent-operations/slab-surface-script.d.ts +25 -0
- package/dist/agent-operations/slab-surface-script.d.ts.map +1 -0
- package/dist/agent-operations/slab-surface-script.js +60 -0
- package/dist/agent-operations/stair-plan-footprint.d.ts +9 -0
- package/dist/agent-operations/stair-plan-footprint.d.ts.map +1 -0
- package/dist/agent-operations/stair-plan-footprint.js +50 -0
- package/dist/agent-operations/types.d.ts +10 -5
- package/dist/agent-operations/types.d.ts.map +1 -1
- package/dist/agent-operations/units.d.ts +26 -0
- package/dist/agent-operations/units.d.ts.map +1 -0
- package/dist/agent-operations/units.js +110 -0
- package/dist/agent-operations/unreachable-rooms-check.d.ts +17 -0
- package/dist/agent-operations/unreachable-rooms-check.d.ts.map +1 -0
- package/dist/agent-operations/unreachable-rooms-check.js +35 -0
- package/dist/agent-operations/usage-checks.d.ts +2 -0
- package/dist/agent-operations/usage-checks.d.ts.map +1 -0
- package/dist/agent-operations/usage-checks.js +12 -0
- package/dist/agent-operations/verify-scene.d.ts +8 -11
- package/dist/agent-operations/verify-scene.d.ts.map +1 -1
- package/dist/agent-operations/verify-scene.js +59 -13
- package/dist/agent-operations/wall-face-defaults.d.ts +44 -0
- package/dist/agent-operations/wall-face-defaults.d.ts.map +1 -0
- package/dist/agent-operations/wall-face-defaults.js +229 -0
- package/dist/agent-operations/wall-joins.d.ts +119 -0
- package/dist/agent-operations/wall-joins.d.ts.map +1 -0
- package/dist/agent-operations/wall-joins.js +308 -0
- package/dist/agent-operations/wall-object-observations.d.ts.map +1 -1
- package/dist/agent-operations/wall-object-observations.js +5 -7
- package/dist/agent-operations/wall-skirting.d.ts.map +1 -1
- package/dist/agent-operations/wall-skirting.js +2 -0
- package/dist/agent-operations/wall-type.d.ts +49 -0
- package/dist/agent-operations/wall-type.d.ts.map +1 -0
- package/dist/agent-operations/wall-type.js +31 -0
- package/dist/agent-operations/zone-coverage.d.ts +85 -0
- package/dist/agent-operations/zone-coverage.d.ts.map +1 -0
- package/dist/agent-operations/zone-coverage.js +208 -0
- package/dist/agent-tools/add-object.d.ts +31 -0
- package/dist/agent-tools/add-object.d.ts.map +1 -1
- package/dist/agent-tools/add-object.js +44 -7
- package/dist/agent-tools/assets.d.ts +4 -0
- package/dist/agent-tools/assets.d.ts.map +1 -1
- package/dist/agent-tools/assets.js +11 -1
- package/dist/agent-tools/author-scene.d.ts +810 -15
- package/dist/agent-tools/author-scene.d.ts.map +1 -1
- package/dist/agent-tools/author-scene.js +275 -34
- package/dist/agent-tools/batch.d.ts +1 -1
- package/dist/agent-tools/batch.d.ts.map +1 -1
- package/dist/agent-tools/batch.js +5 -1
- package/dist/agent-tools/checkpoints.js +1 -1
- package/dist/agent-tools/columns.d.ts +14 -0
- package/dist/agent-tools/columns.d.ts.map +1 -1
- package/dist/agent-tools/columns.js +14 -2
- package/dist/agent-tools/contract.d.ts +10 -0
- package/dist/agent-tools/contract.d.ts.map +1 -1
- package/dist/agent-tools/contract.js +5 -1
- package/dist/agent-tools/corner-window.d.ts +1 -0
- package/dist/agent-tools/corner-window.d.ts.map +1 -1
- package/dist/agent-tools/corner-window.js +1 -1
- package/dist/agent-tools/create-room.d.ts +85 -0
- package/dist/agent-tools/create-room.d.ts.map +1 -1
- package/dist/agent-tools/create-room.js +8 -1
- package/dist/agent-tools/design-brief.d.ts +513 -7
- package/dist/agent-tools/design-brief.d.ts.map +1 -1
- package/dist/agent-tools/design-brief.js +192 -14
- package/dist/agent-tools/designs.js +1 -1
- package/dist/agent-tools/find-by-type.js +1 -1
- package/dist/agent-tools/index.d.ts +658 -10
- package/dist/agent-tools/index.d.ts.map +1 -1
- package/dist/agent-tools/index.js +9 -1
- package/dist/agent-tools/levels.js +4 -4
- package/dist/agent-tools/move-duplicate.d.ts +65 -0
- package/dist/agent-tools/move-duplicate.d.ts.map +1 -0
- package/dist/agent-tools/move-duplicate.js +68 -0
- package/dist/agent-tools/paint.d.ts +6 -0
- package/dist/agent-tools/paint.d.ts.map +1 -1
- package/dist/agent-tools/paint.js +7 -3
- package/dist/agent-tools/photo-elevation.js +1 -1
- package/dist/agent-tools/project-files.d.ts +2 -2
- package/dist/agent-tools/project-files.d.ts.map +1 -1
- package/dist/agent-tools/project-files.js +1 -1
- package/dist/agent-tools/refusal.d.ts +9 -1
- package/dist/agent-tools/refusal.d.ts.map +1 -1
- package/dist/agent-tools/refusal.js +5 -1
- package/dist/agent-tools/roofs.js +1 -1
- package/dist/agent-tools/room-structure.d.ts +60 -0
- package/dist/agent-tools/room-structure.d.ts.map +1 -1
- package/dist/agent-tools/room-structure.js +20 -1
- package/dist/agent-tools/search-materials.d.ts +1 -0
- package/dist/agent-tools/search-materials.d.ts.map +1 -1
- package/dist/agent-tools/site.js +1 -1
- package/dist/agent-tools/stairs.js +1 -1
- package/dist/agent-tools/units.d.ts +84 -0
- package/dist/agent-tools/units.d.ts.map +1 -0
- package/dist/agent-tools/units.js +54 -0
- package/dist/agent-tools/verify-scene-usage.d.ts +44 -0
- package/dist/agent-tools/verify-scene-usage.d.ts.map +1 -0
- package/dist/agent-tools/verify-scene-usage.js +146 -0
- package/dist/agent-tools/verify-scene.d.ts +12 -1
- package/dist/agent-tools/verify-scene.d.ts.map +1 -1
- package/dist/agent-tools/verify-scene.js +183 -3
- package/dist/agent-tools/view-scene.d.ts +3 -0
- package/dist/agent-tools/view-scene.d.ts.map +1 -1
- package/dist/agent-tools/view-scene.js +4 -1
- package/dist/agent-tools/wall-openings.d.ts +121 -2
- package/dist/agent-tools/wall-openings.d.ts.map +1 -1
- package/dist/agent-tools/wall-openings.js +123 -18
- package/dist/agent-tools/walls.d.ts +30 -0
- package/dist/agent-tools/walls.d.ts.map +1 -1
- package/dist/agent-tools/walls.js +54 -2
- package/dist/building/__fixtures__/wall-opening-cases.d.ts +181 -35
- package/dist/building/__fixtures__/wall-opening-cases.d.ts.map +1 -1
- package/dist/building/__fixtures__/wall-opening-cases.js +13 -0
- package/dist/building/code-profile.d.ts +231 -0
- package/dist/building/code-profile.d.ts.map +1 -0
- package/dist/building/code-profile.js +208 -0
- package/dist/building/corner-window.d.ts +2 -2
- package/dist/building/corner-window.d.ts.map +1 -1
- package/dist/building/corner-window.js +11 -2
- package/dist/building/door-types.d.ts +1 -0
- package/dist/building/door-types.d.ts.map +1 -1
- package/dist/building/door-types.js +2 -0
- package/dist/building/floor-item-fit.d.ts.map +1 -1
- package/dist/building/floor-item-fit.js +2 -4
- package/dist/building/ground-level.d.ts +1 -3
- package/dist/building/ground-level.d.ts.map +1 -1
- package/dist/building/ground-level.js +3 -5
- package/dist/building/index.d.ts +2 -0
- package/dist/building/index.d.ts.map +1 -1
- package/dist/building/index.js +2 -0
- package/dist/building/level-duplication.d.ts +4667 -3065
- package/dist/building/level-duplication.d.ts.map +1 -1
- package/dist/building/level-insertion.d.ts +1 -1
- package/dist/building/level-wall-copy.d.ts +24 -0
- package/dist/building/level-wall-copy.d.ts.map +1 -0
- package/dist/building/level-wall-copy.js +53 -0
- package/dist/building/merge-windows.d.ts.map +1 -1
- package/dist/building/merge-windows.js +5 -3
- package/dist/building/opening-style-presets.d.ts +2 -0
- package/dist/building/opening-style-presets.d.ts.map +1 -1
- package/dist/building/opening-style-presets.js +26 -5
- package/dist/building/room-use.d.ts +26 -0
- package/dist/building/room-use.d.ts.map +1 -0
- package/dist/building/room-use.js +102 -0
- package/dist/building/wall-openings.d.ts +134 -12
- package/dist/building/wall-openings.d.ts.map +1 -1
- package/dist/building/wall-openings.js +182 -41
- package/dist/building/window-types.d.ts +1 -0
- package/dist/building/window-types.d.ts.map +1 -1
- package/dist/building/window-types.js +3 -0
- package/dist/coherence/__fixtures__/enclosure-scenes.d.ts +84 -0
- package/dist/coherence/__fixtures__/enclosure-scenes.d.ts.map +1 -0
- package/dist/coherence/__fixtures__/enclosure-scenes.js +44 -0
- package/dist/commands/structure/attached-walls.d.ts +16 -0
- package/dist/commands/structure/attached-walls.d.ts.map +1 -0
- package/dist/commands/structure/attached-walls.js +53 -0
- package/dist/commands/structure/index.d.ts +3 -0
- package/dist/commands/structure/index.d.ts.map +1 -1
- package/dist/commands/structure/index.js +3 -0
- package/dist/commands/structure/move-separators.d.ts +62 -0
- package/dist/commands/structure/move-separators.d.ts.map +1 -0
- package/dist/commands/structure/move-separators.js +346 -0
- package/dist/commands/structure/set-floor-foundation.d.ts +5 -0
- package/dist/commands/structure/set-floor-foundation.d.ts.map +1 -1
- package/dist/commands/structure/set-wall-geometry.d.ts.map +1 -1
- package/dist/commands/structure/set-wall-geometry.js +44 -11
- package/dist/commands/structure/set-zone-intent.d.ts +26 -0
- package/dist/commands/structure/set-zone-intent.d.ts.map +1 -1
- package/dist/commands/structure/set-zone-intent.js +107 -2
- package/dist/commands/structure/shared.d.ts +889 -88
- package/dist/commands/structure/shared.d.ts.map +1 -1
- package/dist/commands/structure/wall-overlap.d.ts +45 -0
- package/dist/commands/structure/wall-overlap.d.ts.map +1 -0
- package/dist/commands/structure/wall-overlap.js +289 -0
- package/dist/commands/structure/zone-wall-merge.d.ts +2897 -1938
- package/dist/commands/structure/zone-wall-merge.d.ts.map +1 -1
- package/dist/contracts/reference-discovery.d.ts +2 -2
- package/dist/contracts/reference-discovery.d.ts.map +1 -1
- package/dist/contracts/reference-discovery.js +5 -3
- package/dist/contracts/reference-inventory.d.ts.map +1 -1
- package/dist/contracts/reference-inventory.js +75 -25
- package/dist/events/bus.d.ts +15 -2
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/built-in-floor-placed.d.ts +40 -0
- package/dist/hooks/spatial-grid/built-in-floor-placed.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/built-in-floor-placed.js +220 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +13 -7
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +62 -2
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +207 -36
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +2915 -2114
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +32 -5
- package/dist/hooks/spatial-grid/support-host-patch.js +10 -10
- package/dist/i18n/format.d.ts +41 -0
- package/dist/i18n/format.d.ts.map +1 -0
- package/dist/i18n/format.js +124 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/registry.d.ts +78 -0
- package/dist/i18n/registry.d.ts.map +1 -0
- package/dist/i18n/registry.js +252 -0
- package/dist/i18n/use-translate.d.ts +16 -0
- package/dist/i18n/use-translate.d.ts.map +1 -0
- package/dist/i18n/use-translate.js +23 -0
- package/dist/index.d.ts +43 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -19
- package/dist/lib/__fixtures__/mezzanine.d.ts +10900 -8225
- package/dist/lib/__fixtures__/mezzanine.d.ts.map +1 -1
- package/dist/lib/absorb-wall-separators.d.ts +1629 -828
- package/dist/lib/absorb-wall-separators.d.ts.map +1 -1
- package/dist/lib/beam.d.ts +137 -0
- package/dist/lib/beam.d.ts.map +1 -0
- package/dist/lib/beam.js +297 -0
- package/dist/lib/below-grade-footprints.d.ts +13 -5
- package/dist/lib/below-grade-footprints.d.ts.map +1 -1
- package/dist/lib/below-grade-footprints.js +53 -10
- package/dist/lib/build-mode.d.ts +11 -0
- package/dist/lib/build-mode.d.ts.map +1 -0
- package/dist/lib/build-mode.js +24 -0
- package/dist/lib/build-up.d.ts +89 -0
- package/dist/lib/build-up.d.ts.map +1 -0
- package/dist/lib/build-up.js +216 -0
- package/dist/lib/corner-pair.d.ts +207 -4
- package/dist/lib/corner-pair.d.ts.map +1 -1
- package/dist/lib/deck-boards.d.ts +80 -0
- package/dist/lib/deck-boards.d.ts.map +1 -0
- package/dist/lib/deck-boards.js +184 -0
- package/dist/lib/deck-framing.d.ts +150 -0
- package/dist/lib/deck-framing.d.ts.map +1 -0
- package/dist/lib/deck-framing.js +515 -0
- package/dist/lib/deepest-drawn.d.ts +3 -6
- package/dist/lib/deepest-drawn.d.ts.map +1 -1
- package/dist/lib/deepest-drawn.js +4 -8
- package/dist/lib/door-interaction.d.ts +14 -0
- package/dist/lib/door-interaction.d.ts.map +1 -0
- package/dist/lib/door-interaction.js +62 -0
- package/dist/lib/door-opening.d.ts +19 -0
- package/dist/lib/door-opening.d.ts.map +1 -0
- package/dist/lib/door-opening.js +27 -0
- package/dist/lib/door-operation.d.ts +2 -0
- package/dist/lib/door-operation.d.ts.map +1 -1
- package/dist/lib/door-operation.js +4 -0
- package/dist/lib/elevator-support.d.ts +15 -0
- package/dist/lib/elevator-support.d.ts.map +1 -0
- package/dist/lib/elevator-support.js +40 -0
- package/dist/lib/exterior-wall-loops.d.ts.map +1 -1
- package/dist/lib/exterior-wall-loops.js +2 -1
- package/dist/lib/finish-script.d.ts +5 -5
- package/dist/lib/finish-script.d.ts.map +1 -1
- package/dist/lib/finish-script.js +12 -5
- package/dist/lib/floor-footprint-name.d.ts +17 -0
- package/dist/lib/floor-footprint-name.d.ts.map +1 -1
- package/dist/lib/floor-footprint-name.js +17 -4
- package/dist/lib/floor-intent-changes.d.ts.map +1 -1
- package/dist/lib/floor-intent-changes.js +66 -39
- package/dist/lib/floor-opening-footprints.d.ts +1 -1
- package/dist/lib/floor-opening-footprints.d.ts.map +1 -1
- package/dist/lib/floor-opening-footprints.js +10 -4
- package/dist/lib/floor-plate-paint.d.ts +2 -0
- package/dist/lib/floor-plate-paint.d.ts.map +1 -1
- package/dist/lib/floor-plate-paint.js +12 -0
- package/dist/lib/floor-plates.d.ts +250 -0
- package/dist/lib/floor-plates.d.ts.map +1 -1
- package/dist/lib/floor-plates.js +43 -4
- package/dist/lib/floor-script.d.ts +35 -6
- package/dist/lib/floor-script.d.ts.map +1 -1
- package/dist/lib/floor-script.js +96 -16
- package/dist/lib/foundation-construction.d.ts +4 -13
- package/dist/lib/foundation-construction.d.ts.map +1 -1
- package/dist/lib/foundation-construction.js +4 -14
- package/dist/lib/foundation-standard.d.ts +2 -3
- package/dist/lib/foundation-standard.d.ts.map +1 -1
- package/dist/lib/foundation-standard.js +2 -3
- package/dist/lib/foundation-wall.d.ts +1 -2
- package/dist/lib/foundation-wall.d.ts.map +1 -1
- package/dist/lib/foundation-wall.js +1 -2
- package/dist/lib/framing-post.d.ts +91 -0
- package/dist/lib/framing-post.d.ts.map +1 -0
- package/dist/lib/framing-post.js +174 -0
- package/dist/lib/geometry-script-node.d.ts +11 -0
- package/dist/lib/geometry-script-node.d.ts.map +1 -1
- package/dist/lib/geometry-script-node.js +35 -0
- package/dist/lib/grade-standing.d.ts +16 -2
- package/dist/lib/grade-standing.d.ts.map +1 -1
- package/dist/lib/grade-standing.js +129 -9
- package/dist/lib/ground-seat.d.ts +2 -22
- package/dist/lib/ground-seat.d.ts.map +1 -1
- package/dist/lib/ground-seat.js +2 -17
- package/dist/lib/ground-window.d.ts +2 -1
- package/dist/lib/ground-window.d.ts.map +1 -1
- package/dist/lib/ground-window.js +5 -2
- package/dist/lib/hierarchy.d.ts +19 -0
- package/dist/lib/hierarchy.d.ts.map +1 -0
- package/dist/lib/hierarchy.js +53 -0
- package/dist/lib/level-footprints.d.ts +2 -0
- package/dist/lib/level-footprints.d.ts.map +1 -1
- package/dist/lib/level-footprints.js +7 -0
- package/dist/lib/opening-casing.d.ts +67 -0
- package/dist/lib/opening-casing.d.ts.map +1 -0
- package/dist/lib/opening-casing.js +131 -0
- package/dist/lib/opening-exterior-face.d.ts +6 -0
- package/dist/lib/opening-exterior-face.d.ts.map +1 -0
- package/dist/lib/opening-exterior-face.js +15 -0
- package/dist/lib/opening-floor-datum.d.ts +11 -5
- package/dist/lib/opening-floor-datum.d.ts.map +1 -1
- package/dist/lib/opening-floor-datum.js +40 -8
- package/dist/lib/opening-frame-depth.d.ts +18 -0
- package/dist/lib/opening-frame-depth.d.ts.map +1 -0
- package/dist/lib/opening-frame-depth.js +22 -0
- package/dist/lib/outdoor-room.d.ts +32 -0
- package/dist/lib/outdoor-room.d.ts.map +1 -0
- package/dist/lib/outdoor-room.js +66 -0
- package/dist/lib/own-floor-intent.d.ts.map +1 -1
- package/dist/lib/own-floor-intent.js +2 -2
- package/dist/lib/plate-surface.d.ts +8 -0
- package/dist/lib/plate-surface.d.ts.map +1 -1
- package/dist/lib/plate-surface.js +53 -3
- package/dist/lib/polygon-boolean.d.ts.map +1 -1
- package/dist/lib/polygon-boolean.js +2 -1
- package/dist/lib/porch-framing-sync.d.ts +46 -0
- package/dist/lib/porch-framing-sync.d.ts.map +1 -0
- package/dist/lib/porch-framing-sync.js +206 -0
- package/dist/lib/porch-framing.d.ts +44 -0
- package/dist/lib/porch-framing.d.ts.map +1 -0
- package/dist/lib/porch-framing.js +159 -0
- package/dist/lib/railing-rooms.d.ts +13 -0
- package/dist/lib/railing-rooms.d.ts.map +1 -0
- package/dist/lib/railing-rooms.js +37 -0
- package/dist/lib/roof-eave.d.ts +135 -0
- package/dist/lib/roof-eave.d.ts.map +1 -0
- package/dist/lib/roof-eave.js +186 -0
- package/dist/lib/room-floor-elevation.d.ts +12 -0
- package/dist/lib/room-floor-elevation.d.ts.map +1 -0
- package/dist/lib/room-floor-elevation.js +41 -0
- package/dist/lib/room-floor-feasibility.d.ts.map +1 -1
- package/dist/lib/room-floor-feasibility.js +1 -1
- package/dist/lib/room-floor-plate.d.ts +3 -1
- package/dist/lib/room-floor-plate.d.ts.map +1 -1
- package/dist/lib/room-floor-plate.js +7 -0
- package/dist/lib/space-detection.d.ts +13 -0
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +113 -24
- package/dist/lib/structure-kernel.d.ts +22 -9
- package/dist/lib/structure-kernel.d.ts.map +1 -1
- package/dist/lib/structure-kernel.js +146 -28
- package/dist/lib/structure-reconcile.d.ts +1 -1
- package/dist/lib/structure-reconcile.d.ts.map +1 -1
- package/dist/lib/structure-reconcile.js +7 -4
- package/dist/lib/surface-intent.d.ts +10 -0
- package/dist/lib/surface-intent.d.ts.map +1 -0
- package/dist/lib/surface-intent.js +18 -0
- package/dist/lib/terrain-source.d.ts.map +1 -1
- package/dist/lib/terrain-source.js +2 -1
- package/dist/lib/wall-face-script.d.ts.map +1 -1
- package/dist/lib/wall-face-script.js +8 -5
- package/dist/lib/window-interaction.d.ts +16 -0
- package/dist/lib/window-interaction.d.ts.map +1 -0
- package/dist/lib/window-interaction.js +82 -0
- package/dist/lib/zone-quantities.js +1 -1
- package/dist/material-kinds.d.ts +1 -1
- package/dist/material-kinds.d.ts.map +1 -1
- package/dist/material-kinds.js +1 -0
- package/dist/material-library.d.ts +15 -0
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +458 -59
- package/dist/node-slots/column.d.ts +5 -0
- package/dist/node-slots/column.d.ts.map +1 -1
- package/dist/node-slots/column.js +10 -0
- package/dist/node-slots/door.d.ts +3 -4
- package/dist/node-slots/door.d.ts.map +1 -1
- package/dist/node-slots/door.js +4 -3
- package/dist/node-slots/wall.d.ts.map +1 -1
- package/dist/node-slots/wall.js +21 -3
- package/dist/node-slots/window.d.ts +2 -2
- package/dist/node-slots/window.d.ts.map +1 -1
- package/dist/node-slots/window.js +6 -1
- package/dist/procedural-items/design.d.ts +8 -0
- package/dist/procedural-items/design.d.ts.map +1 -1
- package/dist/procedural-items/design.js +169 -21
- package/dist/procedural-items/motion-controller.d.ts +2 -0
- package/dist/procedural-items/motion-controller.d.ts.map +1 -1
- package/dist/procedural-items/motion-controller.js +2 -1
- package/dist/registry/index.d.ts +3 -3
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +2 -2
- package/dist/registry/mechanism.d.ts +4 -1
- package/dist/registry/mechanism.d.ts.map +1 -1
- package/dist/registry/mechanism.js +10 -0
- package/dist/registry/registry.d.ts +5 -0
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +10 -0
- package/dist/registry/types.d.ts +89 -23
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/runtime/__fixtures__/live-scene.d.ts +14 -0
- package/dist/runtime/__fixtures__/live-scene.d.ts.map +1 -0
- package/dist/runtime/__fixtures__/live-scene.js +64 -0
- package/dist/runtime/apply.d.ts +42 -0
- package/dist/runtime/apply.d.ts.map +1 -0
- package/dist/runtime/apply.js +274 -0
- package/dist/runtime/dependencies.d.ts +97 -0
- package/dist/runtime/dependencies.d.ts.map +1 -0
- package/dist/runtime/dependencies.js +42 -0
- package/dist/runtime/ids.d.ts +11 -0
- package/dist/runtime/ids.d.ts.map +1 -0
- package/dist/runtime/ids.js +80 -0
- package/dist/runtime/index.d.ts +14 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +11 -0
- package/dist/runtime/node-queries.d.ts +16 -0
- package/dist/runtime/node-queries.d.ts.map +1 -0
- package/dist/runtime/node-queries.js +30 -0
- package/dist/runtime/patch.d.ts +26 -0
- package/dist/runtime/patch.d.ts.map +1 -0
- package/dist/runtime/patch.js +165 -0
- package/dist/runtime/reader.d.ts +104 -0
- package/dist/runtime/reader.d.ts.map +1 -0
- package/dist/runtime/reader.js +455 -0
- package/dist/runtime/scene-runtime.d.ts +112 -0
- package/dist/runtime/scene-runtime.d.ts.map +1 -0
- package/dist/runtime/scene-runtime.js +302 -0
- package/dist/schema/__fixtures__/node-fixtures.d.ts.map +1 -1
- package/dist/schema/__fixtures__/node-fixtures.js +1 -0
- package/dist/schema/assembly.d.ts +3 -0
- package/dist/schema/assembly.d.ts.map +1 -1
- package/dist/schema/assembly.js +13 -0
- package/dist/schema/asset-url.d.ts +37 -2
- package/dist/schema/asset-url.d.ts.map +1 -1
- package/dist/schema/asset-url.js +80 -11
- package/dist/schema/base.d.ts +8 -0
- package/dist/schema/base.d.ts.map +1 -1
- package/dist/schema/base.js +19 -1
- package/dist/schema/build-up.d.ts +247 -0
- package/dist/schema/build-up.d.ts.map +1 -0
- package/dist/schema/build-up.js +83 -0
- package/dist/schema/geometry-source.d.ts +80 -0
- package/dist/schema/geometry-source.d.ts.map +1 -1
- package/dist/schema/geometry-source.js +38 -0
- package/dist/schema/index.d.ts +12 -7
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +12 -6
- package/dist/schema/material.d.ts +5 -0
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +6 -0
- package/dist/schema/nodes/beam.d.ts +264 -0
- package/dist/schema/nodes/beam.d.ts.map +1 -0
- package/dist/schema/nodes/beam.js +139 -0
- package/dist/schema/nodes/box-vent.d.ts +5 -0
- package/dist/schema/nodes/box-vent.d.ts.map +1 -1
- package/dist/schema/nodes/cabinet.d.ts +10 -0
- package/dist/schema/nodes/cabinet.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.d.ts +48 -0
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +5 -0
- package/dist/schema/nodes/chimney.d.ts +10 -0
- package/dist/schema/nodes/chimney.d.ts.map +1 -1
- package/dist/schema/nodes/column.d.ts +42 -0
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +11 -1
- package/dist/schema/nodes/cupola.d.ts +5 -0
- package/dist/schema/nodes/cupola.d.ts.map +1 -1
- package/dist/schema/nodes/door.d.ts +54 -3
- package/dist/schema/nodes/door.d.ts.map +1 -1
- package/dist/schema/nodes/door.js +41 -7
- package/dist/schema/nodes/dormer.d.ts +20 -0
- package/dist/schema/nodes/dormer.d.ts.map +1 -1
- package/dist/schema/nodes/downspout.d.ts +5 -0
- package/dist/schema/nodes/downspout.d.ts.map +1 -1
- package/dist/schema/nodes/elevator.d.ts +5 -0
- package/dist/schema/nodes/elevator.d.ts.map +1 -1
- package/dist/schema/nodes/eyebrow-vent.d.ts +5 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -1
- package/dist/schema/nodes/fence.d.ts +5 -0
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/guide.d.ts +14 -0
- package/dist/schema/nodes/guide.d.ts.map +1 -1
- package/dist/schema/nodes/guide.js +2 -0
- package/dist/schema/nodes/gutter.d.ts +5 -0
- package/dist/schema/nodes/gutter.d.ts.map +1 -1
- package/dist/schema/nodes/item.d.ts +71 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +5 -1
- package/dist/schema/nodes/level.d.ts +1 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/opening-types.d.ts +13 -0
- package/dist/schema/nodes/opening-types.d.ts.map +1 -1
- package/dist/schema/nodes/opening-types.js +8 -1
- package/dist/schema/nodes/railing-wall.d.ts +112 -0
- package/dist/schema/nodes/railing-wall.d.ts.map +1 -0
- package/dist/schema/nodes/railing-wall.js +160 -0
- package/dist/schema/nodes/ridge-vent.d.ts +5 -0
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-resize.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-resize.js +2 -2
- package/dist/schema/nodes/roof-segment.d.ts +226 -1
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +97 -12
- package/dist/schema/nodes/roof-types.d.ts +11 -0
- package/dist/schema/nodes/roof-types.d.ts.map +1 -1
- package/dist/schema/nodes/roof-types.js +6 -0
- package/dist/schema/nodes/roof.d.ts +60 -0
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +4 -0
- package/dist/schema/nodes/shelf.d.ts +5 -0
- package/dist/schema/nodes/shelf.d.ts.map +1 -1
- package/dist/schema/nodes/site.d.ts +80 -0
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/site.js +7 -0
- package/dist/schema/nodes/skylight.d.ts +10 -0
- package/dist/schema/nodes/skylight.d.ts.map +1 -1
- package/dist/schema/nodes/slab.d.ts +137 -0
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +3 -0
- package/dist/schema/nodes/solar-panel.d.ts +10 -0
- package/dist/schema/nodes/solar-panel.d.ts.map +1 -1
- package/dist/schema/nodes/stair-segment.d.ts +5 -0
- package/dist/schema/nodes/stair-segment.d.ts.map +1 -1
- package/dist/schema/nodes/stair-structure.d.ts +69 -0
- package/dist/schema/nodes/stair-structure.d.ts.map +1 -0
- package/dist/schema/nodes/stair-structure.js +39 -0
- package/dist/schema/nodes/stair.d.ts +46 -0
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +3 -0
- package/dist/schema/nodes/turbine-vent.d.ts +5 -0
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -1
- package/dist/schema/nodes/wall.d.ts +136 -2
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +48 -6
- package/dist/schema/nodes/window.d.ts +66 -1
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +50 -5
- package/dist/schema/nodes/zone.d.ts +68 -0
- package/dist/schema/nodes/zone.d.ts.map +1 -1
- package/dist/schema/nodes/zone.js +32 -0
- package/dist/schema/scene-material.d.ts +5 -0
- package/dist/schema/scene-material.d.ts.map +1 -1
- package/dist/schema/types.d.ts +1080 -4
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +2 -0
- package/dist/schema/wall-type.d.ts +95 -0
- package/dist/schema/wall-type.d.ts.map +1 -0
- package/dist/schema/wall-type.js +32 -0
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/movement.d.ts +7 -0
- package/dist/services/movement.d.ts.map +1 -1
- package/dist/services/movement.js +13 -0
- package/dist/services/port-connectivity.d.ts +15 -6
- package/dist/services/port-connectivity.d.ts.map +1 -1
- package/dist/services/port-connectivity.js +83 -17
- package/dist/services/roof-bearing.d.ts.map +1 -1
- package/dist/services/roof-bearing.js +8 -4
- package/dist/services/roof-level.d.ts.map +1 -1
- package/dist/services/roof-level.js +4 -3
- package/dist/services/roof-move.d.ts.map +1 -1
- package/dist/services/roof-move.js +13 -7
- package/dist/services/storey.d.ts +6 -1
- package/dist/services/storey.d.ts.map +1 -1
- package/dist/services/storey.js +24 -10
- package/dist/spatial/usage-field/beds.d.ts +51 -0
- package/dist/spatial/usage-field/beds.d.ts.map +1 -0
- package/dist/spatial/usage-field/beds.js +346 -0
- package/dist/spatial/usage-field/blocked.d.ts +28 -0
- package/dist/spatial/usage-field/blocked.d.ts.map +1 -0
- package/dist/spatial/usage-field/blocked.js +185 -0
- package/dist/spatial/usage-field/boxes.d.ts +17 -0
- package/dist/spatial/usage-field/boxes.d.ts.map +1 -0
- package/dist/spatial/usage-field/boxes.js +82 -0
- package/dist/spatial/usage-field/catalogue-shapes.d.ts +3 -0
- package/dist/spatial/usage-field/catalogue-shapes.d.ts.map +1 -0
- package/dist/spatial/usage-field/catalogue-shapes.js +257 -0
- package/dist/spatial/usage-field/catalogue.d.ts +6 -0
- package/dist/spatial/usage-field/catalogue.d.ts.map +1 -0
- package/dist/spatial/usage-field/catalogue.js +11 -0
- package/dist/spatial/usage-field/clash.d.ts +40 -0
- package/dist/spatial/usage-field/clash.d.ts.map +1 -0
- package/dist/spatial/usage-field/clash.js +187 -0
- package/dist/spatial/usage-field/door-stairs.d.ts +18 -0
- package/dist/spatial/usage-field/door-stairs.d.ts.map +1 -0
- package/dist/spatial/usage-field/door-stairs.js +129 -0
- package/dist/spatial/usage-field/entry-way.d.ts +14 -0
- package/dist/spatial/usage-field/entry-way.d.ts.map +1 -0
- package/dist/spatial/usage-field/entry-way.js +219 -0
- package/dist/spatial/usage-field/fences.d.ts +18 -0
- package/dist/spatial/usage-field/fences.d.ts.map +1 -0
- package/dist/spatial/usage-field/fences.js +253 -0
- package/dist/spatial/usage-field/field.d.ts +44 -0
- package/dist/spatial/usage-field/field.d.ts.map +1 -0
- package/dist/spatial/usage-field/field.js +145 -0
- package/dist/spatial/usage-field/findings.d.ts +76 -0
- package/dist/spatial/usage-field/findings.d.ts.map +1 -0
- package/dist/spatial/usage-field/findings.js +252 -0
- package/dist/spatial/usage-field/fixtures.d.ts +18 -0
- package/dist/spatial/usage-field/fixtures.d.ts.map +1 -0
- package/dist/spatial/usage-field/fixtures.js +160 -0
- package/dist/spatial/usage-field/flag.d.ts +10 -0
- package/dist/spatial/usage-field/flag.d.ts.map +1 -0
- package/dist/spatial/usage-field/flag.js +12 -0
- package/dist/spatial/usage-field/floors.d.ts +13 -0
- package/dist/spatial/usage-field/floors.d.ts.map +1 -0
- package/dist/spatial/usage-field/floors.js +36 -0
- package/dist/spatial/usage-field/index.d.ts +7 -0
- package/dist/spatial/usage-field/index.d.ts.map +1 -0
- package/dist/spatial/usage-field/index.js +5 -0
- package/dist/spatial/usage-field/items.d.ts +38 -0
- package/dist/spatial/usage-field/items.d.ts.map +1 -0
- package/dist/spatial/usage-field/items.js +261 -0
- package/dist/spatial/usage-field/labels.d.ts +8 -0
- package/dist/spatial/usage-field/labels.d.ts.map +1 -0
- package/dist/spatial/usage-field/labels.js +14 -0
- package/dist/spatial/usage-field/level-field.d.ts +47 -0
- package/dist/spatial/usage-field/level-field.d.ts.map +1 -0
- package/dist/spatial/usage-field/level-field.js +96 -0
- package/dist/spatial/usage-field/measure.d.ts +324 -0
- package/dist/spatial/usage-field/measure.d.ts.map +1 -0
- package/dist/spatial/usage-field/measure.js +458 -0
- package/dist/spatial/usage-field/obstacles.d.ts +63 -0
- package/dist/spatial/usage-field/obstacles.d.ts.map +1 -0
- package/dist/spatial/usage-field/obstacles.js +215 -0
- package/dist/spatial/usage-field/openings.d.ts +12 -0
- package/dist/spatial/usage-field/openings.d.ts.map +1 -0
- package/dist/spatial/usage-field/openings.js +162 -0
- package/dist/spatial/usage-field/path.d.ts +48 -0
- package/dist/spatial/usage-field/path.d.ts.map +1 -0
- package/dist/spatial/usage-field/path.js +354 -0
- package/dist/spatial/usage-field/pieces.d.ts +27 -0
- package/dist/spatial/usage-field/pieces.d.ts.map +1 -0
- package/dist/spatial/usage-field/pieces.js +89 -0
- package/dist/spatial/usage-field/shapes.d.ts +18 -0
- package/dist/spatial/usage-field/shapes.d.ts.map +1 -0
- package/dist/spatial/usage-field/shapes.js +92 -0
- package/dist/spatial/usage-field/stairs.d.ts +23 -0
- package/dist/spatial/usage-field/stairs.d.ts.map +1 -0
- package/dist/spatial/usage-field/stairs.js +163 -0
- package/dist/spatial/usage-field/support.d.ts +24 -0
- package/dist/spatial/usage-field/support.d.ts.map +1 -0
- package/dist/spatial/usage-field/support.js +46 -0
- package/dist/spatial/usage-field/swings.d.ts +847 -0
- package/dist/spatial/usage-field/swings.d.ts.map +1 -0
- package/dist/spatial/usage-field/swings.js +427 -0
- package/dist/spatial/usage-field/types.d.ts +65 -0
- package/dist/spatial/usage-field/types.d.ts.map +1 -0
- package/dist/spatial/usage-field/types.js +1 -0
- package/dist/spatial/usage-field/walls.d.ts +1059 -0
- package/dist/spatial/usage-field/walls.d.ts.map +1 -0
- package/dist/spatial/usage-field/walls.js +180 -0
- package/dist/spatial/usage-field/way-in.d.ts +26 -0
- package/dist/spatial/usage-field/way-in.d.ts.map +1 -0
- package/dist/spatial/usage-field/way-in.js +113 -0
- package/dist/store/actions/node-actions.d.ts +1467 -664
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +65 -21
- package/dist/store/derived-node-guard.d.ts +10 -2
- package/dist/store/derived-node-guard.d.ts.map +1 -1
- package/dist/store/derived-node-guard.js +33 -12
- package/dist/store/document-settling.d.ts +9 -0
- package/dist/store/document-settling.d.ts.map +1 -0
- package/dist/store/document-settling.js +22 -0
- package/dist/store/history-control.d.ts +19 -10
- package/dist/store/history-control.d.ts.map +1 -1
- package/dist/store/history-control.js +176 -127
- package/dist/store/history-drafts.d.ts.map +1 -1
- package/dist/store/history-drafts.js +21 -17
- package/dist/store/live-previews.d.ts +9 -0
- package/dist/store/live-previews.d.ts.map +1 -0
- package/dist/store/live-previews.js +19 -0
- package/dist/store/scene-annotations.d.ts +5409 -3807
- package/dist/store/scene-annotations.d.ts.map +1 -1
- package/dist/store/scene-environment.d.ts +33 -0
- package/dist/store/scene-environment.d.ts.map +1 -0
- package/dist/store/scene-environment.js +54 -0
- package/dist/store/scene-hydration.d.ts.map +1 -1
- package/dist/store/scene-hydration.js +41 -30
- package/dist/store/use-interactive.d.ts +9 -3
- package/dist/store/use-interactive.d.ts.map +1 -1
- package/dist/store/use-interactive.js +43 -11
- package/dist/store/use-live-node-overrides.d.ts.map +1 -1
- package/dist/store/use-live-node-overrides.js +3 -0
- package/dist/store/use-scene.d.ts +25 -0
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +408 -336
- package/dist/systems/document-reconcile.d.ts +40 -0
- package/dist/systems/document-reconcile.d.ts.map +1 -0
- package/dist/systems/document-reconcile.js +159 -0
- package/dist/systems/elevator/elevator-geometry.d.ts +25 -0
- package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-geometry.js +40 -8
- package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-opening-sync.js +4 -35
- package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-opening-system.js +9 -24
- package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-runtime.js +95 -18
- package/dist/systems/elevator/elevator-service.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-service.js +24 -4
- package/dist/systems/fence/guard-layout.d.ts +69 -0
- package/dist/systems/fence/guard-layout.d.ts.map +1 -0
- package/dist/systems/fence/guard-layout.js +194 -0
- package/dist/systems/owned-floor-openings.d.ts +13 -1
- package/dist/systems/owned-floor-openings.d.ts.map +1 -1
- package/dist/systems/owned-floor-openings.js +58 -2
- package/dist/systems/reconcile-owned-floor-openings.d.ts +3 -1
- package/dist/systems/reconcile-owned-floor-openings.d.ts.map +1 -1
- package/dist/systems/reconcile-owned-floor-openings.js +4 -4
- package/dist/systems/roof/roof-elevation-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-elevation-system.js +8 -26
- package/dist/systems/roof/roof-elevation.d.ts +16 -1
- package/dist/systems/roof/roof-elevation.d.ts.map +1 -1
- package/dist/systems/roof/roof-elevation.js +87 -5
- package/dist/systems/roof/roof-footprint.d.ts +42 -1
- package/dist/systems/roof/roof-footprint.d.ts.map +1 -1
- package/dist/systems/roof/roof-footprint.js +160 -4
- package/dist/systems/slab/__fixtures__/floor-step.d.ts +2714 -1481
- package/dist/systems/slab/__fixtures__/floor-step.d.ts.map +1 -1
- package/dist/systems/slab/__fixtures__/raised-room.d.ts +3815 -2565
- package/dist/systems/slab/__fixtures__/raised-room.d.ts.map +1 -1
- package/dist/systems/stair/stair-clearance.d.ts +7 -0
- package/dist/systems/stair/stair-clearance.d.ts.map +1 -1
- package/dist/systems/stair/stair-clearance.js +31 -11
- package/dist/systems/stair/stair-construction.d.ts +8 -3
- package/dist/systems/stair/stair-construction.d.ts.map +1 -1
- package/dist/systems/stair/stair-construction.js +55 -4
- package/dist/systems/stair/stair-opening-preview.d.ts +1630 -829
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-system.js +18 -34
- package/dist/systems/stair/stair-presets.d.ts +36 -0
- package/dist/systems/stair/stair-presets.d.ts.map +1 -1
- package/dist/systems/stair/stair-rise-query.d.ts.map +1 -1
- package/dist/systems/stair/stair-rise-query.js +3 -2
- package/dist/systems/stair/stair-rise.d.ts.map +1 -1
- package/dist/systems/stair/stair-rise.js +6 -0
- package/dist/systems/stair/stair-sizing.d.ts +42 -11
- package/dist/systems/stair/stair-sizing.d.ts.map +1 -1
- package/dist/systems/stair/stair-sizing.js +10 -1
- package/dist/systems/stair/stair-structure.d.ts +277 -0
- package/dist/systems/stair/stair-structure.d.ts.map +1 -0
- package/dist/systems/stair/stair-structure.js +496 -0
- package/dist/systems/wall/wall-assembly.d.ts +84 -6
- package/dist/systems/wall/wall-assembly.d.ts.map +1 -1
- package/dist/systems/wall/wall-assembly.js +204 -5
- package/dist/systems/wall/wall-curve.d.ts +10 -0
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +38 -0
- package/dist/systems/wall/wall-finish.d.ts +38 -2
- package/dist/systems/wall/wall-finish.d.ts.map +1 -1
- package/dist/systems/wall/wall-finish.js +70 -2
- package/dist/systems/wall/wall-footprint.js +2 -2
- package/dist/systems/wall/wall-frame.d.ts.map +1 -1
- package/dist/systems/wall/wall-frame.js +8 -0
- package/dist/systems/wall/wall-merge.d.ts +9 -1
- package/dist/systems/wall/wall-merge.d.ts.map +1 -1
- package/dist/systems/wall/wall-merge.js +66 -13
- package/dist/systems/wall/wall-mitering.d.ts +1 -0
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +152 -52
- package/dist/systems/wall/wall-move.d.ts +23 -6
- package/dist/systems/wall/wall-move.d.ts.map +1 -1
- package/dist/systems/wall/wall-move.js +65 -0
- package/dist/systems/wall/wall-top.d.ts +11 -4
- package/dist/systems/wall/wall-top.d.ts.map +1 -1
- package/dist/systems/wall/wall-top.js +28 -7
- package/dist/systems/wall/wall-topology.d.ts +32 -1
- package/dist/systems/wall/wall-topology.d.ts.map +1 -1
- package/dist/systems/wall/wall-topology.js +137 -11
- package/dist/systems/wall/wall-trim-profiles.d.ts +12 -0
- package/dist/systems/wall/wall-trim-profiles.d.ts.map +1 -0
- package/dist/systems/wall/wall-trim-profiles.js +191 -0
- package/dist/utils/ensure-scene-openings.d.ts +4663 -3061
- package/dist/utils/ensure-scene-openings.d.ts.map +1 -1
- package/dist/utils/floor-piece-adoption.d.ts +5 -0
- package/dist/utils/floor-piece-adoption.d.ts.map +1 -1
- package/dist/utils/floor-plate-migration.d.ts +3372 -2571
- package/dist/utils/floor-plate-migration.d.ts.map +1 -1
- package/dist/utils/measurement-format.d.ts +27 -0
- package/dist/utils/measurement-format.d.ts.map +1 -0
- package/dist/utils/measurement-format.js +143 -0
- package/dist/utils/owned-floor-opening-migration.d.ts +1629 -828
- package/dist/utils/owned-floor-opening-migration.d.ts.map +1 -1
- package/dist/utils/reconcile-structure-on-load.d.ts +2308 -1507
- package/dist/utils/reconcile-structure-on-load.d.ts.map +1 -1
- package/dist/utils/structure-id.d.ts.map +1 -1
- package/dist/utils/structure-id.js +4 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -1
- package/dist/validation/validate-build-json.js +3 -1
- package/package.json +22 -5
- package/dist/lib/generic-foundation.d.ts +0 -40
- package/dist/lib/generic-foundation.d.ts.map +0 -1
- package/dist/lib/generic-foundation.js +0 -200
|
@@ -1,32 +1,88 @@
|
|
|
1
1
|
import { refuse } from '../agent-tools/refusal.js';
|
|
2
|
-
import { doorTypeFields } from '../building/door-types.js';
|
|
2
|
+
import { doorTypeFields, NEW_DOOR_FRAME_THICKNESS } from '../building/door-types.js';
|
|
3
3
|
import { DOOR_STYLES, doorStyleLook, getWindowStyleOverrides, WINDOW_STYLES, } from '../building/opening-style-presets.js';
|
|
4
|
-
import {
|
|
4
|
+
import { doorFacing, wallOpeningCeilingOver } from '../building/wall-openings.js';
|
|
5
|
+
import { NEW_WINDOW_FRAME_THICKNESS, windowTakesStyle, windowTypeFields, } from '../building/window-types.js';
|
|
6
|
+
import { cutFloorOpening } from '../commands/structure/floor-opening.js';
|
|
7
|
+
import { doorPositionForFacing, getDoorOpeningSize } from '../lib/door-opening.js';
|
|
5
8
|
import { floorRoomFaces } from '../lib/floor-room-faces.js';
|
|
6
9
|
import { matchScriptSlotsToLibrary, requireMeasured, scriptedSize, scriptSource, } from '../lib/geometry-script-node.js';
|
|
10
|
+
import { exposedIntervals } from '../lib/level-footprints.js';
|
|
11
|
+
import { area, containsPoint, difference, intersection, union, } from '../lib/polygon-boolean.js';
|
|
12
|
+
import { roundFloorElevation } from '../lib/room-floor-feasibility.js';
|
|
7
13
|
import { detectSpacesForLevel } from '../lib/space-detection.js';
|
|
8
14
|
import { reconcileSceneStructure } from '../lib/structure-reconcile.js';
|
|
9
15
|
import { wallFaceScriptInputs } from '../lib/wall-face-script.js';
|
|
16
|
+
import { nodeSlotDeclarations } from '../node-slots/index.js';
|
|
10
17
|
import { nearestLibraryColorRef } from '../procedural-items/library-colors.js';
|
|
11
|
-
import { ColumnNode, DoorNode, LevelNode, SlabNode, WallNode, WindowNode, ZoneNode, } from '../schema/index.js';
|
|
18
|
+
import { ColumnNode, CurtainWallConfig, DoorNode, FenceNode, getActiveRoofHeight, getCurtainWallConfig, getSegmentSlopeFrame, LevelNode, RoofNode, RoofSegmentNode, SlabNode, UnitNode, WallNode, WindowNode, ZoneNode, } from '../schema/index.js';
|
|
12
19
|
import { DoorType, WindowType } from '../schema/nodes/opening-types.js';
|
|
13
|
-
import {
|
|
20
|
+
import { MIN_SLAB_THICKNESS } from '../schema/nodes/slab.js';
|
|
21
|
+
import { DEFAULT_LEVEL_HEIGHT, resolveCeilingHeight } from '../services/level-height.js';
|
|
22
|
+
import { planRoofLevel } from '../services/roof-level.js';
|
|
14
23
|
import { CEILING_DRAW_OFFSET } from '../services/storey.js';
|
|
24
|
+
import { resolveRoofElevation } from '../systems/roof/roof-elevation.js';
|
|
25
|
+
import { stairArrivalOpening } from '../systems/stair/stair-footprint.js';
|
|
26
|
+
import { getMaxWallCurveOffset, getWallCenterlineSegments, getWallCurveLength, getWallSurfacePolygon, isCurvedWall, normalizeWallCurveOffset, } from '../systems/wall/wall-curve.js';
|
|
27
|
+
import { getWallPlanFootprint, getWallThickness } from '../systems/wall/wall-footprint.js';
|
|
15
28
|
import { getWallLocalFaceZ } from '../systems/wall/wall-frame.js';
|
|
29
|
+
import { calculateLevelMiters } from '../systems/wall/wall-mitering.js';
|
|
16
30
|
import { roomFaceCover } from '../systems/wall/wall-room-sides.js';
|
|
17
31
|
import { migrateRoomZones } from '../utils/room-zone-migration.js';
|
|
18
32
|
import { addObject } from './add-object.js';
|
|
33
|
+
import { applySceneChanges } from './apply-changes.js';
|
|
19
34
|
import { authoredSceneChanges } from './authored-scene-merge.js';
|
|
35
|
+
import { createStair } from './create-stair.js';
|
|
20
36
|
import { paint } from './paint.js';
|
|
21
37
|
import { pointInPolygon, projectWorldPointToWallLocalX } from './plan-geometry.js';
|
|
22
|
-
import {
|
|
38
|
+
import { exactColorMaterial, exactScriptSlots, MATTE } from './scene-materials.js';
|
|
39
|
+
import { queryNodes } from './scene-queries.js';
|
|
23
40
|
import { setWallSkirting } from './wall-skirting.js';
|
|
41
|
+
const BOOKKEEPING = ['authorSceneBase', 'authorSceneWritten'];
|
|
42
|
+
/** A value as live collaboration and storage keep it: no undefined, no non-finite number. */
|
|
43
|
+
const plain = (value) => JSON.parse(JSON.stringify(value));
|
|
44
|
+
/** A node without the conversion's bookkeeping: what a person edits and a revision merges. */
|
|
45
|
+
function withoutBookkeeping(node) {
|
|
46
|
+
const metadata = node.metadata;
|
|
47
|
+
if (!(metadata && BOOKKEEPING.some((key) => key in metadata)))
|
|
48
|
+
return node;
|
|
49
|
+
const kept = { ...metadata };
|
|
50
|
+
for (const key of BOOKKEEPING)
|
|
51
|
+
delete kept[key];
|
|
52
|
+
return { ...node, metadata: kept };
|
|
53
|
+
}
|
|
54
|
+
/** A node the person deleted since the last conversion: present in the baseline, never read. */
|
|
55
|
+
const DELETED = { type: 'deleted' };
|
|
56
|
+
/**
|
|
57
|
+
* The last conversion of `building`, read back from its levels and nodes: its seed, its source-key
|
|
58
|
+
* identities, and the baseline of every node it wrote (a deleted one marked so).
|
|
59
|
+
*/
|
|
60
|
+
function authoredBaseline(scene, building) {
|
|
61
|
+
const state = building.metadata?.authorSceneState;
|
|
62
|
+
if (!(state?.version === 2 && state.buildingId === building.id && typeof state.seed === 'string'))
|
|
63
|
+
return undefined;
|
|
64
|
+
const ids = {};
|
|
65
|
+
const nodes = {};
|
|
66
|
+
// The building lists what it holds itself (its units); each level, what stands on it.
|
|
67
|
+
for (const holder of [building, ...queryNodes(scene, { type: 'level', parentId: building.id })]) {
|
|
68
|
+
const written = holder.metadata?.authorSceneWritten;
|
|
69
|
+
if (!written)
|
|
70
|
+
continue;
|
|
71
|
+
Object.assign(ids, written.keys);
|
|
72
|
+
for (const id of written.ids) {
|
|
73
|
+
const node = scene[id];
|
|
74
|
+
nodes[id] = node?.metadata?.authorSceneBase ?? DELETED;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { seed: state.seed, ids, nodes };
|
|
78
|
+
}
|
|
24
79
|
const JOIN_REACH = 0.3;
|
|
80
|
+
/** The deepest floor between storeys: a wall stopping this far under the floor above carries it. */
|
|
81
|
+
const FLOOR_DEPTH = 0.5;
|
|
25
82
|
const THIN_SLAB = 0.02;
|
|
26
83
|
const MANY_SLABS = 20;
|
|
27
84
|
const MANY_REGIONS = 24;
|
|
28
85
|
const HEX = /^#[0-9a-f]{6}$/i;
|
|
29
|
-
const GLASS = 'library:preset-glass';
|
|
30
86
|
/**
|
|
31
87
|
* Explicit payload keys retain their identities across revisions; derived nodes use the
|
|
32
88
|
* conversion's retained seed. A changed payload never remints every existing wall or item.
|
|
@@ -52,6 +108,17 @@ function payloadSeed(payload) {
|
|
|
52
108
|
}
|
|
53
109
|
const dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]);
|
|
54
110
|
const metres = (value) => `${value.toFixed(2)} m`;
|
|
111
|
+
/** The value given most often, the first one on a tie; undefined for none. */
|
|
112
|
+
const mostCommon = (values) => {
|
|
113
|
+
const counts = new Map();
|
|
114
|
+
for (const value of values)
|
|
115
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
116
|
+
let best;
|
|
117
|
+
for (const [value, count] of counts)
|
|
118
|
+
if (!best || count > counts.get(best))
|
|
119
|
+
best = value;
|
|
120
|
+
return best;
|
|
121
|
+
};
|
|
55
122
|
/**
|
|
56
123
|
* Where a wall end meets another wall's centerline: the projection onto that line, as long as it
|
|
57
124
|
* lands on the wall or within reach past its ends. The line, not the segment, so a partition
|
|
@@ -65,6 +132,37 @@ function nearestOnSegment(p, a, b) {
|
|
|
65
132
|
const q = [a[0] + t * (b[0] - a[0]), a[1] + t * (b[1] - a[1])];
|
|
66
133
|
return { point: q, distance: dist(p, q) };
|
|
67
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The bend Pascal stores (`curveOffset`) for an arc from start to end through a plan point: the
|
|
137
|
+
* point's distance from the straight line, positive when it lies to the right looking from start
|
|
138
|
+
* to end (away from the wall's left normal).
|
|
139
|
+
*/
|
|
140
|
+
function bendThrough(start, end, through) {
|
|
141
|
+
const [dx, dz] = [end[0] - start[0], end[1] - start[1]];
|
|
142
|
+
return ((through[0] - start[0]) * dz - (through[1] - start[1]) * dx) / (Math.hypot(dx, dz) || 1);
|
|
143
|
+
}
|
|
144
|
+
/** The same on a curved wall: the nearest point of its arc, so a partition meets the curve. */
|
|
145
|
+
function nearestOnWall(p, wall) {
|
|
146
|
+
if (!wall.spec.through)
|
|
147
|
+
return nearestOnSegment(p, wall.start, wall.end);
|
|
148
|
+
const arc = {
|
|
149
|
+
start: wall.start,
|
|
150
|
+
end: wall.end,
|
|
151
|
+
curveOffset: bendThrough(wall.start, wall.end, wall.spec.through),
|
|
152
|
+
};
|
|
153
|
+
let best = { point: [p[0], p[1]], distance: Number.POSITIVE_INFINITY };
|
|
154
|
+
for (const [a, b] of getWallCenterlineSegments(arc, 64)) {
|
|
155
|
+
const [dx, dz] = [b.x - a.x, b.y - a.y];
|
|
156
|
+
const t = ((p[0] - a.x) * dx + (p[1] - a.y) * dz) / (dx * dx + dz * dz || 1);
|
|
157
|
+
const q = [
|
|
158
|
+
a.x + Math.max(0, Math.min(1, t)) * dx,
|
|
159
|
+
a.y + Math.max(0, Math.min(1, t)) * dz,
|
|
160
|
+
];
|
|
161
|
+
if (dist(p, q) < best.distance)
|
|
162
|
+
best = { point: q, distance: dist(p, q) };
|
|
163
|
+
}
|
|
164
|
+
return best;
|
|
165
|
+
}
|
|
68
166
|
const cross = (o, a, b) => (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
|
|
69
167
|
/** Whether two segments cross each other's interior (touching ends and shared edges do not). */
|
|
70
168
|
function segmentsCross(a, b, c, d) {
|
|
@@ -83,6 +181,8 @@ function polygonsOverlap(a, b) {
|
|
|
83
181
|
}
|
|
84
182
|
/** A wall's footprint, pulled in by a few millimetres so a slab meeting its face is not on it. */
|
|
85
183
|
function wallFootprint(wall) {
|
|
184
|
+
if (wall.curveOffset)
|
|
185
|
+
return getWallSurfacePolygon({ ...wall, thickness: wall.thickness - 0.01 }).map(({ x, y }) => [x, y]);
|
|
86
186
|
const inset = 0.005;
|
|
87
187
|
const [dx, dz] = [wall.end[0] - wall.start[0], wall.end[1] - wall.start[1]];
|
|
88
188
|
const len = Math.hypot(dx, dz) || 1;
|
|
@@ -106,7 +206,31 @@ function toLocal(frame, world) {
|
|
|
106
206
|
const [c, s] = [Math.cos(frame.yaw), Math.sin(frame.yaw)];
|
|
107
207
|
return [c * dx - s * dz, dy, s * dx + c * dz];
|
|
108
208
|
}
|
|
109
|
-
|
|
209
|
+
/**
|
|
210
|
+
* A payload light's beam in its host's frame (so it turns with the host), or none for a point
|
|
211
|
+
* light. A target on the light itself points it down.
|
|
212
|
+
*/
|
|
213
|
+
function lightSpot(light, at, host) {
|
|
214
|
+
if (!light.direction && !light.target && light.angle === undefined)
|
|
215
|
+
return undefined;
|
|
216
|
+
let [x, y, z] = light.target
|
|
217
|
+
? [light.target[0] - at[0], light.target[1] - at[1], light.target[2] - at[2]]
|
|
218
|
+
: (light.direction ?? [0, -1, 0]);
|
|
219
|
+
let length = Math.hypot(x, y, z);
|
|
220
|
+
if (length < 1e-6)
|
|
221
|
+
[x, y, z, length] = [0, -1, 0, 1];
|
|
222
|
+
const [c, s] = [Math.cos(host.yaw), Math.sin(host.yaw)];
|
|
223
|
+
const round = (v) => Math.round(v * 1e4) / 1e4;
|
|
224
|
+
return {
|
|
225
|
+
direction: [
|
|
226
|
+
round((c * x - s * z) / length),
|
|
227
|
+
round(y / length),
|
|
228
|
+
round((s * x + c * z) / length),
|
|
229
|
+
],
|
|
230
|
+
angle: light.angle ?? 60,
|
|
231
|
+
softness: light.softness ?? 0.4,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
110
234
|
/**
|
|
111
235
|
* The finish a payload value writes. A library or scene ref passes through; a hex colour is kept
|
|
112
236
|
* as an exact native material, one scene material per distinct colour and surface across the
|
|
@@ -127,25 +251,13 @@ function finishPool(context, say) {
|
|
|
127
251
|
return { ref: value };
|
|
128
252
|
if (!HEX.test(value))
|
|
129
253
|
return { error: `${value} is no colour: give #rrggbb or library:<id>.` };
|
|
130
|
-
const hex = value.toLowerCase();
|
|
131
254
|
if (!materials) {
|
|
132
255
|
if (!snapped)
|
|
133
256
|
say('this host keeps no scene materials: colours written as the nearest library colour');
|
|
134
257
|
snapped = true;
|
|
135
|
-
return { ref: nearestLibraryColorRef(
|
|
258
|
+
return { ref: nearestLibraryColorRef(value).ref };
|
|
136
259
|
}
|
|
137
|
-
const resolved =
|
|
138
|
-
properties: {
|
|
139
|
-
color: hex,
|
|
140
|
-
roughness: surface.roughness,
|
|
141
|
-
metalness: surface.metalness,
|
|
142
|
-
opacity: 1,
|
|
143
|
-
transparent: false,
|
|
144
|
-
side: 'front',
|
|
145
|
-
},
|
|
146
|
-
}, surface.roughness === MATTE.roughness && surface.metalness === MATTE.metalness
|
|
147
|
-
? hex
|
|
148
|
-
: `${hex}, roughness ${+surface.roughness.toFixed(2)}, metalness ${+surface.metalness.toFixed(2)}`);
|
|
260
|
+
const resolved = exactColorMaterial(materials, value, surface);
|
|
149
261
|
if (resolved.created) {
|
|
150
262
|
materials[resolved.sceneMaterial.id] = resolved.sceneMaterial;
|
|
151
263
|
created.push(resolved.sceneMaterial);
|
|
@@ -166,8 +278,13 @@ export default function build({ THREE }) {
|
|
|
166
278
|
}`;
|
|
167
279
|
/** Candidate modules, compiled by the host before room derivation decides which points are used. */
|
|
168
280
|
export function authorSceneCeilingPoints(payload) {
|
|
169
|
-
return payload.levels.flatMap((level, index) =>
|
|
281
|
+
return payload.levels.flatMap((level, index) => [
|
|
282
|
+
...level.lights.flatMap((light, i) => light.fixture ? [] : [{ id: `light/${index}/${i}`, code: CEILING_POINT_CODE }]),
|
|
283
|
+
// A room left with no light gets one at its seed.
|
|
284
|
+
...level.rooms.map((_, i) => ({ id: roomLightId(index, i), code: CEILING_POINT_CODE })),
|
|
285
|
+
]);
|
|
170
286
|
}
|
|
287
|
+
const roomLightId = (level, room) => `light/${level}/room/${room}`;
|
|
171
288
|
/**
|
|
172
289
|
* The rooms whose floor finish is a module, by their zone once the write made it: the host reads
|
|
173
290
|
* each room's floor (`floorScriptInputs`), compiles the module for it and writes it with
|
|
@@ -181,12 +298,27 @@ export function authorSceneFloorScripts(payload, report) {
|
|
|
181
298
|
return room.floorCode && zoneId ? [{ roomId, zoneId, code: room.floorCode }] : [];
|
|
182
299
|
}));
|
|
183
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* The slabs whose surface finish is a module, by their node once the write made them: the host
|
|
303
|
+
* reads each slab's surface (`slabSurfaceScriptInputs`), compiles the module for it and writes it
|
|
304
|
+
* with `setSlabSurfaceScript`, as it does a room's floor module.
|
|
305
|
+
*/
|
|
306
|
+
export function authorSceneSlabScripts(payload, report) {
|
|
307
|
+
return payload.levels.flatMap((level, index) => (level.slabs ?? []).flatMap((slab, i) => {
|
|
308
|
+
const slabId = `slab/${index}/${i}`;
|
|
309
|
+
const nodeId = report.ids[slabId];
|
|
310
|
+
return slab.surfaceCode && nodeId
|
|
311
|
+
? [{ slabId, nodeId, name: slab.name ?? slabId, code: slab.surfaceCode }]
|
|
312
|
+
: [];
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
184
315
|
/**
|
|
185
316
|
* Which faces the payload's face modules build on, and why the others cannot be: a wall's own
|
|
186
|
-
* `faceLeft` (face a) and `faceRight` (face b),
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
317
|
+
* `faceLeft` (face a) and `faceRight` (face b), its `faceOutside` on the faces no room of its level
|
|
318
|
+
* looks into, then each room's `wallCode` on the faces its floor meets (the spans of the room the
|
|
319
|
+
* zone was derived from, the faces `setWallSkirting` runs along), where a wall's own module wins.
|
|
320
|
+
* A face with nothing to build on (a curved or curtain wall), a left or right face no room looks
|
|
321
|
+
* into, or an outside module on a wall with rooms on both faces is a `no_face` defect.
|
|
190
322
|
*/
|
|
191
323
|
function resolveFaceScripts(payload, ids, nodes) {
|
|
192
324
|
const scripts = [];
|
|
@@ -225,6 +357,27 @@ function resolveFaceScripts(payload, ids, nodes) {
|
|
|
225
357
|
else
|
|
226
358
|
add(spec.id, face, nodeId, module.code, module.toHeight);
|
|
227
359
|
}
|
|
360
|
+
const outside = spec.faceOutside;
|
|
361
|
+
if (!outside)
|
|
362
|
+
continue;
|
|
363
|
+
if (!wallFaceScriptInputs(nodes, nodeId, 'a', outside.toHeight)) {
|
|
364
|
+
defects.push({
|
|
365
|
+
id: `${spec.id}/outside`,
|
|
366
|
+
code: 'no_face',
|
|
367
|
+
message: `Wall ${spec.id} ${flat}: its outside face has nothing a script can build on and keeps its paint.`,
|
|
368
|
+
});
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const cover = roomFaceCover(nodes, nodeId);
|
|
372
|
+
const faces = ['a', 'b'].filter((face) => cover[face] === 'none');
|
|
373
|
+
if (!faces.length)
|
|
374
|
+
defects.push({
|
|
375
|
+
id: `${spec.id}/outside`,
|
|
376
|
+
code: 'no_face',
|
|
377
|
+
message: `Rooms look into both faces of wall ${spec.id}: it has no outside face, so its faceOutside was not written.`,
|
|
378
|
+
});
|
|
379
|
+
for (const face of faces)
|
|
380
|
+
add(spec.id, face, nodeId, outside.code, outside.toHeight);
|
|
228
381
|
}
|
|
229
382
|
const byRoom = new Map();
|
|
230
383
|
for (const [i, room] of level.rooms.entries()) {
|
|
@@ -232,7 +385,10 @@ function resolveFaceScripts(payload, ids, nodes) {
|
|
|
232
385
|
const zone = nodes[ids[roomId] ?? ''];
|
|
233
386
|
if (!room.wallCode || zone?.type !== 'zone')
|
|
234
387
|
continue;
|
|
235
|
-
const boundaries =
|
|
388
|
+
const boundaries = queryNodes(nodes, {
|
|
389
|
+
type: ['wall', 'separator'],
|
|
390
|
+
parentId: zone.parentId ?? null,
|
|
391
|
+
});
|
|
236
392
|
const zoneWalls = [...zone.boundaryWallIds].sort().join();
|
|
237
393
|
const derived = floorRoomFaces(boundaries).find((r) => [...new Set(r.spans.filter((s) => s.kind === 'wall').map((s) => s.boundaryId))]
|
|
238
394
|
.sort()
|
|
@@ -347,6 +503,8 @@ export function authorScene(scene, input, context) {
|
|
|
347
503
|
['slab', level.slabs],
|
|
348
504
|
['column', level.columns],
|
|
349
505
|
['light', level.lights],
|
|
506
|
+
['stair', level.stairs],
|
|
507
|
+
['void', level.voids],
|
|
350
508
|
])
|
|
351
509
|
parts?.forEach((_, i) => {
|
|
352
510
|
claimSourceKey(`${kind}/${index}/${i}`);
|
|
@@ -360,6 +518,10 @@ export function authorScene(scene, input, context) {
|
|
|
360
518
|
for (const item of level.items ?? [])
|
|
361
519
|
claimSourceKey(item.id);
|
|
362
520
|
});
|
|
521
|
+
for (const item of payload.roof?.items ?? [])
|
|
522
|
+
claimSourceKey(item.id);
|
|
523
|
+
for (const unit of payload.units)
|
|
524
|
+
claimSourceKey(`unit/${unit.name}`);
|
|
363
525
|
const buildings = Object.values(scene).filter((n) => n.type === 'building');
|
|
364
526
|
const selected = input.buildingId ?? (context.activeLevelId ? scene[context.activeLevelId]?.parentId : undefined);
|
|
365
527
|
const building = selected
|
|
@@ -370,14 +532,7 @@ export function authorScene(scene, input, context) {
|
|
|
370
532
|
if (!building)
|
|
371
533
|
refuse('no_building', 'The scene has no building to put the levels in.');
|
|
372
534
|
const existingLevels = Object.values(scene).filter((n) => n.type === 'level' && n.parentId === building.id);
|
|
373
|
-
const
|
|
374
|
-
const previous = saved?.version === 1 &&
|
|
375
|
-
saved.buildingId === building.id &&
|
|
376
|
-
saved.nodes &&
|
|
377
|
-
saved.ids &&
|
|
378
|
-
typeof saved.seed === 'string'
|
|
379
|
-
? saved
|
|
380
|
-
: undefined;
|
|
535
|
+
const previous = authoredBaseline(scene, building);
|
|
381
536
|
const converted = Boolean(previous) || existingLevels.some((l) => l.metadata?.authorScene);
|
|
382
537
|
if (converted && !input.replace)
|
|
383
538
|
refuse('conversion_exists', 'This scene already holds a conversion. Pass replace: true to write over it.');
|
|
@@ -392,11 +547,16 @@ export function authorScene(scene, input, context) {
|
|
|
392
547
|
walls: 0,
|
|
393
548
|
doors: 0,
|
|
394
549
|
windows: 0,
|
|
550
|
+
passages: 0,
|
|
395
551
|
rooms: 0,
|
|
396
552
|
roomsNamed: 0,
|
|
397
553
|
slabs: 0,
|
|
398
554
|
columns: 0,
|
|
399
555
|
items: 0,
|
|
556
|
+
stairs: 0,
|
|
557
|
+
voids: 0,
|
|
558
|
+
roofSegments: 0,
|
|
559
|
+
units: 0,
|
|
400
560
|
lights: 0,
|
|
401
561
|
floorScripts: 0,
|
|
402
562
|
faceScripts: 0,
|
|
@@ -421,23 +581,7 @@ export function authorScene(scene, input, context) {
|
|
|
421
581
|
}
|
|
422
582
|
return got.ref;
|
|
423
583
|
};
|
|
424
|
-
const exactSlots = (compiled, id) =>
|
|
425
|
-
const slots = matchScriptSlotsToLibrary(compiled.manifest);
|
|
426
|
-
for (const slot of compiled.manifest.slots) {
|
|
427
|
-
// Glass keeps the glass add_object writes, drawn see-through and letting light pass. A
|
|
428
|
-
// scene material carries no emission, so an emissive slot keeps the module's own material
|
|
429
|
-
// (the library match leaves it alone) and a bulb stays lit.
|
|
430
|
-
if (!slot.color || slot.emissive || slots[slot.id] === GLASS)
|
|
431
|
-
continue;
|
|
432
|
-
const ref = finishRef(slot.color, id, {
|
|
433
|
-
roughness: slot.roughness ?? MATTE.roughness,
|
|
434
|
-
metalness: slot.metalness ?? MATTE.metalness,
|
|
435
|
-
});
|
|
436
|
-
if (ref)
|
|
437
|
-
slots[slot.id] = ref;
|
|
438
|
-
}
|
|
439
|
-
return slots;
|
|
440
|
-
};
|
|
584
|
+
const exactSlots = (compiled, id) => exactScriptSlots(compiled.manifest, matchScriptSlotsToLibrary(compiled.manifest), (hex, surface) => finishRef(hex, id, surface));
|
|
441
585
|
const paintContext = (levelId) => ({
|
|
442
586
|
...context,
|
|
443
587
|
activeLevelId: levelId,
|
|
@@ -452,11 +596,20 @@ export function authorScene(scene, input, context) {
|
|
|
452
596
|
}
|
|
453
597
|
const scaffoldIds = new Set(Object.keys(draft));
|
|
454
598
|
const levelNodes = [];
|
|
599
|
+
const levelWalls = [];
|
|
455
600
|
const itemNodeIds = new Map();
|
|
456
601
|
const itemFrames = new Map();
|
|
457
602
|
const skirtings = [];
|
|
458
603
|
let untypedWindows = 0;
|
|
604
|
+
/** By upper level index: the storey under it and the height of the tallest wall carrying it. */
|
|
605
|
+
const floorsAbove = new Map();
|
|
459
606
|
payload.levels.forEach((level, index) => {
|
|
607
|
+
// A storey is as tall as the climb to the next level's floor; the top one, as its walls.
|
|
608
|
+
const above = payload.levels[index + 1];
|
|
609
|
+
const walled = Math.max(0, ...(level.walls ?? []).map((w) => w.height)) || DEFAULT_LEVEL_HEIGHT;
|
|
610
|
+
if (above && above.elevation <= level.elevation)
|
|
611
|
+
say(`"${above.name}" (elevation ${metres(above.elevation)}) is not above "${level.name}" (${metres(level.elevation)}): "${level.name}" is as tall as its walls, ${metres(walled)}`);
|
|
612
|
+
const storey = above && above.elevation > level.elevation ? above.elevation - level.elevation : 0;
|
|
460
613
|
const levelNode = LevelNode.parse({
|
|
461
614
|
object: 'node',
|
|
462
615
|
type: 'level',
|
|
@@ -466,6 +619,7 @@ export function authorScene(scene, input, context) {
|
|
|
466
619
|
metadata: { authorScene: { elevation: level.elevation } },
|
|
467
620
|
name: level.name,
|
|
468
621
|
level: index,
|
|
622
|
+
height: storey || walled,
|
|
469
623
|
children: [],
|
|
470
624
|
});
|
|
471
625
|
draft[levelNode.id] = levelNode;
|
|
@@ -476,11 +630,26 @@ export function authorScene(scene, input, context) {
|
|
|
476
630
|
levelNode.children.push(node.id);
|
|
477
631
|
};
|
|
478
632
|
// ---------- walls, joined ----------
|
|
633
|
+
// Walls keep their height; the floor above is built between the storeys, from the tallest wall
|
|
634
|
+
// carrying it up to its surface. A wall reaching into that floor stops under it, following the
|
|
635
|
+
// storey as a drawn wall does.
|
|
636
|
+
const reaches = (height) => storey > 0 && height > storey - MIN_SLAB_THICKNESS;
|
|
479
637
|
const walls = (level.walls ?? []).map((w) => ({
|
|
480
638
|
spec: w,
|
|
481
639
|
start: [...w.start],
|
|
482
640
|
end: [...w.end],
|
|
641
|
+
height: reaches(w.height) ? storey : w.height,
|
|
483
642
|
}));
|
|
643
|
+
const carrying = walls
|
|
644
|
+
.map((w) => w.spec.height)
|
|
645
|
+
.filter((height) => storey > 0 && !reaches(height) && height >= storey - FLOOR_DEPTH);
|
|
646
|
+
if (carrying.length)
|
|
647
|
+
floorsAbove.set(index + 1, { below: level.name, wallHeight: Math.max(...carrying), storey });
|
|
648
|
+
const stopped = [
|
|
649
|
+
...new Set(walls.filter((w) => w.height !== w.spec.height).map((w) => w.spec.height)),
|
|
650
|
+
];
|
|
651
|
+
if (stopped.length)
|
|
652
|
+
say(`walls of "${level.name}" given ${stopped.map(metres).join(', ')} reach the floor of "${above.name}", ${metres(storey)} up: they stop under it, following the storey`);
|
|
484
653
|
for (const w of walls) {
|
|
485
654
|
for (const end of ['start', 'end']) {
|
|
486
655
|
const pt = w[end];
|
|
@@ -488,7 +657,7 @@ export function authorScene(scene, input, context) {
|
|
|
488
657
|
for (const o of walls) {
|
|
489
658
|
if (o === w)
|
|
490
659
|
continue;
|
|
491
|
-
const near =
|
|
660
|
+
const near = nearestOnWall(pt, o);
|
|
492
661
|
if (near.distance < JOIN_REACH && (!best || near.distance < best.distance))
|
|
493
662
|
best = near;
|
|
494
663
|
}
|
|
@@ -508,6 +677,13 @@ export function authorScene(scene, input, context) {
|
|
|
508
677
|
defect(w.spec.id, 'wall_too_short', 'The wall is shorter than 5 cm after joining.');
|
|
509
678
|
continue;
|
|
510
679
|
}
|
|
680
|
+
const bend = w.spec.through ? bendThrough(w.start, w.end, w.spec.through) : 0;
|
|
681
|
+
const curve = bend ? normalizeWallCurveOffset(w, bend) : 0;
|
|
682
|
+
if (w.spec.through && !curve)
|
|
683
|
+
say(`wall ${w.spec.id}: its through point is on the straight line: written straight`);
|
|
684
|
+
else if (Math.abs(bend) > getMaxWallCurveOffset(w) + 1e-9)
|
|
685
|
+
say(`wall ${w.spec.id}: its through point stands ${metres(Math.abs(bend))} off the straight line, more than half its ${metres(dist(w.start, w.end))} span: written as a half circle, ${metres(Math.abs(curve))} out`);
|
|
686
|
+
const { curtain } = w.spec;
|
|
511
687
|
const node = WallNode.parse({
|
|
512
688
|
object: 'node',
|
|
513
689
|
type: 'wall',
|
|
@@ -518,8 +694,20 @@ export function authorScene(scene, input, context) {
|
|
|
518
694
|
children: [],
|
|
519
695
|
start: w.start,
|
|
520
696
|
end: w.end,
|
|
521
|
-
height: w.spec.height,
|
|
697
|
+
...(w.height === w.spec.height ? { height: w.spec.height } : {}),
|
|
522
698
|
thickness: w.spec.thickness,
|
|
699
|
+
...(curve ? { curveOffset: curve } : {}),
|
|
700
|
+
...(curtain
|
|
701
|
+
? {
|
|
702
|
+
wallType: 'curtain',
|
|
703
|
+
curtainWall: CurtainWallConfig.parse({
|
|
704
|
+
columns: {
|
|
705
|
+
layout: 'maximum-spacing',
|
|
706
|
+
spacing: Math.min(100, Math.max(0.2, curtain.spacing)),
|
|
707
|
+
},
|
|
708
|
+
}),
|
|
709
|
+
}
|
|
710
|
+
: {}),
|
|
523
711
|
});
|
|
524
712
|
place(node);
|
|
525
713
|
wallNodes.set(w.spec.id, { ...w, node });
|
|
@@ -532,13 +720,20 @@ export function authorScene(scene, input, context) {
|
|
|
532
720
|
});
|
|
533
721
|
report.ids[w.spec.id] = node.id;
|
|
534
722
|
report.walls++;
|
|
535
|
-
// ---------- its openings ----------
|
|
536
|
-
const length = dist(w.start, w.end);
|
|
723
|
+
// ---------- its openings: along the arc on a curved wall, as Pascal places them ----------
|
|
724
|
+
const length = curve ? getWallCurveLength(node) : dist(w.start, w.end);
|
|
537
725
|
(w.spec.openings ?? []).forEach((o, i) => {
|
|
538
726
|
const id = `${w.spec.id}/${i}`;
|
|
727
|
+
const passage = o.kind === 'passage';
|
|
728
|
+
const kind = passage ? 'door' : o.kind;
|
|
729
|
+
if (passage) {
|
|
730
|
+
const leaf = ['type', 'style', 'leaves', 'hinge', 'swing', 'code'].filter((field) => o[field] !== undefined);
|
|
731
|
+
if (leaf.length)
|
|
732
|
+
say(`passage ${id}: it has no leaf, its ${leaf.join(', ')} ignored`);
|
|
733
|
+
}
|
|
539
734
|
// A module supplies geometry; the declared type still describes how the opening works.
|
|
540
735
|
let compiled;
|
|
541
|
-
if (o.code) {
|
|
736
|
+
if (o.code && !passage) {
|
|
542
737
|
compiled = input.compiled[id];
|
|
543
738
|
if (!compiled) {
|
|
544
739
|
defect(id, 'compile_failed', input.compileErrors?.[id] ?? 'The module did not compile.');
|
|
@@ -560,24 +755,53 @@ export function authorScene(scene, input, context) {
|
|
|
560
755
|
say(`${o.kind} ${id}: style ignored, its module builds it`);
|
|
561
756
|
}
|
|
562
757
|
const [width, height] = compiled ? scriptedSize(compiled.manifest) : [o.width, o.height];
|
|
563
|
-
|
|
758
|
+
const size = o.kind === 'door' && !compiled
|
|
759
|
+
? getDoorOpeningSize({
|
|
760
|
+
...o,
|
|
761
|
+
width,
|
|
762
|
+
height,
|
|
763
|
+
doorType: o.type && isDoorType(o.type) ? o.type : 'hinged',
|
|
764
|
+
})
|
|
765
|
+
: { width, height, leafOffsetX: 0 };
|
|
766
|
+
if (size.width > length + 0.01) {
|
|
564
767
|
defect(id, 'opening_wider_than_wall', `${o.kind} ${width.toFixed(2)} m wide on a ${length.toFixed(2)} m wall.`);
|
|
565
768
|
return;
|
|
566
769
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
770
|
+
if (o.kind === 'door') {
|
|
771
|
+
const wallHeight = wallOpeningCeilingOver(node, draft, o.along - size.width / 2, o.along + size.width / 2) - (node.wallType === 'curtain' ? getCurtainWallConfig(node).perimeterWidth : 0);
|
|
772
|
+
if (o.sill + size.height > wallHeight + 1e-6) {
|
|
773
|
+
defect(id, 'wall_too_low', `Wall ${w.spec.id} is ${metres(wallHeight)} high, too low for a ${metres(size.height)} door at sill ${metres(o.sill)}.`);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
// A door or window is flat, set on the arc at its centre: its ends leave the curve.
|
|
778
|
+
if (curve) {
|
|
779
|
+
const chord = dist(w.start, w.end);
|
|
780
|
+
const radius = (chord * chord) / (8 * Math.abs(curve)) + Math.abs(curve) / 2;
|
|
781
|
+
const standoff = radius - Math.sqrt(Math.max(0, radius ** 2 - (size.width / 2) ** 2));
|
|
782
|
+
const half = w.spec.thickness / 2;
|
|
783
|
+
if (standoff > half)
|
|
784
|
+
warn(id, 'flat_on_arc', `The ${metres(width)} ${o.kind} is flat on wall ${w.spec.id}, an arc of radius ${metres(radius)}: its ends stand ${metres(standoff)} off the curve, past the wall's faces. Set narrower ones side by side, at most ${metres(2 * Math.sqrt(radius ** 2 - (radius - half) ** 2))} wide.`);
|
|
785
|
+
}
|
|
786
|
+
const centre = Math.min(Math.max(o.along, size.width / 2), length - size.width / 2);
|
|
787
|
+
const x = centre + size.leafOffsetX;
|
|
788
|
+
if (Math.abs(centre - o.along) > 0.01)
|
|
789
|
+
say(`${o.kind} ${id}: moved ${Math.abs(centre - o.along).toFixed(2)} m along the wall to fit inside it`);
|
|
570
790
|
const common = {
|
|
571
791
|
object: 'node',
|
|
572
|
-
id: mint(
|
|
792
|
+
id: mint(kind, id),
|
|
573
793
|
parentId: node.id,
|
|
574
794
|
visible: true,
|
|
575
795
|
metadata: { authorScene: { id, color: o.color ?? null } },
|
|
576
796
|
wallId: node.id,
|
|
797
|
+
casingFaceA: true,
|
|
798
|
+
casingFaceB: true,
|
|
577
799
|
width,
|
|
578
800
|
height,
|
|
579
801
|
rotation: [0, 0, 0],
|
|
580
802
|
...(o.name ? { name: o.name } : {}),
|
|
803
|
+
...(o.shape ? { openingShape: o.shape } : {}),
|
|
804
|
+
...(o.archHeight === undefined ? {} : { archHeight: Math.min(o.archHeight, height) }),
|
|
581
805
|
...(compiled
|
|
582
806
|
? {
|
|
583
807
|
source: scriptSource(compiled, { category: o.kind }),
|
|
@@ -586,7 +810,17 @@ export function authorScene(scene, input, context) {
|
|
|
586
810
|
: {}),
|
|
587
811
|
};
|
|
588
812
|
let opening;
|
|
589
|
-
if (
|
|
813
|
+
if (passage) {
|
|
814
|
+
opening = DoorNode.parse({
|
|
815
|
+
...common,
|
|
816
|
+
type: 'door',
|
|
817
|
+
frameThickness: NEW_DOOR_FRAME_THICKNESS,
|
|
818
|
+
position: [x, o.sill + height / 2, 0],
|
|
819
|
+
openingKind: 'opening',
|
|
820
|
+
});
|
|
821
|
+
report.passages++;
|
|
822
|
+
}
|
|
823
|
+
else if (o.kind === 'door') {
|
|
590
824
|
const type = o.type;
|
|
591
825
|
if (type && !isDoorType(type))
|
|
592
826
|
defect(id, 'type_mismatch', `${type} is a window type; the door is written hinged.`);
|
|
@@ -600,12 +834,19 @@ export function authorScene(scene, input, context) {
|
|
|
600
834
|
opening = DoorNode.parse({
|
|
601
835
|
...common,
|
|
602
836
|
type: 'door',
|
|
603
|
-
|
|
837
|
+
frameThickness: NEW_DOOR_FRAME_THICKNESS,
|
|
838
|
+
position: [x, o.sill + height / 2, 0],
|
|
604
839
|
...(type && isDoorType(type) ? doorTypeFields(type) : {}),
|
|
605
840
|
...(style && isDoorStyle(style) ? doorStyleLook(style) : {}),
|
|
606
841
|
...(o.leaves ? { leafCount: o.leaves } : {}),
|
|
842
|
+
sidelightLeft: o.sidelightLeft,
|
|
843
|
+
sidelightRight: o.sidelightRight,
|
|
844
|
+
transomHeight: o.transomHeight,
|
|
845
|
+
slidingStyle: o.slidingStyle,
|
|
846
|
+
...(o.slideDirection ? { slideDirection: o.slideDirection } : {}),
|
|
607
847
|
hingesSide,
|
|
608
848
|
handleSide,
|
|
849
|
+
handleStyle: o.handleStyle,
|
|
609
850
|
...(o.swing ? { swingDirection: o.swing === 'back' ? 'outward' : 'inward' } : {}),
|
|
610
851
|
});
|
|
611
852
|
report.doors++;
|
|
@@ -626,13 +867,35 @@ export function authorScene(scene, input, context) {
|
|
|
626
867
|
opening = WindowNode.parse({
|
|
627
868
|
...common,
|
|
628
869
|
type: 'window',
|
|
870
|
+
hingesSide: o.hingesSide ?? (o.hinge === 'end' ? 'right' : 'left'),
|
|
871
|
+
sashGrid: !windowTakesStyle(windowType ?? 'fixed') && (o.columns || o.rows) ? true : o.sashGrid,
|
|
872
|
+
windowHandle: o.windowHandle,
|
|
873
|
+
sillInterior: o.sillInterior,
|
|
874
|
+
shutters: o.shutters,
|
|
875
|
+
shutterWidth: o.shutterWidth,
|
|
876
|
+
tiltTurnMode: o.tiltTurnMode,
|
|
877
|
+
sillOverhang: o.sillOverhang,
|
|
878
|
+
sillInteriorDepth: o.sillInteriorDepth,
|
|
879
|
+
sillInteriorThickness: o.sillInteriorThickness,
|
|
880
|
+
sillInteriorOverhang: o.sillInteriorOverhang,
|
|
881
|
+
shutterOpen: o.shutterOpen,
|
|
882
|
+
shutterSides: o.shutterSides,
|
|
883
|
+
rollerShutter: o.rollerShutter,
|
|
884
|
+
rollerShutterBoxHeight: o.rollerShutterBoxHeight,
|
|
885
|
+
rollerShutterOpen: o.rollerShutterOpen,
|
|
886
|
+
frameThickness: NEW_WINDOW_FRAME_THICKNESS,
|
|
629
887
|
position: [x, o.sill + height / 2, 0],
|
|
630
888
|
...(windowType ? windowTypeFields(windowType) : {}),
|
|
889
|
+
...(o.openingDirection === undefined ? {} : { openingDirection: o.openingDirection }),
|
|
631
890
|
...(style && isWindowStyle(style) && takesStyle ? getWindowStyleOverrides(style) : {}),
|
|
891
|
+
...(o.columns
|
|
892
|
+
? { columnRatios: Array.from({ length: o.columns }, () => 1 / o.columns) }
|
|
893
|
+
: {}),
|
|
894
|
+
...(o.rows ? { rowRatios: Array.from({ length: o.rows }, () => 1 / o.rows) } : {}),
|
|
632
895
|
});
|
|
633
896
|
report.windows++;
|
|
634
897
|
}
|
|
635
|
-
const panel =
|
|
898
|
+
const panel = kind === 'door' ? finishRef(o.color, id) : undefined;
|
|
636
899
|
const frame = finishRef(o.frameColor ?? o.color, id);
|
|
637
900
|
if (panel || frame)
|
|
638
901
|
opening = {
|
|
@@ -658,10 +921,14 @@ export function authorScene(scene, input, context) {
|
|
|
658
921
|
const polygon = s.polygon;
|
|
659
922
|
// A slab over a wall's footprint, higher than half the wall: a cap, which would lift the
|
|
660
923
|
// wall onto it. Pascal caps its walls itself.
|
|
661
|
-
const lifted = [...wallNodes.values()].find((w) => s.top > w.
|
|
662
|
-
polygonsOverlap(polygon, wallFootprint({
|
|
924
|
+
const lifted = [...wallNodes.values()].find((w) => s.top > w.height / 2 &&
|
|
925
|
+
polygonsOverlap(polygon, wallFootprint({
|
|
926
|
+
...w,
|
|
927
|
+
thickness: w.spec.thickness,
|
|
928
|
+
curveOffset: w.node.curveOffset,
|
|
929
|
+
})));
|
|
663
930
|
if (lifted) {
|
|
664
|
-
defect(id, 'slab_over_wall', `${label} overlaps wall ${lifted.spec.id} with its top at ${metres(s.top)}, above half the wall's ${metres(lifted.
|
|
931
|
+
defect(id, 'slab_over_wall', `${label} overlaps wall ${lifted.spec.id} with its top at ${metres(s.top)}, above half the wall's ${metres(lifted.height)}: it would lift the wall onto it. Pascal builds a wall's cap itself; fold it into the wall.`);
|
|
665
932
|
return;
|
|
666
933
|
}
|
|
667
934
|
if (s.thickness < THIN_SLAB)
|
|
@@ -679,35 +946,527 @@ export function authorScene(scene, input, context) {
|
|
|
679
946
|
elevation: s.top,
|
|
680
947
|
thickness: s.thickness,
|
|
681
948
|
...(finish ? { slots: { surface: finish } } : {}),
|
|
682
|
-
...(s.railing?.length
|
|
683
|
-
? { railing: s.railing.map((r) => ({ start: r.start, end: r.end })) }
|
|
684
|
-
: {}),
|
|
685
949
|
});
|
|
686
950
|
place(node);
|
|
687
951
|
report.ids[id] = node.id;
|
|
688
952
|
report.slabs++;
|
|
953
|
+
(s.railing ?? []).forEach((r, k) => {
|
|
954
|
+
const railId = `${id}/railing/${k}`;
|
|
955
|
+
let fence = FenceNode.parse({
|
|
956
|
+
object: 'node',
|
|
957
|
+
id: mint('fence', railId),
|
|
958
|
+
parentId: levelNode.id,
|
|
959
|
+
visible: true,
|
|
960
|
+
metadata: { authorScene: { id: railId } },
|
|
961
|
+
name: 'Railing',
|
|
962
|
+
start: r.start,
|
|
963
|
+
end: r.end,
|
|
964
|
+
height: r.height,
|
|
965
|
+
style: 'rail',
|
|
966
|
+
baseStyle: 'floating',
|
|
967
|
+
baseHeight: 0,
|
|
968
|
+
postCap: 'none',
|
|
969
|
+
supportSlabId: node.id,
|
|
970
|
+
});
|
|
971
|
+
const railFinish = finishRef(r.color, railId);
|
|
972
|
+
if (railFinish)
|
|
973
|
+
fence = {
|
|
974
|
+
...fence,
|
|
975
|
+
slots: Object.fromEntries((nodeSlotDeclarations(fence) ?? []).map((slot) => [slot.slotId, railFinish])),
|
|
976
|
+
};
|
|
977
|
+
place(fence);
|
|
978
|
+
});
|
|
689
979
|
});
|
|
690
980
|
(level.columns ?? []).forEach((c, i) => {
|
|
691
|
-
const
|
|
981
|
+
const id = `column/${index}/${i}`;
|
|
982
|
+
let compiled;
|
|
983
|
+
if (c.code) {
|
|
984
|
+
compiled = input.compiled[id];
|
|
985
|
+
if (!compiled) {
|
|
986
|
+
defect(id, 'compile_failed', input.compileErrors?.[id] ?? 'The module did not compile.');
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (compiled.mount !== 'floor') {
|
|
990
|
+
defect(id, 'wrong_mount', `A column's module uses mount 'floor', not '${compiled.mount}'.`);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
try {
|
|
994
|
+
requireMeasured(compiled, { label: c.name ?? id, tool: 'author_scene' });
|
|
995
|
+
}
|
|
996
|
+
catch (error) {
|
|
997
|
+
const e = error;
|
|
998
|
+
defect(id, e.code ?? 'unmeasurable_object', e.message ?? String(error));
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
if (c.color)
|
|
1002
|
+
say(`column ${id}: built by its module, which keeps its own colours; color ignored`);
|
|
1003
|
+
}
|
|
1004
|
+
const size = compiled && scriptedSize(compiled.manifest);
|
|
1005
|
+
let node = ColumnNode.parse({
|
|
692
1006
|
object: 'node',
|
|
693
1007
|
type: 'column',
|
|
694
|
-
id: mint('column',
|
|
1008
|
+
id: mint('column', id),
|
|
695
1009
|
parentId: levelNode.id,
|
|
696
1010
|
visible: true,
|
|
697
|
-
metadata: { authorScene: {
|
|
1011
|
+
metadata: { authorScene: { id } },
|
|
698
1012
|
...(c.name ? { name: c.name } : {}),
|
|
699
1013
|
position: [c.position[0], 0, c.position[1]],
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
1014
|
+
rotation: (c.rotation * Math.PI) / 180,
|
|
1015
|
+
...(compiled && size
|
|
1016
|
+
? {
|
|
1017
|
+
source: scriptSource(compiled, { category: 'column' }),
|
|
1018
|
+
slots: exactSlots(compiled, id),
|
|
1019
|
+
width: size[0],
|
|
1020
|
+
height: size[1],
|
|
1021
|
+
depth: size[2],
|
|
1022
|
+
}
|
|
1023
|
+
: {
|
|
1024
|
+
crossSection: c.shape,
|
|
1025
|
+
...(c.shape === 'round' ? { radius: c.width / 2 } : {}),
|
|
1026
|
+
width: c.width,
|
|
1027
|
+
depth: c.shape === 'round' ? c.width : (c.depth ?? c.width),
|
|
1028
|
+
height: c.height,
|
|
1029
|
+
baseHeight: 0,
|
|
1030
|
+
capitalHeight: 0,
|
|
1031
|
+
}),
|
|
706
1032
|
});
|
|
1033
|
+
const finish = !compiled && finishRef(c.color, id);
|
|
1034
|
+
if (finish)
|
|
1035
|
+
node = {
|
|
1036
|
+
...node,
|
|
1037
|
+
slots: Object.fromEntries((nodeSlotDeclarations(node) ?? []).map((slot) => [slot.slotId, finish])),
|
|
1038
|
+
};
|
|
707
1039
|
place(node);
|
|
708
|
-
report.ids[
|
|
1040
|
+
report.ids[id] = node.id;
|
|
709
1041
|
report.columns++;
|
|
710
1042
|
});
|
|
1043
|
+
levelWalls.push({ walls, wallNodes });
|
|
1044
|
+
});
|
|
1045
|
+
report.levels = levelNodes.length;
|
|
1046
|
+
if (untypedWindows)
|
|
1047
|
+
say(`${untypedWindows} window${untypedWindows === 1 ? ' has' : 's have'} no type: written fixed (cannot open); give type`);
|
|
1048
|
+
// ---------- which face of each wall looks into a room: read from the plan, as the editor does ----------
|
|
1049
|
+
for (const levelNode of levelNodes) {
|
|
1050
|
+
const boundaries = Object.values(draft).filter((n) => n.parentId === levelNode.id && (n.type === 'wall' || n.type === 'separator'));
|
|
1051
|
+
for (const { wallId, ...sides } of detectSpacesForLevel(levelNode.id, boundaries).wallUpdates)
|
|
1052
|
+
draft[wallId] = { ...draft[wallId], ...sides };
|
|
1053
|
+
}
|
|
1054
|
+
// ---------- rooms: Pascal's own derivation, then the payload's names and floors ----------
|
|
1055
|
+
const { nodes: withZones, createdZoneIds } = migrateRoomZones(draft);
|
|
1056
|
+
const zones = createdZoneIds.map((id) => ZoneNode.parse(withZones[id]));
|
|
1057
|
+
for (const zone of zones) {
|
|
1058
|
+
draft[zone.id] = zone;
|
|
1059
|
+
const level = draft[zone.parentId];
|
|
1060
|
+
draft[zone.parentId] = { ...level, children: [...level.children, zone.id] };
|
|
1061
|
+
report.rooms++;
|
|
1062
|
+
}
|
|
1063
|
+
const joined = new Map();
|
|
1064
|
+
const roomIds = new Map();
|
|
1065
|
+
payload.levels.forEach((level, index) => {
|
|
1066
|
+
const levelNode = levelNodes[index];
|
|
1067
|
+
for (const [i, room] of level.rooms.entries()) {
|
|
1068
|
+
const id = `room/${index}/${i}`;
|
|
1069
|
+
roomIds.set(room, id);
|
|
1070
|
+
const zone = zones.find((z) => z.parentId === levelNode.id && pointInPolygon(room.seed, z.polygon));
|
|
1071
|
+
// Several seeds in one enclosure name one open-plan room, in the order given.
|
|
1072
|
+
const sharing = zone && joined.get(zone.id);
|
|
1073
|
+
if (sharing) {
|
|
1074
|
+
sharing.push(room);
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
if (!zone) {
|
|
1078
|
+
defect(id, 'room_not_enclosed', `No closed room around (${room.seed[0]}, ${room.seed[1]}) "${room.name}": its walls do not meet.`);
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
if (room.floorRegions.length > MANY_REGIONS)
|
|
1082
|
+
warn(id, 'many_regions', `"${room.name}" has ${room.floorRegions.length} floor regions: regions are fixed polygons that do not follow a resize; use one floor finish.`);
|
|
1083
|
+
const finish = finishRef(room.floor, id);
|
|
1084
|
+
const regions = room.floorRegions.map((r, k) => ({
|
|
1085
|
+
id: `region_${k}`,
|
|
1086
|
+
polygon: r.polygon,
|
|
1087
|
+
finish: finishRef(r.finish, id) ?? r.finish,
|
|
1088
|
+
}));
|
|
1089
|
+
draft[zone.id] = ZoneNode.parse({
|
|
1090
|
+
...zone,
|
|
1091
|
+
name: room.name,
|
|
1092
|
+
spaceRole: 'room',
|
|
1093
|
+
...(finish || regions.length
|
|
1094
|
+
? {
|
|
1095
|
+
floor: {
|
|
1096
|
+
...zone.floor,
|
|
1097
|
+
...(finish ? { finish } : {}),
|
|
1098
|
+
...(regions.length ? { regions } : {}),
|
|
1099
|
+
},
|
|
1100
|
+
}
|
|
1101
|
+
: {}),
|
|
1102
|
+
metadata: { ...zone.metadata, authorScene: { seed: room.seed } },
|
|
1103
|
+
});
|
|
1104
|
+
joined.set(zone.id, [room]);
|
|
1105
|
+
report.ids[id] = zone.id;
|
|
1106
|
+
report.roomsNamed++;
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
for (const [zoneId, rooms] of joined) {
|
|
1110
|
+
if (rooms.length < 2)
|
|
1111
|
+
continue;
|
|
1112
|
+
const [first, ...rest] = rooms.map((room) => room.name);
|
|
1113
|
+
// "Kitchen" joins as "kitchen"; a name that is not a capitalised word ("WC") stays as given.
|
|
1114
|
+
const names = [
|
|
1115
|
+
first,
|
|
1116
|
+
...rest.map((name) => name.replace(/^([A-Z])(?=[a-z])/, (c) => c.toLowerCase())),
|
|
1117
|
+
];
|
|
1118
|
+
const name = `${names.slice(0, -1).join(', ')} and ${names.at(-1)}`;
|
|
1119
|
+
draft[zoneId] = { ...draft[zoneId], name };
|
|
1120
|
+
report.roomsNamed += rest.length;
|
|
1121
|
+
const floors = rooms
|
|
1122
|
+
.slice(1)
|
|
1123
|
+
.filter((room) => room.floor || room.floorCode || room.floorRegions.length);
|
|
1124
|
+
say(`${rooms.map((room) => `"${room.name}"`).join(', ')} are one enclosure, no wall between them: one open-plan room, "${name}"${floors.length ? `, with the floor of "${first}"` : ''}`);
|
|
1125
|
+
}
|
|
1126
|
+
// ---------- units: one per distinct name, under the building, holding the rooms that name it ----------
|
|
1127
|
+
const units = new Map();
|
|
1128
|
+
for (const unit of payload.units) {
|
|
1129
|
+
const color = unit.color && HEX.test(unit.color) ? unit.color : undefined;
|
|
1130
|
+
if (unit.color && !color)
|
|
1131
|
+
defect(`unit/${unit.name}`, 'invalid_color', `"${unit.color}" is not a #rrggbb colour.`);
|
|
1132
|
+
units.set(unit.name, { kind: unit.kind, color, zones: [] });
|
|
1133
|
+
}
|
|
1134
|
+
for (const [zoneId, rooms] of joined) {
|
|
1135
|
+
const names = [...new Set(rooms.flatMap((room) => (room.unit ? [room.unit] : [])))];
|
|
1136
|
+
// One enclosure is one room, so it is in one unit: seeds naming two want a wall between them.
|
|
1137
|
+
if (names.length > 1) {
|
|
1138
|
+
defect(roomIds.get(rooms[0]), 'unit_split_room', `${rooms.map((room) => `"${room.name}"`).join(', ')} are one open-plan room but name different units (${names.join(', ')}): the room is in none. Put a wall between them, or one unit on all of them.`);
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
if (!names[0])
|
|
1142
|
+
continue;
|
|
1143
|
+
const entry = units.get(names[0]) ?? { kind: 'apartment', zones: [] };
|
|
1144
|
+
entry.zones.push(zoneId);
|
|
1145
|
+
units.set(names[0], entry);
|
|
1146
|
+
}
|
|
1147
|
+
for (const [name, { kind, color, zones: members }] of units) {
|
|
1148
|
+
const unit = UnitNode.parse({
|
|
1149
|
+
id: mint('unit', `unit/${name}`),
|
|
1150
|
+
parentId: building.id,
|
|
1151
|
+
name,
|
|
1152
|
+
kind,
|
|
1153
|
+
...(color ? { color } : {}),
|
|
1154
|
+
members,
|
|
1155
|
+
});
|
|
1156
|
+
draft[unit.id] = unit;
|
|
1157
|
+
report.ids[`unit/${name}`] = unit.id;
|
|
1158
|
+
report.units++;
|
|
1159
|
+
if (!members.length)
|
|
1160
|
+
warn(`unit/${name}`, 'unit_no_rooms', `No room names unit "${name}".`);
|
|
1161
|
+
}
|
|
1162
|
+
// ---------- floors and ceilings, as the editor derives them on load ----------
|
|
1163
|
+
const levelIds = levelNodes.map((l) => l.id);
|
|
1164
|
+
let written = {
|
|
1165
|
+
...reconcileSceneStructure({ nodes: draft, levelIds, mintId: (kind) => mint(kind) }).nodes,
|
|
1166
|
+
};
|
|
1167
|
+
for (const opening of Object.values(written)) {
|
|
1168
|
+
const wall = written[opening.parentId ?? ''];
|
|
1169
|
+
if (opening.type !== 'door' || wall?.type !== 'wall' || scaffoldIds.has(opening.id))
|
|
1170
|
+
continue;
|
|
1171
|
+
const facing = doorFacing(wall);
|
|
1172
|
+
written[opening.id] = {
|
|
1173
|
+
...opening,
|
|
1174
|
+
...facing,
|
|
1175
|
+
position: doorPositionForFacing(opening, facing.rotation),
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
// A room's plate and ceiling are named by its room, whose id follows its walls: a revision keeps
|
|
1179
|
+
// an unchanged room's, and a room it adds brings its own with it (never another room's renamed).
|
|
1180
|
+
const derivedIds = new Map();
|
|
1181
|
+
for (const node of Object.values(written)) {
|
|
1182
|
+
const zones = node.type === 'ceiling' && node.zoneId
|
|
1183
|
+
? [node.zoneId]
|
|
1184
|
+
: node.type === 'slab' && node.plateRole === 'base'
|
|
1185
|
+
? [...(node.zoneIds ?? [])].sort()
|
|
1186
|
+
: [];
|
|
1187
|
+
if (zones.length && !scaffoldIds.has(node.id))
|
|
1188
|
+
derivedIds.set(node.id, mint(node.type, `${node.type}/${zones.join('+')}`));
|
|
1189
|
+
}
|
|
1190
|
+
if (derivedIds.size) {
|
|
1191
|
+
let json = JSON.stringify(written);
|
|
1192
|
+
for (const [from, to] of derivedIds)
|
|
1193
|
+
json = json.replaceAll(`"${from}"`, `"${to}"`);
|
|
1194
|
+
written = JSON.parse(json);
|
|
1195
|
+
}
|
|
1196
|
+
// ---------- the floor between storeys: each upper plate keeps its top and reaches down to the walls ----------
|
|
1197
|
+
for (const [index, { below, wallHeight, storey }] of floorsAbove) {
|
|
1198
|
+
const plates = (levelId) => Object.values(written).filter((node) => node.type === 'slab' && node.parentId === levelId && node.plateRole === 'base');
|
|
1199
|
+
// The carrying walls stand on the floor below; their tops, in this level's frame, are its underside.
|
|
1200
|
+
const lower = plates(levelNodes[index - 1].id);
|
|
1201
|
+
const underside = (lower.length ? Math.max(...lower.map((p) => p.elevation)) : 0) + wallHeight;
|
|
1202
|
+
// Seen from outside, the band continues the cladding under it: the colour most of those walls
|
|
1203
|
+
// wear outside paints the plate's edge and sides.
|
|
1204
|
+
const outsides = (payload.levels[index - 1].walls ?? []).flatMap((w) => w.colorOutside ? [w.colorOutside] : []);
|
|
1205
|
+
const cladding = mostCommon(outsides);
|
|
1206
|
+
const bandRef = cladding && finishRef(cladding, `level/${index}/floor`);
|
|
1207
|
+
const depths = new Set();
|
|
1208
|
+
for (const plate of plates(levelNodes[index].id)) {
|
|
1209
|
+
const thickness = roundFloorElevation(plate.elevation - (underside - storey));
|
|
1210
|
+
written[plate.id] = {
|
|
1211
|
+
...plate,
|
|
1212
|
+
thickness,
|
|
1213
|
+
...(bandRef ? { slots: { ...plate.slots, edge: bandRef, side: bandRef } } : {}),
|
|
1214
|
+
};
|
|
1215
|
+
depths.add(thickness);
|
|
1216
|
+
}
|
|
1217
|
+
if (depths.size)
|
|
1218
|
+
say(`"${payload.levels[index].name}" stands on a ${[...depths].map(metres).join(', ')} floor over the ${metres(wallHeight)} walls of "${below}": its plate reaches down to their tops`);
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* The room a plan point of a level is in, its ceiling, and the height in that level's frame that
|
|
1222
|
+
* ceiling hangs things from. Under an opening in the ceiling (a void, a stairwell) it is the
|
|
1223
|
+
* ceiling above, the room's double height.
|
|
1224
|
+
*/
|
|
1225
|
+
const ceilingAt = (index, x, z) => {
|
|
1226
|
+
const levelId = levelNodes[index].id;
|
|
1227
|
+
const room = Object.values(written).find((node) => node.type === 'zone' &&
|
|
1228
|
+
node.spaceRole === 'room' &&
|
|
1229
|
+
node.parentId === levelId &&
|
|
1230
|
+
pointInPolygon([x, z], node.polygon) &&
|
|
1231
|
+
!node.holes.some((hole) => pointInPolygon([x, z], hole)));
|
|
1232
|
+
const ceiling = room &&
|
|
1233
|
+
Object.values(written).find((node) => node.type === 'ceiling' && node.zoneId === room.id && node.parentId === levelId);
|
|
1234
|
+
if (!room || !ceiling)
|
|
1235
|
+
return undefined;
|
|
1236
|
+
if (levelNodes[index + 1] && ceiling.holes.some((hole) => pointInPolygon([x, z], hole))) {
|
|
1237
|
+
const above = ceilingAt(index + 1, x, z);
|
|
1238
|
+
const climb = payload.levels[index + 1].elevation - payload.levels[index].elevation;
|
|
1239
|
+
if (above)
|
|
1240
|
+
return { ...above, height: above.height + climb };
|
|
1241
|
+
}
|
|
1242
|
+
return { room, ceiling, height: resolveCeilingHeight(ceiling, written) - CEILING_DRAW_OFFSET };
|
|
1243
|
+
};
|
|
1244
|
+
// ---------- stairs, as create_stair builds them: up to the next level, opening its floor ----------
|
|
1245
|
+
let stairsWritten = false;
|
|
1246
|
+
payload.levels.forEach((level, index) => {
|
|
1247
|
+
const levelNode = levelNodes[index];
|
|
1248
|
+
const upper = levelNodes[index + 1];
|
|
1249
|
+
(level.stairs ?? []).forEach((stair, i) => {
|
|
1250
|
+
const id = `stair/${index}/${i}`;
|
|
1251
|
+
if (!upper) {
|
|
1252
|
+
defect(id, 'no_level_above', `A stair on "${level.name}", the top level, has nothing to rise to: give the level it reaches after this one in levels.`);
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
try {
|
|
1256
|
+
const outcome = createStair(written, {
|
|
1257
|
+
x: stair.start[0],
|
|
1258
|
+
z: stair.start[1],
|
|
1259
|
+
rotation: stair.rotation,
|
|
1260
|
+
layout: stair.layout,
|
|
1261
|
+
width: stair.width,
|
|
1262
|
+
...(stair.turn ? { turn: stair.turn } : {}),
|
|
1263
|
+
...(stair.length === undefined ? {} : { length: stair.length }),
|
|
1264
|
+
...(stair.steps === undefined ? {} : { steps: stair.steps }),
|
|
1265
|
+
...(stair.lowerSteps === undefined ? {} : { lowerSteps: stair.lowerSteps }),
|
|
1266
|
+
...(stair.railing ? { railingMode: stair.railing } : {}),
|
|
1267
|
+
...(stair.railingStyle ? { railingStyle: stair.railingStyle } : {}),
|
|
1268
|
+
...(stair.name ? { name: stair.name } : {}),
|
|
1269
|
+
levelId: levelNode.id,
|
|
1270
|
+
toLevelId: upper.id,
|
|
1271
|
+
}, { ...context, activeLevelId: levelNode.id });
|
|
1272
|
+
const result = outcome.result;
|
|
1273
|
+
// create_stair mints fresh ids; the conversion's own keep the stair across revisions.
|
|
1274
|
+
let json = JSON.stringify(outcome.changes);
|
|
1275
|
+
(outcome.changes?.create ?? []).forEach(({ node }, k) => {
|
|
1276
|
+
const kind = node.id.slice(0, node.id.indexOf('_'));
|
|
1277
|
+
json = json.replaceAll(node.id, mint(kind, k ? `${id}/${k}` : id));
|
|
1278
|
+
});
|
|
1279
|
+
const changes = JSON.parse(json);
|
|
1280
|
+
const [made, ...rest] = changes.create;
|
|
1281
|
+
changes.create = [
|
|
1282
|
+
{
|
|
1283
|
+
...made,
|
|
1284
|
+
node: {
|
|
1285
|
+
...made.node,
|
|
1286
|
+
metadata: { ...made.node.metadata, authorScene: { id } },
|
|
1287
|
+
},
|
|
1288
|
+
},
|
|
1289
|
+
...rest,
|
|
1290
|
+
];
|
|
1291
|
+
written = applySceneChanges(written, changes);
|
|
1292
|
+
const finishes = [
|
|
1293
|
+
['body', stair.color],
|
|
1294
|
+
['treads', stair.treads ?? stair.color],
|
|
1295
|
+
['railing', stair.railingColor],
|
|
1296
|
+
];
|
|
1297
|
+
for (const [role, color] of finishes) {
|
|
1298
|
+
const finish = finishRef(color, id);
|
|
1299
|
+
if (!finish)
|
|
1300
|
+
continue;
|
|
1301
|
+
if (!nodeSlotDeclarations(written[made.node.id])?.some((slot) => slot.slotId === role)) {
|
|
1302
|
+
say(`${id}: it has no ${role}, its ${role === 'railing' ? 'railingColor' : 'color'} ignored`);
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
try {
|
|
1306
|
+
const { changes } = paint(written, { targets: [made.node.id], material: finish, role }, paintContext(levelNode.id));
|
|
1307
|
+
written = applySceneChanges(written, changes);
|
|
1308
|
+
}
|
|
1309
|
+
catch (error) {
|
|
1310
|
+
defect(id, 'paint_refused', error.message);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
report.ids[id] = made.node.id;
|
|
1314
|
+
report.stairs++;
|
|
1315
|
+
stairsWritten = true;
|
|
1316
|
+
if (result.slabHoleCut)
|
|
1317
|
+
say(`${id}: ${result.message}`);
|
|
1318
|
+
else
|
|
1319
|
+
warn(id, 'no_floor_opening', result.message);
|
|
1320
|
+
}
|
|
1321
|
+
catch (error) {
|
|
1322
|
+
const e = error;
|
|
1323
|
+
defect(id, e.code ?? 'stair_refused', e.message ?? String(error));
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
});
|
|
1327
|
+
/** The plan footprints of a level's walls as written, each grown by `grow` on both faces. */
|
|
1328
|
+
const wallFootprints = (levelId, grow = 0) => {
|
|
1329
|
+
const walls = Object.values(written).flatMap((node) => node.type === 'wall' && node.parentId === levelId
|
|
1330
|
+
? [grow ? { ...node, thickness: getWallThickness(node) + 2 * grow } : node]
|
|
1331
|
+
: []);
|
|
1332
|
+
const miters = calculateLevelMiters(walls);
|
|
1333
|
+
return walls.map((wall) => getWallPlanFootprint(wall, miters).map(({ x, y }) => [x, y]));
|
|
1334
|
+
};
|
|
1335
|
+
// ---------- voids: openings in a level's floor, the room below rising through them ----------
|
|
1336
|
+
const voidCuts = new Map();
|
|
1337
|
+
const voidsCut = [];
|
|
1338
|
+
payload.levels.forEach((level, index) => {
|
|
1339
|
+
const levelNode = levelNodes[index];
|
|
1340
|
+
(level.voids ?? []).forEach((v, i) => {
|
|
1341
|
+
const id = `void/${index}/${i}`;
|
|
1342
|
+
const label = v.name ? `"${v.name}"` : `void ${i}`;
|
|
1343
|
+
if (index === 0) {
|
|
1344
|
+
defect(id, 'no_level_below', `${label} is in the floor of "${level.name}", the first level, with nothing under it to open onto: give it in the floor of the level above the room that rises through it.`);
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
// The cut stops at the faces of the walls standing on this floor, as the floor carries them,
|
|
1348
|
+
// and at the floor's own edge (a void drawn past a curved outside wall).
|
|
1349
|
+
const floor = union(Object.values(written).flatMap((node) => node.type === 'slab' && node.parentId === levelNode.id && node.plateRole === 'base'
|
|
1350
|
+
? [node.polygon]
|
|
1351
|
+
: []));
|
|
1352
|
+
const cut = intersection(difference(v.polygon, union(wallFootprints(levelNode.id))), floor).filter((part) => area([part]) > 1e-4);
|
|
1353
|
+
const opened = area(cut);
|
|
1354
|
+
if (opened < 1e-4) {
|
|
1355
|
+
defect(id, 'no_floor', `No floor of "${level.name}" lies under ${label} clear of its walls: there is nothing to open.`);
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
if (cut.some((part) => part.holes.length))
|
|
1359
|
+
warn(id, 'wall_in_void', `A wall of "${level.name}" stands inside ${label}, over the opening around it: end the void at the wall, or move the wall.`);
|
|
1360
|
+
let cutsCeiling = false;
|
|
1361
|
+
cut.forEach((part, k) => {
|
|
1362
|
+
const plan = cutFloorOpening(written, {
|
|
1363
|
+
levelId: levelNode.id,
|
|
1364
|
+
polygon: part.outer,
|
|
1365
|
+
drawnOn: 'floor',
|
|
1366
|
+
mintId: () => mint('floor-opening', k ? `${id}/${k}` : id),
|
|
1367
|
+
});
|
|
1368
|
+
for (const change of plan.changes) {
|
|
1369
|
+
if (change.op === 'create') {
|
|
1370
|
+
const opening = change.node;
|
|
1371
|
+
cutsCeiling ||= opening.cutsAdjacent;
|
|
1372
|
+
written[opening.id] = {
|
|
1373
|
+
...opening,
|
|
1374
|
+
name: v.name ?? 'Void',
|
|
1375
|
+
metadata: { ...opening.metadata, authorScene: { id } },
|
|
1376
|
+
};
|
|
1377
|
+
if (!k)
|
|
1378
|
+
report.ids[id] = opening.id;
|
|
1379
|
+
}
|
|
1380
|
+
else if (change.op === 'update')
|
|
1381
|
+
written[change.id] = { ...written[change.id], ...change.data };
|
|
1382
|
+
}
|
|
1383
|
+
});
|
|
1384
|
+
report.voids++;
|
|
1385
|
+
say(`${id}: the floor of "${level.name}" opened over ${opened.toFixed(1)} m², ${cutsCeiling ? `and the ceiling of "${payload.levels[index - 1].name}" under it` : `with no ceiling of "${payload.levels[index - 1].name}" under it`}`);
|
|
1386
|
+
voidCuts.set(index, union([...(voidCuts.get(index) ?? []), ...cut]));
|
|
1387
|
+
voidsCut.push({
|
|
1388
|
+
id,
|
|
1389
|
+
index,
|
|
1390
|
+
cut,
|
|
1391
|
+
railing: v.railing === null ? null : (v.railing ?? { height: 1.1 }),
|
|
1392
|
+
});
|
|
1393
|
+
});
|
|
1394
|
+
});
|
|
1395
|
+
// The openings the stairs and voids own cut the floor plates and ceilings they pass, as on load.
|
|
1396
|
+
if (stairsWritten || report.voids)
|
|
1397
|
+
written = {
|
|
1398
|
+
...reconcileSceneStructure({ nodes: written, levelIds, mintId: (kind) => mint(kind) }).nodes,
|
|
1399
|
+
};
|
|
1400
|
+
// ---------- each void's railing: along its open edges, on the floor, as the slab railing looks ----------
|
|
1401
|
+
for (const { id, index, cut, railing } of voidsCut) {
|
|
1402
|
+
const levelNode = levelNodes[index];
|
|
1403
|
+
// Inside the double height the floor's edge round the void continues the walls it rises
|
|
1404
|
+
// between: it wears their inside paint, where the facade's edge keeps the cladding.
|
|
1405
|
+
const insides = (payload.levels[index - 1].walls ?? []).flatMap((w) => w.colorInside ? [w.colorInside] : []);
|
|
1406
|
+
const edge = finishRef(mostCommon(insides), id);
|
|
1407
|
+
if (edge)
|
|
1408
|
+
for (const zone of Object.values(written))
|
|
1409
|
+
if (zone.type === 'zone' &&
|
|
1410
|
+
zone.parentId === levelNode.id &&
|
|
1411
|
+
zone.spaceRole === 'room' &&
|
|
1412
|
+
area(intersection(cut, zone.polygon)) > 1e-4)
|
|
1413
|
+
written[zone.id] = { ...zone, floorEdgeFinish: edge };
|
|
1414
|
+
if (!railing)
|
|
1415
|
+
continue;
|
|
1416
|
+
// A run stops at a wall standing on the edge and where a stair arrives.
|
|
1417
|
+
const arrivals = Object.values(written).flatMap((node) => node.type === 'stair' && node.toLevelId === levelNode.id
|
|
1418
|
+
? [stairArrivalOpening(node, written)]
|
|
1419
|
+
: []);
|
|
1420
|
+
const cover = union([
|
|
1421
|
+
...wallFootprints(levelNode.id, 0.005),
|
|
1422
|
+
...arrivals.filter((ring) => ring.length >= 3),
|
|
1423
|
+
]);
|
|
1424
|
+
const runs = exposedIntervals(cut, [], cover).filter(({ start, end }) => dist(start, end) >= 0.1);
|
|
1425
|
+
const plate = Object.values(written)
|
|
1426
|
+
.filter((node) => node.type === 'slab' && node.parentId === levelNode.id && node.plateRole === 'base')
|
|
1427
|
+
.map((node) => ({ node, shared: area(intersection(cut, node.polygon)) }))
|
|
1428
|
+
.sort((a, b) => b.shared - a.shared)[0]?.node;
|
|
1429
|
+
const fences = runs.map((run, k) => FenceNode.parse({
|
|
1430
|
+
object: 'node',
|
|
1431
|
+
id: mint('fence', `${id}/railing/${k}`),
|
|
1432
|
+
parentId: levelNode.id,
|
|
1433
|
+
visible: true,
|
|
1434
|
+
metadata: { authorScene: { id } },
|
|
1435
|
+
name: 'Railing',
|
|
1436
|
+
start: run.start,
|
|
1437
|
+
end: run.end,
|
|
1438
|
+
height: railing.height,
|
|
1439
|
+
style: 'rail',
|
|
1440
|
+
baseStyle: 'floating',
|
|
1441
|
+
baseHeight: 0,
|
|
1442
|
+
postCap: 'none',
|
|
1443
|
+
...(plate ? { supportSlabId: plate.id } : {}),
|
|
1444
|
+
}));
|
|
1445
|
+
if (!fences.length)
|
|
1446
|
+
continue;
|
|
1447
|
+
const level = written[levelNode.id];
|
|
1448
|
+
written[level.id] = {
|
|
1449
|
+
...level,
|
|
1450
|
+
children: [...level.children, ...fences.map((f) => f.id)],
|
|
1451
|
+
};
|
|
1452
|
+
for (const fence of fences)
|
|
1453
|
+
written[fence.id] = fence;
|
|
1454
|
+
const finish = finishRef(railing.color, id);
|
|
1455
|
+
if (finish)
|
|
1456
|
+
try {
|
|
1457
|
+
const { changes } = paint(written, { targets: fences.map((fence) => fence.id), material: finish }, paintContext(levelNode.id));
|
|
1458
|
+
for (const u of changes?.update ?? [])
|
|
1459
|
+
written[u.id] = { ...written[u.id], ...u.data };
|
|
1460
|
+
}
|
|
1461
|
+
catch (error) {
|
|
1462
|
+
defect(id, 'paint_refused', error.message);
|
|
1463
|
+
}
|
|
1464
|
+
say(`${id}: a ${metres(railing.height)} railing along ${metres(runs.reduce((sum, run) => sum + dist(run.start, run.end), 0))} of its edges`);
|
|
1465
|
+
}
|
|
1466
|
+
// ---------- items, on their floors and ceilings, then each wall's paint ----------
|
|
1467
|
+
payload.levels.forEach((level, index) => {
|
|
1468
|
+
const levelNode = levelNodes[index];
|
|
1469
|
+
const { walls, wallNodes } = levelWalls[index];
|
|
711
1470
|
// ---------- items, hosts first ----------
|
|
712
1471
|
const pending = [...(level.items ?? [])];
|
|
713
1472
|
let progress = true;
|
|
@@ -725,14 +1484,20 @@ export function authorScene(scene, input, context) {
|
|
|
725
1484
|
defect(item.id, 'compile_failed', input.compileErrors?.[item.id] ?? 'The module did not compile.');
|
|
726
1485
|
continue;
|
|
727
1486
|
}
|
|
1487
|
+
if (/^(stairs?|staircases?)$/i.test(item.category))
|
|
1488
|
+
warn(item.id, 'use_stairs', `"${item.name}" is a stair written as an item: give it as its level's stairs, so it rises with the storey and opens the floor above.`);
|
|
728
1489
|
const world = [item.position[0], item.position[1], item.position[2]];
|
|
729
1490
|
const yaw = (item.rotation * Math.PI) / 180;
|
|
730
1491
|
let placed;
|
|
1492
|
+
if (hostWall && isCurvedWall(hostWall.node)) {
|
|
1493
|
+
defect(item.id, 'curved_host', `Wall ${item.host} is curved: nothing hangs on a curved wall, as in the editor. Hang "${item.name}" on a straight wall, or stand it on the floor.`);
|
|
1494
|
+
continue;
|
|
1495
|
+
}
|
|
731
1496
|
if (hostWall) {
|
|
732
1497
|
// On the wall as place_items puts a wall-side item there: along it from its start, the
|
|
733
1498
|
// payload's height for its bottom, on the face nearest the point, facing the room.
|
|
734
1499
|
const [width, height] = scriptedSize(compiled.manifest);
|
|
735
|
-
const wallHeight = hostWall.
|
|
1500
|
+
const wallHeight = hostWall.height;
|
|
736
1501
|
if (height > wallHeight) {
|
|
737
1502
|
defect(item.id, 'item_too_tall', `"${item.name}" is ${metres(height)} tall; wall ${item.host} is ${metres(wallHeight)}.`);
|
|
738
1503
|
continue;
|
|
@@ -773,8 +1538,27 @@ export function authorScene(scene, input, context) {
|
|
|
773
1538
|
},
|
|
774
1539
|
};
|
|
775
1540
|
}
|
|
1541
|
+
else if (compiled.mount === 'ceiling' && !item.host) {
|
|
1542
|
+
// From the ceiling of the room its plan point is in, its top on that ceiling.
|
|
1543
|
+
const over = ceilingAt(index, world[0], world[2]);
|
|
1544
|
+
if (!over) {
|
|
1545
|
+
defect(item.id, 'no_ceiling', `"${item.name}" hangs from a ceiling, and no room on "${level.name}" contains (${world[0]}, ${world[2]}): move it into a room, or build it with mount 'floor'.`);
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
say(`${item.id}: hung from the ceiling of ${over.room.name}, ${metres(over.height)} up`);
|
|
1549
|
+
placed = {
|
|
1550
|
+
compiled,
|
|
1551
|
+
parentId: over.ceiling.id,
|
|
1552
|
+
position: [world[0], 0, world[2]],
|
|
1553
|
+
rotation: item.rotation,
|
|
1554
|
+
frame: { position: [world[0], over.height, world[2]], yaw },
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
776
1557
|
else {
|
|
777
1558
|
const host = hostItemId ? itemFrames.get(item.host) : undefined;
|
|
1559
|
+
const opening = voidCuts.get(index);
|
|
1560
|
+
if (!host && opening && containsPoint(opening, [world[0], world[2]]))
|
|
1561
|
+
warn(item.id, 'over_void', `"${item.name}" stands at (${world[0]}, ${world[2]}), over a void in the floor of "${level.name}": nothing is under it. Move it onto the floor.`);
|
|
778
1562
|
placed = {
|
|
779
1563
|
compiled,
|
|
780
1564
|
parentId: hostItemId ?? levelNode.id,
|
|
@@ -784,7 +1568,7 @@ export function authorScene(scene, input, context) {
|
|
|
784
1568
|
};
|
|
785
1569
|
}
|
|
786
1570
|
try {
|
|
787
|
-
const outcome = addObject(
|
|
1571
|
+
const outcome = addObject(written, {
|
|
788
1572
|
compiled: { ...placed.compiled, nodeId: mint('item', item.id) },
|
|
789
1573
|
name: item.name,
|
|
790
1574
|
category: item.category,
|
|
@@ -795,13 +1579,16 @@ export function authorScene(scene, input, context) {
|
|
|
795
1579
|
...(placed.side ? { side: placed.side } : {}),
|
|
796
1580
|
}, { ...context, activeLevelId: levelNode.id });
|
|
797
1581
|
for (const { node, parentId } of outcome.changes?.create ?? []) {
|
|
798
|
-
|
|
1582
|
+
written[node.id] = {
|
|
799
1583
|
...node,
|
|
800
1584
|
slots: exactSlots(compiled, item.id),
|
|
801
1585
|
...(placed.wallT === undefined ? {} : { wallT: placed.wallT }),
|
|
802
1586
|
};
|
|
803
|
-
const parent =
|
|
804
|
-
parent.
|
|
1587
|
+
const parent = written[parentId ?? levelNode.id];
|
|
1588
|
+
written[parent.id] = {
|
|
1589
|
+
...parent,
|
|
1590
|
+
children: [...(parent.children ?? []), node.id],
|
|
1591
|
+
};
|
|
805
1592
|
itemNodeIds.set(item.id, node.id);
|
|
806
1593
|
itemFrames.set(item.id, placed.frame);
|
|
807
1594
|
report.ids[item.id] = node.id;
|
|
@@ -817,15 +1604,46 @@ export function authorScene(scene, input, context) {
|
|
|
817
1604
|
for (const item of pending)
|
|
818
1605
|
defect(item.id, 'host_missing', `Its host ${item.host} was not placed.`);
|
|
819
1606
|
// ---------- wall paint, through the editor's paint: both faces, or each its own ----------
|
|
1607
|
+
const inside = {
|
|
1608
|
+
left: [],
|
|
1609
|
+
right: [],
|
|
1610
|
+
both: [],
|
|
1611
|
+
neither: [],
|
|
1612
|
+
};
|
|
820
1613
|
for (const w of walls) {
|
|
821
1614
|
const node = wallNodes.get(w.spec.id)?.node;
|
|
822
1615
|
if (!node)
|
|
823
1616
|
continue;
|
|
824
|
-
const faces = {
|
|
825
|
-
a: w.spec.colorLeft ?? w.spec.color,
|
|
826
|
-
b: w.spec.colorRight ?? w.spec.color,
|
|
827
|
-
};
|
|
828
1617
|
const coats = [];
|
|
1618
|
+
if (w.spec.curtain) {
|
|
1619
|
+
// Glass and mullions: no face to paint; its frame takes the colour.
|
|
1620
|
+
const ignored = ['color', 'colorInside', 'colorOutside', 'colorLeft', 'colorRight'].filter((field) => w.spec[field]);
|
|
1621
|
+
if (ignored.length)
|
|
1622
|
+
say(`wall ${w.spec.id}: a curtain wall has no faces to paint, its ${ignored.join(', ')} ignored; curtain.frameColor colours its frame`);
|
|
1623
|
+
const frame = finishRef(w.spec.curtain.frameColor, w.spec.id);
|
|
1624
|
+
if (frame)
|
|
1625
|
+
coats.push({ role: 'curtain-frame', material: frame });
|
|
1626
|
+
}
|
|
1627
|
+
const { colorInside, colorOutside } = w.spec.curtain ? {} : w.spec;
|
|
1628
|
+
// A face is inside when a room of this level stands on it, known once the rooms are derived.
|
|
1629
|
+
const cover = colorInside || colorOutside ? roomFaceCover(written, node.id) : undefined;
|
|
1630
|
+
const meant = (face) => cover && (cover[face] === 'none' ? colorOutside : colorInside);
|
|
1631
|
+
if (cover) {
|
|
1632
|
+
const faces = ['a', 'b'].filter((face) => cover[face] !== 'none');
|
|
1633
|
+
inside[faces.length === 2
|
|
1634
|
+
? 'both'
|
|
1635
|
+
: faces.length === 0
|
|
1636
|
+
? 'neither'
|
|
1637
|
+
: faces[0] === 'a'
|
|
1638
|
+
? 'left'
|
|
1639
|
+
: 'right'].push(w.spec.id);
|
|
1640
|
+
}
|
|
1641
|
+
const faces = w.spec.curtain
|
|
1642
|
+
? {}
|
|
1643
|
+
: {
|
|
1644
|
+
a: w.spec.colorLeft ?? meant('a') ?? w.spec.color,
|
|
1645
|
+
b: w.spec.colorRight ?? meant('b') ?? w.spec.color,
|
|
1646
|
+
};
|
|
829
1647
|
if (faces.a && faces.a === faces.b) {
|
|
830
1648
|
const ref = finishRef(faces.a, w.spec.id);
|
|
831
1649
|
if (ref)
|
|
@@ -839,84 +1657,24 @@ export function authorScene(scene, input, context) {
|
|
|
839
1657
|
}
|
|
840
1658
|
for (const coat of coats) {
|
|
841
1659
|
try {
|
|
842
|
-
const { changes } = paint(
|
|
1660
|
+
const { changes } = paint(written, { targets: [node.id], ...coat }, paintContext(levelNode.id));
|
|
843
1661
|
for (const u of changes?.update ?? [])
|
|
844
|
-
|
|
1662
|
+
written[u.id] = { ...written[u.id], ...u.data };
|
|
845
1663
|
}
|
|
846
1664
|
catch (error) {
|
|
847
1665
|
defect(w.spec.id, 'paint_refused', error.message);
|
|
848
1666
|
}
|
|
849
1667
|
}
|
|
850
1668
|
}
|
|
1669
|
+
const resolved = [
|
|
1670
|
+
inside.left.length && `the left face of ${inside.left.join(', ')}`,
|
|
1671
|
+
inside.right.length && `the right face of ${inside.right.join(', ')}`,
|
|
1672
|
+
inside.both.length && `both faces of ${inside.both.join(', ')}`,
|
|
1673
|
+
inside.neither.length && `neither face of ${inside.neither.join(', ')}`,
|
|
1674
|
+
].filter(Boolean);
|
|
1675
|
+
if (resolved.length)
|
|
1676
|
+
say(`colorInside / colorOutside on "${level.name}", left and right as seen from each wall's start to its end: inside is ${resolved.join('; ')}`);
|
|
851
1677
|
});
|
|
852
|
-
report.levels = levelNodes.length;
|
|
853
|
-
if (untypedWindows)
|
|
854
|
-
say(`${untypedWindows} window${untypedWindows === 1 ? ' has' : 's have'} no type: written fixed (cannot open); give type`);
|
|
855
|
-
// ---------- which face of each wall looks into a room: read from the plan, as the editor does ----------
|
|
856
|
-
for (const levelNode of levelNodes) {
|
|
857
|
-
const boundaries = Object.values(draft).filter((n) => n.parentId === levelNode.id && (n.type === 'wall' || n.type === 'separator'));
|
|
858
|
-
for (const { wallId, ...sides } of detectSpacesForLevel(levelNode.id, boundaries).wallUpdates)
|
|
859
|
-
draft[wallId] = { ...draft[wallId], ...sides };
|
|
860
|
-
}
|
|
861
|
-
// ---------- rooms: Pascal's own derivation, then the payload's names and floors ----------
|
|
862
|
-
const { nodes: withZones, createdZoneIds } = migrateRoomZones(draft);
|
|
863
|
-
const zones = createdZoneIds.map((id) => ZoneNode.parse(withZones[id]));
|
|
864
|
-
for (const zone of zones) {
|
|
865
|
-
draft[zone.id] = zone;
|
|
866
|
-
const level = draft[zone.parentId];
|
|
867
|
-
draft[zone.parentId] = { ...level, children: [...level.children, zone.id] };
|
|
868
|
-
report.rooms++;
|
|
869
|
-
}
|
|
870
|
-
const claimed = new Map();
|
|
871
|
-
payload.levels.forEach((level, index) => {
|
|
872
|
-
const levelNode = levelNodes[index];
|
|
873
|
-
for (const [i, room] of level.rooms.entries()) {
|
|
874
|
-
const id = `room/${index}/${i}`;
|
|
875
|
-
const zone = zones.find((z) => z.parentId === levelNode.id && pointInPolygon(room.seed, z.polygon));
|
|
876
|
-
// Two seeds in one enclosure: the walls do not separate the rooms the payload names.
|
|
877
|
-
if (zone && claimed.has(zone.id)) {
|
|
878
|
-
defect(id, 'room_shares_enclosure', `"${room.name}" and "${claimed.get(zone.id)}" are one enclosure: no wall separates them.`);
|
|
879
|
-
continue;
|
|
880
|
-
}
|
|
881
|
-
if (!zone) {
|
|
882
|
-
defect(id, 'room_not_enclosed', `No closed room around (${room.seed[0]}, ${room.seed[1]}) "${room.name}": its walls do not meet.`);
|
|
883
|
-
continue;
|
|
884
|
-
}
|
|
885
|
-
if (room.floorRegions.length > MANY_REGIONS)
|
|
886
|
-
warn(id, 'many_regions', `"${room.name}" has ${room.floorRegions.length} floor regions: regions are fixed polygons that do not follow a resize; use one floor finish.`);
|
|
887
|
-
const finish = finishRef(room.floor, id);
|
|
888
|
-
const regions = room.floorRegions.map((r, k) => ({
|
|
889
|
-
id: `region_${k}`,
|
|
890
|
-
polygon: r.polygon,
|
|
891
|
-
finish: finishRef(r.finish, id) ?? r.finish,
|
|
892
|
-
}));
|
|
893
|
-
draft[zone.id] = ZoneNode.parse({
|
|
894
|
-
...zone,
|
|
895
|
-
name: room.name,
|
|
896
|
-
spaceRole: 'room',
|
|
897
|
-
...(finish || regions.length
|
|
898
|
-
? {
|
|
899
|
-
floor: {
|
|
900
|
-
...zone.floor,
|
|
901
|
-
...(finish ? { finish } : {}),
|
|
902
|
-
...(regions.length ? { regions } : {}),
|
|
903
|
-
},
|
|
904
|
-
}
|
|
905
|
-
: {}),
|
|
906
|
-
metadata: { ...zone.metadata, authorScene: { seed: room.seed } },
|
|
907
|
-
});
|
|
908
|
-
claimed.set(zone.id, room.name);
|
|
909
|
-
report.ids[id] = zone.id;
|
|
910
|
-
report.roomsNamed++;
|
|
911
|
-
}
|
|
912
|
-
});
|
|
913
|
-
// ---------- floors and ceilings, as the editor derives them on load ----------
|
|
914
|
-
const structure = reconcileSceneStructure({
|
|
915
|
-
nodes: draft,
|
|
916
|
-
levelIds: levelNodes.map((l) => l.id),
|
|
917
|
-
mintId: (kind) => mint(kind),
|
|
918
|
-
});
|
|
919
|
-
const written = { ...structure.nodes };
|
|
920
1678
|
// ---------- skirting, native on each wall, along the faces its rooms stand on ----------
|
|
921
1679
|
for (const { id, nodeId, levelId, skirting } of skirtings) {
|
|
922
1680
|
const finish = finishRef(skirting.color, id);
|
|
@@ -944,22 +1702,47 @@ export function authorScene(scene, input, context) {
|
|
|
944
1702
|
// Ceilings exist only after structure reconciliation; lights can now use their room's host.
|
|
945
1703
|
payload.levels.forEach((level, index) => {
|
|
946
1704
|
const levelNode = levelNodes[index];
|
|
947
|
-
|
|
948
|
-
|
|
1705
|
+
const entries = level.lights.map((light, i) => ({ id: `light/${index}/${i}`, light }));
|
|
1706
|
+
// A named room with no light of its own (none whose plan point is in it, given or built into
|
|
1707
|
+
// an item) gets a plain ceiling light at its seed.
|
|
1708
|
+
const lit = [
|
|
1709
|
+
...level.lights.map((light) => [light.position[0], light.position[2]]),
|
|
1710
|
+
...(level.items ?? []).flatMap((item) => {
|
|
1711
|
+
const frame = itemFrames.get(item.id);
|
|
1712
|
+
return frame && input.compiled[item.id]?.manifest.lights?.length
|
|
1713
|
+
? [[frame.position[0], frame.position[2]]]
|
|
1714
|
+
: [];
|
|
1715
|
+
}),
|
|
1716
|
+
];
|
|
1717
|
+
const seen = new Set();
|
|
1718
|
+
level.rooms.forEach((room, i) => {
|
|
1719
|
+
const zone = written[report.ids[`room/${index}/${i}`] ?? ''];
|
|
1720
|
+
if (!zone || seen.has(zone.id))
|
|
1721
|
+
return;
|
|
1722
|
+
seen.add(zone.id);
|
|
1723
|
+
const inside = (p) => pointInPolygon(p, zone.polygon) && !zone.holes.some((hole) => pointInPolygon(p, hole));
|
|
1724
|
+
if (lit.some(inside))
|
|
1725
|
+
return;
|
|
1726
|
+
const over = ceilingAt(index, room.seed[0], room.seed[1]);
|
|
1727
|
+
if (!over)
|
|
1728
|
+
return;
|
|
1729
|
+
entries.push({
|
|
1730
|
+
id: roomLightId(index, i),
|
|
1731
|
+
light: {
|
|
1732
|
+
position: [room.seed[0], over.height - 0.1, room.seed[1]],
|
|
1733
|
+
color: '#ffffff',
|
|
1734
|
+
intensity: 1,
|
|
1735
|
+
},
|
|
1736
|
+
room: zone.name ?? room.name,
|
|
1737
|
+
});
|
|
1738
|
+
});
|
|
1739
|
+
for (const { id, light, room: unlit } of entries) {
|
|
949
1740
|
const at = [light.position[0], light.position[1], light.position[2]];
|
|
950
1741
|
let hostId = light.fixture ? itemNodeIds.get(light.fixture) : undefined;
|
|
951
1742
|
let hostFrame = light.fixture ? itemFrames.get(light.fixture) : undefined;
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
node.parentId === levelNode.id &&
|
|
956
|
-
pointInPolygon([at[0], at[2]], node.polygon) &&
|
|
957
|
-
!node.holes.some((hole) => pointInPolygon([at[0], at[2]], hole)))
|
|
958
|
-
: undefined;
|
|
959
|
-
const ceiling = room
|
|
960
|
-
? Object.values(written).find((node) => node.type === 'ceiling' && node.zoneId === room.id && node.parentId === levelNode.id)
|
|
961
|
-
: undefined;
|
|
962
|
-
if (ceiling) {
|
|
1743
|
+
const over = light.fixture ? undefined : ceilingAt(index, at[0], at[2]);
|
|
1744
|
+
if (over) {
|
|
1745
|
+
const { room, ceiling } = over;
|
|
963
1746
|
const compiled = input.compiled[id];
|
|
964
1747
|
if (!compiled) {
|
|
965
1748
|
defect(id, 'compile_failed', input.compileErrors?.[id] ?? 'The ceiling point module did not compile.');
|
|
@@ -980,12 +1763,14 @@ export function authorScene(scene, input, context) {
|
|
|
980
1763
|
written[ceiling.id] = { ...ceiling, children: [...ceiling.children, node.id] };
|
|
981
1764
|
hostId = node.id;
|
|
982
1765
|
hostFrame = {
|
|
983
|
-
position: [at[0],
|
|
1766
|
+
position: [at[0], over.height, at[2]],
|
|
984
1767
|
yaw: 0,
|
|
985
1768
|
};
|
|
986
1769
|
report.ids[id] = node.id;
|
|
987
1770
|
report.items++;
|
|
988
|
-
say(
|
|
1771
|
+
say(unlit
|
|
1772
|
+
? `${unlit} had no light: a ceiling light at its centre`
|
|
1773
|
+
: `light at [${at.join(', ')}] has no fixture: a ceiling point in ${room.name}`);
|
|
989
1774
|
}
|
|
990
1775
|
catch (error) {
|
|
991
1776
|
const e = error;
|
|
@@ -1023,6 +1808,9 @@ export function authorScene(scene, input, context) {
|
|
|
1023
1808
|
color: light.color,
|
|
1024
1809
|
intensityRange: [0, light.intensity],
|
|
1025
1810
|
offset: toLocal(hostFrame, at),
|
|
1811
|
+
spot: lightSpot(light, at, hostFrame),
|
|
1812
|
+
shadows: light.shadows,
|
|
1813
|
+
glow: light.glow,
|
|
1026
1814
|
},
|
|
1027
1815
|
];
|
|
1028
1816
|
written[hostNode.id] = {
|
|
@@ -1032,14 +1820,129 @@ export function authorScene(scene, input, context) {
|
|
|
1032
1820
|
report.lights++;
|
|
1033
1821
|
}
|
|
1034
1822
|
});
|
|
1035
|
-
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1823
|
+
// ---------- the roof: on the building's roof level above the top storey, as create_roof puts it ----------
|
|
1824
|
+
const roof = payload.roof;
|
|
1825
|
+
if (roof && (roof.segments.length || roof.items?.length)) {
|
|
1826
|
+
const top = levelNodes.at(-1);
|
|
1827
|
+
const segments = roof.segments.map((spec, i) => {
|
|
1828
|
+
const segment = RoofSegmentNode.parse({
|
|
1829
|
+
roofType: spec.roofType,
|
|
1830
|
+
width: spec.width,
|
|
1831
|
+
depth: spec.depth,
|
|
1832
|
+
wallHeight: 0,
|
|
1833
|
+
rotation: (spec.rotation * Math.PI) / 180,
|
|
1834
|
+
position: [spec.center[0], 0, spec.center[1]],
|
|
1835
|
+
...(spec.pitch !== undefined && { pitch: spec.pitch }),
|
|
1836
|
+
...(spec.gableOverhang !== undefined && { gableOverhang: spec.gableOverhang }),
|
|
1837
|
+
});
|
|
1838
|
+
// The overhang is asked in plan; the segment stores it along its slope (a barrel's, at its ends).
|
|
1839
|
+
const overhang = spec.roofType === 'barrel'
|
|
1840
|
+
? spec.overhang
|
|
1841
|
+
: spec.overhang / getSegmentSlopeFrame(segment).cosTheta;
|
|
1842
|
+
return { ...segment, id: mint('rseg', `roof/segment/${i}`), overhang };
|
|
1843
|
+
});
|
|
1844
|
+
const peak = Math.max(0, ...segments.map((segment) => getActiveRoofHeight(segment)));
|
|
1845
|
+
// Nothing stands above the payload's top level in its candidate: the plan is a roof level.
|
|
1846
|
+
const plan = planRoofLevel(written, top.id, peak);
|
|
1847
|
+
const created = plan.kind === 'roof' ? plan.create : null;
|
|
1848
|
+
const levelId = created ? mint('level', 'level/roof') : plan.levelId;
|
|
1849
|
+
if (created)
|
|
1850
|
+
written = applySceneChanges(written, {
|
|
1851
|
+
create: [{ node: { ...created, id: levelId }, parentId: building.id }],
|
|
1852
|
+
});
|
|
1853
|
+
report.ids['level/roof'] = levelId;
|
|
1854
|
+
// A roof measured from the top storey's floor stands this much lower on the roof level.
|
|
1855
|
+
const offset = plan.storeyOffset;
|
|
1856
|
+
let rises = peak;
|
|
1857
|
+
if (segments.length) {
|
|
1858
|
+
const cx = segments.reduce((sum, s) => sum + s.position[0], 0) / segments.length;
|
|
1859
|
+
const cz = segments.reduce((sum, s) => sum + s.position[2], 0) / segments.length;
|
|
1860
|
+
const node = RoofNode.parse({
|
|
1861
|
+
id: mint('roof', 'roof'),
|
|
1862
|
+
name: roof.name ?? 'Roof',
|
|
1863
|
+
parentId: levelId,
|
|
1864
|
+
position: [cx, 0, cz],
|
|
1865
|
+
rotation: 0,
|
|
1866
|
+
support: { kind: 'walls' },
|
|
1867
|
+
children: segments.map((segment) => segment.id),
|
|
1868
|
+
metadata: { authorScene: { id: 'roof' } },
|
|
1869
|
+
});
|
|
1870
|
+
written = applySceneChanges(written, {
|
|
1871
|
+
create: [
|
|
1872
|
+
{ node, parentId: levelId },
|
|
1873
|
+
...segments.map((segment) => ({
|
|
1874
|
+
node: {
|
|
1875
|
+
...segment,
|
|
1876
|
+
parentId: node.id,
|
|
1877
|
+
position: [segment.position[0] - cx, 0, segment.position[2] - cz],
|
|
1878
|
+
},
|
|
1879
|
+
parentId: node.id,
|
|
1880
|
+
})),
|
|
1881
|
+
],
|
|
1882
|
+
});
|
|
1883
|
+
const elevation = resolveRoofElevation(written[node.id], written);
|
|
1884
|
+
written[node.id] = { ...node, position: [cx, elevation, cz] };
|
|
1885
|
+
rises = elevation + peak;
|
|
1886
|
+
report.ids.roof = node.id;
|
|
1887
|
+
report.roofSegments = segments.length;
|
|
1888
|
+
for (const [role, color] of [
|
|
1889
|
+
['top', roof.color],
|
|
1890
|
+
['wall', roof.wallColor],
|
|
1891
|
+
]) {
|
|
1892
|
+
const finish = finishRef(color, 'roof');
|
|
1893
|
+
if (finish)
|
|
1894
|
+
try {
|
|
1895
|
+
const { changes } = paint(written, { targets: [node.id], material: finish, role }, paintContext(levelId));
|
|
1896
|
+
for (const u of changes?.update ?? [])
|
|
1897
|
+
written[u.id] = { ...written[u.id], ...u.data };
|
|
1898
|
+
}
|
|
1899
|
+
catch (error) {
|
|
1900
|
+
defect('roof', 'paint_refused', error.message);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
for (const item of roof.items ?? []) {
|
|
1905
|
+
const compiled = input.compiled[item.id];
|
|
1906
|
+
if (!compiled) {
|
|
1907
|
+
defect(item.id, 'compile_failed', input.compileErrors?.[item.id] ?? 'The module did not compile.');
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
try {
|
|
1911
|
+
const outcome = addObject(written, {
|
|
1912
|
+
compiled: { ...compiled, nodeId: mint('item', item.id) },
|
|
1913
|
+
name: item.name,
|
|
1914
|
+
category: item.category,
|
|
1915
|
+
reason: `author_scene: ${item.name}`,
|
|
1916
|
+
position: [item.position[0], item.position[1] + offset, item.position[2]],
|
|
1917
|
+
rotation: item.rotation,
|
|
1918
|
+
parentId: levelId,
|
|
1919
|
+
role: 'roof',
|
|
1920
|
+
}, { ...context, activeLevelId: levelId });
|
|
1921
|
+
for (const { node } of outcome.changes?.create ?? []) {
|
|
1922
|
+
written = applySceneChanges(written, {
|
|
1923
|
+
create: [
|
|
1924
|
+
{
|
|
1925
|
+
node: { ...node, slots: exactSlots(compiled, item.id) },
|
|
1926
|
+
parentId: levelId,
|
|
1927
|
+
},
|
|
1928
|
+
],
|
|
1929
|
+
});
|
|
1930
|
+
report.ids[item.id] = node.id;
|
|
1931
|
+
report.items++;
|
|
1932
|
+
rises = Math.max(rises, item.position[1] + offset + scriptedSize(compiled.manifest)[1]);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
catch (error) {
|
|
1936
|
+
const e = error;
|
|
1937
|
+
defect(item.id, e.code ?? 'item_refused', e.message ?? String(error));
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
// A roof level made for it is as tall as the roof rises over it.
|
|
1941
|
+
if (created)
|
|
1942
|
+
written[levelId] = { ...written[levelId], height: Math.max(rises, 0.2) };
|
|
1943
|
+
}
|
|
1944
|
+
const nodes = plain(Object.fromEntries(Object.entries(written).filter(([id]) => !scaffoldIds.has(id))));
|
|
1945
|
+
const candidate = { buildingId: building.id, seed, ids: report.ids, nodes };
|
|
1043
1946
|
const outcome = {
|
|
1044
1947
|
result: report,
|
|
1045
1948
|
candidate,
|
|
@@ -1053,24 +1956,88 @@ export function finalizeAuthorScene(scene, outcome) {
|
|
|
1053
1956
|
const building = scene[outcome.buildingId];
|
|
1054
1957
|
if (building?.type !== 'building')
|
|
1055
1958
|
refuse('no_building', 'The target building no longer exists.');
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1959
|
+
const { candidate } = outcome;
|
|
1960
|
+
const current = Object.fromEntries(Object.entries(scene).map(([id, node]) => [id, withoutBookkeeping(node)]));
|
|
1961
|
+
const base = authoredBaseline(scene, building)?.nodes ??
|
|
1962
|
+
Object.fromEntries(queryNodes(current, { type: 'level', parentId: building.id }).map((node) => [node.id, node]));
|
|
1963
|
+
const changes = authoredSceneChanges(current, base, candidate.nodes);
|
|
1964
|
+
// Each written node keeps its own baseline; each written level lists what stands on it and the
|
|
1965
|
+
// building what it holds itself (its units), so a revision finds every node of the last one.
|
|
1966
|
+
const holderOf = (id) => {
|
|
1967
|
+
let node = candidate.nodes[id];
|
|
1968
|
+
while (node && node.type !== 'level') {
|
|
1969
|
+
if (node.parentId === building.id)
|
|
1970
|
+
return building.id;
|
|
1971
|
+
node = node.parentId ? candidate.nodes[node.parentId] : undefined;
|
|
1972
|
+
}
|
|
1973
|
+
return node?.id;
|
|
1974
|
+
};
|
|
1975
|
+
const keyOf = new Map(Object.entries(candidate.ids).map(([key, id]) => [id, key]));
|
|
1976
|
+
const writtenOn = new Map();
|
|
1977
|
+
for (const id of Object.keys(candidate.nodes)) {
|
|
1978
|
+
const holder = holderOf(id);
|
|
1979
|
+
if (!holder)
|
|
1980
|
+
continue;
|
|
1981
|
+
const written = writtenOn.get(holder) ?? { ids: [], keys: {} };
|
|
1982
|
+
written.ids.push(id);
|
|
1983
|
+
const key = keyOf.get(id);
|
|
1984
|
+
if (key)
|
|
1985
|
+
written.keys[key] = id;
|
|
1986
|
+
writtenOn.set(holder, written);
|
|
1987
|
+
}
|
|
1988
|
+
const bookkeeping = (id) => ({
|
|
1989
|
+
authorSceneBase: withoutBookkeeping(candidate.nodes[id]),
|
|
1990
|
+
...(writtenOn.has(id) ? { authorSceneWritten: writtenOn.get(id) } : {}),
|
|
1991
|
+
});
|
|
1992
|
+
const create = (changes.create ?? []).map((entry) => candidate.nodes[entry.node.id]
|
|
1993
|
+
? {
|
|
1994
|
+
...entry,
|
|
1995
|
+
node: {
|
|
1996
|
+
...entry.node,
|
|
1997
|
+
metadata: { ...entry.node.metadata, ...bookkeeping(entry.node.id) },
|
|
1998
|
+
},
|
|
1999
|
+
}
|
|
2000
|
+
: entry);
|
|
2001
|
+
const created = new Set(create.map((entry) => entry.node.id));
|
|
2002
|
+
const removed = new Set(changes.delete ?? []);
|
|
2003
|
+
const updated = new Map((changes.update ?? []).map((entry) => [entry.id, entry]));
|
|
2004
|
+
for (const id of Object.keys(candidate.nodes)) {
|
|
2005
|
+
const now = scene[id];
|
|
2006
|
+
if (created.has(id) || removed.has(id) || !now)
|
|
2007
|
+
continue;
|
|
2008
|
+
const kept = { ...bookkeeping(id) };
|
|
2009
|
+
const stored = Object.fromEntries(BOOKKEEPING.flatMap((key) => now.metadata && key in now.metadata ? [[key, now.metadata[key]]] : []));
|
|
2010
|
+
const entry = updated.get(id);
|
|
2011
|
+
if (!entry && equalJson(stored, kept))
|
|
2012
|
+
continue;
|
|
2013
|
+
const metadata = (entry?.data.metadata ??
|
|
2014
|
+
current[id]?.metadata ??
|
|
2015
|
+
{});
|
|
2016
|
+
updated.set(id, {
|
|
2017
|
+
id,
|
|
2018
|
+
data: { ...entry?.data, metadata: { ...metadata, ...kept } },
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
const state = { version: 2, buildingId: building.id, seed: candidate.seed };
|
|
2022
|
+
updated.set(building.id, {
|
|
2023
|
+
id: building.id,
|
|
2024
|
+
data: {
|
|
2025
|
+
...updated.get(building.id)?.data,
|
|
2026
|
+
metadata: {
|
|
2027
|
+
...withoutBookkeeping(building).metadata,
|
|
2028
|
+
authorSceneState: state,
|
|
2029
|
+
...(writtenOn.has(building.id) ? { authorSceneWritten: writtenOn.get(building.id) } : {}),
|
|
2030
|
+
},
|
|
2031
|
+
},
|
|
2032
|
+
});
|
|
1062
2033
|
return {
|
|
1063
2034
|
...outcome,
|
|
1064
2035
|
changes: {
|
|
1065
2036
|
...changes,
|
|
2037
|
+
create,
|
|
1066
2038
|
...(outcome.changes?.materials ? { materials: outcome.changes.materials } : {}),
|
|
1067
|
-
update: [
|
|
1068
|
-
...(changes.update ?? []),
|
|
1069
|
-
{
|
|
1070
|
-
id: building.id,
|
|
1071
|
-
data: { metadata: { ...building.metadata, authorSceneState: outcome.candidate } },
|
|
1072
|
-
},
|
|
1073
|
-
],
|
|
2039
|
+
update: [...updated.values()],
|
|
1074
2040
|
},
|
|
1075
2041
|
};
|
|
1076
2042
|
}
|
|
2043
|
+
const equalJson = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|