@pascal-app/core 1.0.5 → 1.0.7
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 +407 -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 +7253 -4053
- 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 +109 -73
- 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 +1164 -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 +5880 -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 +38 -2
- 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 +257 -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 +1667 -866
- 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 +55 -11
- 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 +4 -6
- 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 +340 -195
- 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 +5951 -4351
- 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 +1 -0
- 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 +115 -7
- package/dist/building/wall-openings.d.ts.map +1 -1
- package/dist/building/wall-openings.js +121 -29
- 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 +888 -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 +3326 -2370
- 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 -24
- 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 +56 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +177 -27
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +1631 -831
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +24 -1
- 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 +32 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -15
- package/dist/lib/__fixtures__/mezzanine.d.ts +6840 -4169
- package/dist/lib/__fixtures__/mezzanine.d.ts.map +1 -1
- package/dist/lib/absorb-wall-separators.d.ts +2128 -1328
- 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-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 +205 -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-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/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 +60 -35
- 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/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 +38 -6
- 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/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/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-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 +112 -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 +5 -2
- 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/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 +2 -2
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +1 -1
- 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/types.d.ts +65 -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.d.ts +224 -1
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +57 -10
- 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 +135 -2
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +42 -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 +1079 -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/roof-bearing.d.ts.map +1 -1
- package/dist/services/roof-bearing.js +6 -3
- 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.map +1 -1
- package/dist/services/storey.js +13 -8
- 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 +846 -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 +1057 -0
- package/dist/spatial/usage-field/walls.d.ts.map +1 -0
- package/dist/spatial/usage-field/walls.js +166 -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 +2124 -1322
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +42 -16
- 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 +31 -11
- 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 +6751 -5151
- 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-opening-system.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-opening-system.js +9 -24
- 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 +85 -4
- 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 +3163 -1934
- package/dist/systems/slab/__fixtures__/floor-step.d.ts.map +1 -1
- package/dist/systems/slab/__fixtures__/raised-room.d.ts +2495 -1249
- 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 +889 -89
- 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-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-merge.d.ts.map +1 -1
- package/dist/systems/wall/wall-merge.js +28 -7
- 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 +2 -2
- package/dist/systems/wall/wall-top.d.ts.map +1 -1
- package/dist/systems/wall/wall-top.js +6 -1
- 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 +118 -10
- 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 +6523 -4923
- 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 +1930 -1130
- package/dist/utils/floor-plate-migration.d.ts.map +1 -1
- package/dist/utils/owned-floor-opening-migration.d.ts +888 -88
- package/dist/utils/owned-floor-opening-migration.d.ts.map +1 -1
- package/dist/utils/reconcile-structure-on-load.d.ts +1930 -1130
- 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/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, resolveWallOpeningCeiling } 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,54 @@ 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 = resolveWallOpeningCeiling(node, draft) -
|
|
772
|
+
(node.wallType === 'curtain' ? getCurtainWallConfig(node).perimeterWidth : 0);
|
|
773
|
+
if (o.sill + size.height > wallHeight + 1e-6) {
|
|
774
|
+
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)}.`);
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
// A door or window is flat, set on the arc at its centre: its ends leave the curve.
|
|
779
|
+
if (curve) {
|
|
780
|
+
const chord = dist(w.start, w.end);
|
|
781
|
+
const radius = (chord * chord) / (8 * Math.abs(curve)) + Math.abs(curve) / 2;
|
|
782
|
+
const standoff = radius - Math.sqrt(Math.max(0, radius ** 2 - (size.width / 2) ** 2));
|
|
783
|
+
const half = w.spec.thickness / 2;
|
|
784
|
+
if (standoff > half)
|
|
785
|
+
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.`);
|
|
786
|
+
}
|
|
787
|
+
const centre = Math.min(Math.max(o.along, size.width / 2), length - size.width / 2);
|
|
788
|
+
const x = centre + size.leafOffsetX;
|
|
789
|
+
if (Math.abs(centre - o.along) > 0.01)
|
|
790
|
+
say(`${o.kind} ${id}: moved ${Math.abs(centre - o.along).toFixed(2)} m along the wall to fit inside it`);
|
|
570
791
|
const common = {
|
|
571
792
|
object: 'node',
|
|
572
|
-
id: mint(
|
|
793
|
+
id: mint(kind, id),
|
|
573
794
|
parentId: node.id,
|
|
574
795
|
visible: true,
|
|
575
796
|
metadata: { authorScene: { id, color: o.color ?? null } },
|
|
576
797
|
wallId: node.id,
|
|
798
|
+
casingFaceA: true,
|
|
799
|
+
casingFaceB: true,
|
|
577
800
|
width,
|
|
578
801
|
height,
|
|
579
802
|
rotation: [0, 0, 0],
|
|
580
803
|
...(o.name ? { name: o.name } : {}),
|
|
804
|
+
...(o.shape ? { openingShape: o.shape } : {}),
|
|
805
|
+
...(o.archHeight === undefined ? {} : { archHeight: Math.min(o.archHeight, height) }),
|
|
581
806
|
...(compiled
|
|
582
807
|
? {
|
|
583
808
|
source: scriptSource(compiled, { category: o.kind }),
|
|
@@ -586,7 +811,17 @@ export function authorScene(scene, input, context) {
|
|
|
586
811
|
: {}),
|
|
587
812
|
};
|
|
588
813
|
let opening;
|
|
589
|
-
if (
|
|
814
|
+
if (passage) {
|
|
815
|
+
opening = DoorNode.parse({
|
|
816
|
+
...common,
|
|
817
|
+
type: 'door',
|
|
818
|
+
frameThickness: NEW_DOOR_FRAME_THICKNESS,
|
|
819
|
+
position: [x, o.sill + height / 2, 0],
|
|
820
|
+
openingKind: 'opening',
|
|
821
|
+
});
|
|
822
|
+
report.passages++;
|
|
823
|
+
}
|
|
824
|
+
else if (o.kind === 'door') {
|
|
590
825
|
const type = o.type;
|
|
591
826
|
if (type && !isDoorType(type))
|
|
592
827
|
defect(id, 'type_mismatch', `${type} is a window type; the door is written hinged.`);
|
|
@@ -600,12 +835,19 @@ export function authorScene(scene, input, context) {
|
|
|
600
835
|
opening = DoorNode.parse({
|
|
601
836
|
...common,
|
|
602
837
|
type: 'door',
|
|
603
|
-
|
|
838
|
+
frameThickness: NEW_DOOR_FRAME_THICKNESS,
|
|
839
|
+
position: [x, o.sill + height / 2, 0],
|
|
604
840
|
...(type && isDoorType(type) ? doorTypeFields(type) : {}),
|
|
605
841
|
...(style && isDoorStyle(style) ? doorStyleLook(style) : {}),
|
|
606
842
|
...(o.leaves ? { leafCount: o.leaves } : {}),
|
|
843
|
+
sidelightLeft: o.sidelightLeft,
|
|
844
|
+
sidelightRight: o.sidelightRight,
|
|
845
|
+
transomHeight: o.transomHeight,
|
|
846
|
+
slidingStyle: o.slidingStyle,
|
|
847
|
+
...(o.slideDirection ? { slideDirection: o.slideDirection } : {}),
|
|
607
848
|
hingesSide,
|
|
608
849
|
handleSide,
|
|
850
|
+
handleStyle: o.handleStyle,
|
|
609
851
|
...(o.swing ? { swingDirection: o.swing === 'back' ? 'outward' : 'inward' } : {}),
|
|
610
852
|
});
|
|
611
853
|
report.doors++;
|
|
@@ -626,13 +868,35 @@ export function authorScene(scene, input, context) {
|
|
|
626
868
|
opening = WindowNode.parse({
|
|
627
869
|
...common,
|
|
628
870
|
type: 'window',
|
|
871
|
+
hingesSide: o.hingesSide ?? (o.hinge === 'end' ? 'right' : 'left'),
|
|
872
|
+
sashGrid: !windowTakesStyle(windowType ?? 'fixed') && (o.columns || o.rows) ? true : o.sashGrid,
|
|
873
|
+
windowHandle: o.windowHandle,
|
|
874
|
+
sillInterior: o.sillInterior,
|
|
875
|
+
shutters: o.shutters,
|
|
876
|
+
shutterWidth: o.shutterWidth,
|
|
877
|
+
tiltTurnMode: o.tiltTurnMode,
|
|
878
|
+
sillOverhang: o.sillOverhang,
|
|
879
|
+
sillInteriorDepth: o.sillInteriorDepth,
|
|
880
|
+
sillInteriorThickness: o.sillInteriorThickness,
|
|
881
|
+
sillInteriorOverhang: o.sillInteriorOverhang,
|
|
882
|
+
shutterOpen: o.shutterOpen,
|
|
883
|
+
shutterSides: o.shutterSides,
|
|
884
|
+
rollerShutter: o.rollerShutter,
|
|
885
|
+
rollerShutterBoxHeight: o.rollerShutterBoxHeight,
|
|
886
|
+
rollerShutterOpen: o.rollerShutterOpen,
|
|
887
|
+
frameThickness: NEW_WINDOW_FRAME_THICKNESS,
|
|
629
888
|
position: [x, o.sill + height / 2, 0],
|
|
630
889
|
...(windowType ? windowTypeFields(windowType) : {}),
|
|
890
|
+
...(o.openingDirection === undefined ? {} : { openingDirection: o.openingDirection }),
|
|
631
891
|
...(style && isWindowStyle(style) && takesStyle ? getWindowStyleOverrides(style) : {}),
|
|
892
|
+
...(o.columns
|
|
893
|
+
? { columnRatios: Array.from({ length: o.columns }, () => 1 / o.columns) }
|
|
894
|
+
: {}),
|
|
895
|
+
...(o.rows ? { rowRatios: Array.from({ length: o.rows }, () => 1 / o.rows) } : {}),
|
|
632
896
|
});
|
|
633
897
|
report.windows++;
|
|
634
898
|
}
|
|
635
|
-
const panel =
|
|
899
|
+
const panel = kind === 'door' ? finishRef(o.color, id) : undefined;
|
|
636
900
|
const frame = finishRef(o.frameColor ?? o.color, id);
|
|
637
901
|
if (panel || frame)
|
|
638
902
|
opening = {
|
|
@@ -658,10 +922,14 @@ export function authorScene(scene, input, context) {
|
|
|
658
922
|
const polygon = s.polygon;
|
|
659
923
|
// A slab over a wall's footprint, higher than half the wall: a cap, which would lift the
|
|
660
924
|
// wall onto it. Pascal caps its walls itself.
|
|
661
|
-
const lifted = [...wallNodes.values()].find((w) => s.top > w.
|
|
662
|
-
polygonsOverlap(polygon, wallFootprint({
|
|
925
|
+
const lifted = [...wallNodes.values()].find((w) => s.top > w.height / 2 &&
|
|
926
|
+
polygonsOverlap(polygon, wallFootprint({
|
|
927
|
+
...w,
|
|
928
|
+
thickness: w.spec.thickness,
|
|
929
|
+
curveOffset: w.node.curveOffset,
|
|
930
|
+
})));
|
|
663
931
|
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.
|
|
932
|
+
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
933
|
return;
|
|
666
934
|
}
|
|
667
935
|
if (s.thickness < THIN_SLAB)
|
|
@@ -679,35 +947,527 @@ export function authorScene(scene, input, context) {
|
|
|
679
947
|
elevation: s.top,
|
|
680
948
|
thickness: s.thickness,
|
|
681
949
|
...(finish ? { slots: { surface: finish } } : {}),
|
|
682
|
-
...(s.railing?.length
|
|
683
|
-
? { railing: s.railing.map((r) => ({ start: r.start, end: r.end })) }
|
|
684
|
-
: {}),
|
|
685
950
|
});
|
|
686
951
|
place(node);
|
|
687
952
|
report.ids[id] = node.id;
|
|
688
953
|
report.slabs++;
|
|
954
|
+
(s.railing ?? []).forEach((r, k) => {
|
|
955
|
+
const railId = `${id}/railing/${k}`;
|
|
956
|
+
let fence = FenceNode.parse({
|
|
957
|
+
object: 'node',
|
|
958
|
+
id: mint('fence', railId),
|
|
959
|
+
parentId: levelNode.id,
|
|
960
|
+
visible: true,
|
|
961
|
+
metadata: { authorScene: { id: railId } },
|
|
962
|
+
name: 'Railing',
|
|
963
|
+
start: r.start,
|
|
964
|
+
end: r.end,
|
|
965
|
+
height: r.height,
|
|
966
|
+
style: 'rail',
|
|
967
|
+
baseStyle: 'floating',
|
|
968
|
+
baseHeight: 0,
|
|
969
|
+
postCap: 'none',
|
|
970
|
+
supportSlabId: node.id,
|
|
971
|
+
});
|
|
972
|
+
const railFinish = finishRef(r.color, railId);
|
|
973
|
+
if (railFinish)
|
|
974
|
+
fence = {
|
|
975
|
+
...fence,
|
|
976
|
+
slots: Object.fromEntries((nodeSlotDeclarations(fence) ?? []).map((slot) => [slot.slotId, railFinish])),
|
|
977
|
+
};
|
|
978
|
+
place(fence);
|
|
979
|
+
});
|
|
689
980
|
});
|
|
690
981
|
(level.columns ?? []).forEach((c, i) => {
|
|
691
|
-
const
|
|
982
|
+
const id = `column/${index}/${i}`;
|
|
983
|
+
let compiled;
|
|
984
|
+
if (c.code) {
|
|
985
|
+
compiled = input.compiled[id];
|
|
986
|
+
if (!compiled) {
|
|
987
|
+
defect(id, 'compile_failed', input.compileErrors?.[id] ?? 'The module did not compile.');
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
if (compiled.mount !== 'floor') {
|
|
991
|
+
defect(id, 'wrong_mount', `A column's module uses mount 'floor', not '${compiled.mount}'.`);
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
try {
|
|
995
|
+
requireMeasured(compiled, { label: c.name ?? id, tool: 'author_scene' });
|
|
996
|
+
}
|
|
997
|
+
catch (error) {
|
|
998
|
+
const e = error;
|
|
999
|
+
defect(id, e.code ?? 'unmeasurable_object', e.message ?? String(error));
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (c.color)
|
|
1003
|
+
say(`column ${id}: built by its module, which keeps its own colours; color ignored`);
|
|
1004
|
+
}
|
|
1005
|
+
const size = compiled && scriptedSize(compiled.manifest);
|
|
1006
|
+
let node = ColumnNode.parse({
|
|
692
1007
|
object: 'node',
|
|
693
1008
|
type: 'column',
|
|
694
|
-
id: mint('column',
|
|
1009
|
+
id: mint('column', id),
|
|
695
1010
|
parentId: levelNode.id,
|
|
696
1011
|
visible: true,
|
|
697
|
-
metadata: { authorScene: {
|
|
1012
|
+
metadata: { authorScene: { id } },
|
|
698
1013
|
...(c.name ? { name: c.name } : {}),
|
|
699
1014
|
position: [c.position[0], 0, c.position[1]],
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
1015
|
+
rotation: (c.rotation * Math.PI) / 180,
|
|
1016
|
+
...(compiled && size
|
|
1017
|
+
? {
|
|
1018
|
+
source: scriptSource(compiled, { category: 'column' }),
|
|
1019
|
+
slots: exactSlots(compiled, id),
|
|
1020
|
+
width: size[0],
|
|
1021
|
+
height: size[1],
|
|
1022
|
+
depth: size[2],
|
|
1023
|
+
}
|
|
1024
|
+
: {
|
|
1025
|
+
crossSection: c.shape,
|
|
1026
|
+
...(c.shape === 'round' ? { radius: c.width / 2 } : {}),
|
|
1027
|
+
width: c.width,
|
|
1028
|
+
depth: c.shape === 'round' ? c.width : (c.depth ?? c.width),
|
|
1029
|
+
height: c.height,
|
|
1030
|
+
baseHeight: 0,
|
|
1031
|
+
capitalHeight: 0,
|
|
1032
|
+
}),
|
|
706
1033
|
});
|
|
1034
|
+
const finish = !compiled && finishRef(c.color, id);
|
|
1035
|
+
if (finish)
|
|
1036
|
+
node = {
|
|
1037
|
+
...node,
|
|
1038
|
+
slots: Object.fromEntries((nodeSlotDeclarations(node) ?? []).map((slot) => [slot.slotId, finish])),
|
|
1039
|
+
};
|
|
707
1040
|
place(node);
|
|
708
|
-
report.ids[
|
|
1041
|
+
report.ids[id] = node.id;
|
|
709
1042
|
report.columns++;
|
|
710
1043
|
});
|
|
1044
|
+
levelWalls.push({ walls, wallNodes });
|
|
1045
|
+
});
|
|
1046
|
+
report.levels = levelNodes.length;
|
|
1047
|
+
if (untypedWindows)
|
|
1048
|
+
say(`${untypedWindows} window${untypedWindows === 1 ? ' has' : 's have'} no type: written fixed (cannot open); give type`);
|
|
1049
|
+
// ---------- which face of each wall looks into a room: read from the plan, as the editor does ----------
|
|
1050
|
+
for (const levelNode of levelNodes) {
|
|
1051
|
+
const boundaries = Object.values(draft).filter((n) => n.parentId === levelNode.id && (n.type === 'wall' || n.type === 'separator'));
|
|
1052
|
+
for (const { wallId, ...sides } of detectSpacesForLevel(levelNode.id, boundaries).wallUpdates)
|
|
1053
|
+
draft[wallId] = { ...draft[wallId], ...sides };
|
|
1054
|
+
}
|
|
1055
|
+
// ---------- rooms: Pascal's own derivation, then the payload's names and floors ----------
|
|
1056
|
+
const { nodes: withZones, createdZoneIds } = migrateRoomZones(draft);
|
|
1057
|
+
const zones = createdZoneIds.map((id) => ZoneNode.parse(withZones[id]));
|
|
1058
|
+
for (const zone of zones) {
|
|
1059
|
+
draft[zone.id] = zone;
|
|
1060
|
+
const level = draft[zone.parentId];
|
|
1061
|
+
draft[zone.parentId] = { ...level, children: [...level.children, zone.id] };
|
|
1062
|
+
report.rooms++;
|
|
1063
|
+
}
|
|
1064
|
+
const joined = new Map();
|
|
1065
|
+
const roomIds = new Map();
|
|
1066
|
+
payload.levels.forEach((level, index) => {
|
|
1067
|
+
const levelNode = levelNodes[index];
|
|
1068
|
+
for (const [i, room] of level.rooms.entries()) {
|
|
1069
|
+
const id = `room/${index}/${i}`;
|
|
1070
|
+
roomIds.set(room, id);
|
|
1071
|
+
const zone = zones.find((z) => z.parentId === levelNode.id && pointInPolygon(room.seed, z.polygon));
|
|
1072
|
+
// Several seeds in one enclosure name one open-plan room, in the order given.
|
|
1073
|
+
const sharing = zone && joined.get(zone.id);
|
|
1074
|
+
if (sharing) {
|
|
1075
|
+
sharing.push(room);
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
if (!zone) {
|
|
1079
|
+
defect(id, 'room_not_enclosed', `No closed room around (${room.seed[0]}, ${room.seed[1]}) "${room.name}": its walls do not meet.`);
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
if (room.floorRegions.length > MANY_REGIONS)
|
|
1083
|
+
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.`);
|
|
1084
|
+
const finish = finishRef(room.floor, id);
|
|
1085
|
+
const regions = room.floorRegions.map((r, k) => ({
|
|
1086
|
+
id: `region_${k}`,
|
|
1087
|
+
polygon: r.polygon,
|
|
1088
|
+
finish: finishRef(r.finish, id) ?? r.finish,
|
|
1089
|
+
}));
|
|
1090
|
+
draft[zone.id] = ZoneNode.parse({
|
|
1091
|
+
...zone,
|
|
1092
|
+
name: room.name,
|
|
1093
|
+
spaceRole: 'room',
|
|
1094
|
+
...(finish || regions.length
|
|
1095
|
+
? {
|
|
1096
|
+
floor: {
|
|
1097
|
+
...zone.floor,
|
|
1098
|
+
...(finish ? { finish } : {}),
|
|
1099
|
+
...(regions.length ? { regions } : {}),
|
|
1100
|
+
},
|
|
1101
|
+
}
|
|
1102
|
+
: {}),
|
|
1103
|
+
metadata: { ...zone.metadata, authorScene: { seed: room.seed } },
|
|
1104
|
+
});
|
|
1105
|
+
joined.set(zone.id, [room]);
|
|
1106
|
+
report.ids[id] = zone.id;
|
|
1107
|
+
report.roomsNamed++;
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
for (const [zoneId, rooms] of joined) {
|
|
1111
|
+
if (rooms.length < 2)
|
|
1112
|
+
continue;
|
|
1113
|
+
const [first, ...rest] = rooms.map((room) => room.name);
|
|
1114
|
+
// "Kitchen" joins as "kitchen"; a name that is not a capitalised word ("WC") stays as given.
|
|
1115
|
+
const names = [
|
|
1116
|
+
first,
|
|
1117
|
+
...rest.map((name) => name.replace(/^([A-Z])(?=[a-z])/, (c) => c.toLowerCase())),
|
|
1118
|
+
];
|
|
1119
|
+
const name = `${names.slice(0, -1).join(', ')} and ${names.at(-1)}`;
|
|
1120
|
+
draft[zoneId] = { ...draft[zoneId], name };
|
|
1121
|
+
report.roomsNamed += rest.length;
|
|
1122
|
+
const floors = rooms
|
|
1123
|
+
.slice(1)
|
|
1124
|
+
.filter((room) => room.floor || room.floorCode || room.floorRegions.length);
|
|
1125
|
+
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}"` : ''}`);
|
|
1126
|
+
}
|
|
1127
|
+
// ---------- units: one per distinct name, under the building, holding the rooms that name it ----------
|
|
1128
|
+
const units = new Map();
|
|
1129
|
+
for (const unit of payload.units) {
|
|
1130
|
+
const color = unit.color && HEX.test(unit.color) ? unit.color : undefined;
|
|
1131
|
+
if (unit.color && !color)
|
|
1132
|
+
defect(`unit/${unit.name}`, 'invalid_color', `"${unit.color}" is not a #rrggbb colour.`);
|
|
1133
|
+
units.set(unit.name, { kind: unit.kind, color, zones: [] });
|
|
1134
|
+
}
|
|
1135
|
+
for (const [zoneId, rooms] of joined) {
|
|
1136
|
+
const names = [...new Set(rooms.flatMap((room) => (room.unit ? [room.unit] : [])))];
|
|
1137
|
+
// One enclosure is one room, so it is in one unit: seeds naming two want a wall between them.
|
|
1138
|
+
if (names.length > 1) {
|
|
1139
|
+
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.`);
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
if (!names[0])
|
|
1143
|
+
continue;
|
|
1144
|
+
const entry = units.get(names[0]) ?? { kind: 'apartment', zones: [] };
|
|
1145
|
+
entry.zones.push(zoneId);
|
|
1146
|
+
units.set(names[0], entry);
|
|
1147
|
+
}
|
|
1148
|
+
for (const [name, { kind, color, zones: members }] of units) {
|
|
1149
|
+
const unit = UnitNode.parse({
|
|
1150
|
+
id: mint('unit', `unit/${name}`),
|
|
1151
|
+
parentId: building.id,
|
|
1152
|
+
name,
|
|
1153
|
+
kind,
|
|
1154
|
+
...(color ? { color } : {}),
|
|
1155
|
+
members,
|
|
1156
|
+
});
|
|
1157
|
+
draft[unit.id] = unit;
|
|
1158
|
+
report.ids[`unit/${name}`] = unit.id;
|
|
1159
|
+
report.units++;
|
|
1160
|
+
if (!members.length)
|
|
1161
|
+
warn(`unit/${name}`, 'unit_no_rooms', `No room names unit "${name}".`);
|
|
1162
|
+
}
|
|
1163
|
+
// ---------- floors and ceilings, as the editor derives them on load ----------
|
|
1164
|
+
const levelIds = levelNodes.map((l) => l.id);
|
|
1165
|
+
let written = {
|
|
1166
|
+
...reconcileSceneStructure({ nodes: draft, levelIds, mintId: (kind) => mint(kind) }).nodes,
|
|
1167
|
+
};
|
|
1168
|
+
for (const opening of Object.values(written)) {
|
|
1169
|
+
const wall = written[opening.parentId ?? ''];
|
|
1170
|
+
if (opening.type !== 'door' || wall?.type !== 'wall' || scaffoldIds.has(opening.id))
|
|
1171
|
+
continue;
|
|
1172
|
+
const facing = doorFacing(wall);
|
|
1173
|
+
written[opening.id] = {
|
|
1174
|
+
...opening,
|
|
1175
|
+
...facing,
|
|
1176
|
+
position: doorPositionForFacing(opening, facing.rotation),
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
// A room's plate and ceiling are named by its room, whose id follows its walls: a revision keeps
|
|
1180
|
+
// an unchanged room's, and a room it adds brings its own with it (never another room's renamed).
|
|
1181
|
+
const derivedIds = new Map();
|
|
1182
|
+
for (const node of Object.values(written)) {
|
|
1183
|
+
const zones = node.type === 'ceiling' && node.zoneId
|
|
1184
|
+
? [node.zoneId]
|
|
1185
|
+
: node.type === 'slab' && node.plateRole === 'base'
|
|
1186
|
+
? [...(node.zoneIds ?? [])].sort()
|
|
1187
|
+
: [];
|
|
1188
|
+
if (zones.length && !scaffoldIds.has(node.id))
|
|
1189
|
+
derivedIds.set(node.id, mint(node.type, `${node.type}/${zones.join('+')}`));
|
|
1190
|
+
}
|
|
1191
|
+
if (derivedIds.size) {
|
|
1192
|
+
let json = JSON.stringify(written);
|
|
1193
|
+
for (const [from, to] of derivedIds)
|
|
1194
|
+
json = json.replaceAll(`"${from}"`, `"${to}"`);
|
|
1195
|
+
written = JSON.parse(json);
|
|
1196
|
+
}
|
|
1197
|
+
// ---------- the floor between storeys: each upper plate keeps its top and reaches down to the walls ----------
|
|
1198
|
+
for (const [index, { below, wallHeight, storey }] of floorsAbove) {
|
|
1199
|
+
const plates = (levelId) => Object.values(written).filter((node) => node.type === 'slab' && node.parentId === levelId && node.plateRole === 'base');
|
|
1200
|
+
// The carrying walls stand on the floor below; their tops, in this level's frame, are its underside.
|
|
1201
|
+
const lower = plates(levelNodes[index - 1].id);
|
|
1202
|
+
const underside = (lower.length ? Math.max(...lower.map((p) => p.elevation)) : 0) + wallHeight;
|
|
1203
|
+
// Seen from outside, the band continues the cladding under it: the colour most of those walls
|
|
1204
|
+
// wear outside paints the plate's edge and sides.
|
|
1205
|
+
const outsides = (payload.levels[index - 1].walls ?? []).flatMap((w) => w.colorOutside ? [w.colorOutside] : []);
|
|
1206
|
+
const cladding = mostCommon(outsides);
|
|
1207
|
+
const bandRef = cladding && finishRef(cladding, `level/${index}/floor`);
|
|
1208
|
+
const depths = new Set();
|
|
1209
|
+
for (const plate of plates(levelNodes[index].id)) {
|
|
1210
|
+
const thickness = roundFloorElevation(plate.elevation - (underside - storey));
|
|
1211
|
+
written[plate.id] = {
|
|
1212
|
+
...plate,
|
|
1213
|
+
thickness,
|
|
1214
|
+
...(bandRef ? { slots: { ...plate.slots, edge: bandRef, side: bandRef } } : {}),
|
|
1215
|
+
};
|
|
1216
|
+
depths.add(thickness);
|
|
1217
|
+
}
|
|
1218
|
+
if (depths.size)
|
|
1219
|
+
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`);
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* The room a plan point of a level is in, its ceiling, and the height in that level's frame that
|
|
1223
|
+
* ceiling hangs things from. Under an opening in the ceiling (a void, a stairwell) it is the
|
|
1224
|
+
* ceiling above, the room's double height.
|
|
1225
|
+
*/
|
|
1226
|
+
const ceilingAt = (index, x, z) => {
|
|
1227
|
+
const levelId = levelNodes[index].id;
|
|
1228
|
+
const room = Object.values(written).find((node) => node.type === 'zone' &&
|
|
1229
|
+
node.spaceRole === 'room' &&
|
|
1230
|
+
node.parentId === levelId &&
|
|
1231
|
+
pointInPolygon([x, z], node.polygon) &&
|
|
1232
|
+
!node.holes.some((hole) => pointInPolygon([x, z], hole)));
|
|
1233
|
+
const ceiling = room &&
|
|
1234
|
+
Object.values(written).find((node) => node.type === 'ceiling' && node.zoneId === room.id && node.parentId === levelId);
|
|
1235
|
+
if (!room || !ceiling)
|
|
1236
|
+
return undefined;
|
|
1237
|
+
if (levelNodes[index + 1] && ceiling.holes.some((hole) => pointInPolygon([x, z], hole))) {
|
|
1238
|
+
const above = ceilingAt(index + 1, x, z);
|
|
1239
|
+
const climb = payload.levels[index + 1].elevation - payload.levels[index].elevation;
|
|
1240
|
+
if (above)
|
|
1241
|
+
return { ...above, height: above.height + climb };
|
|
1242
|
+
}
|
|
1243
|
+
return { room, ceiling, height: resolveCeilingHeight(ceiling, written) - CEILING_DRAW_OFFSET };
|
|
1244
|
+
};
|
|
1245
|
+
// ---------- stairs, as create_stair builds them: up to the next level, opening its floor ----------
|
|
1246
|
+
let stairsWritten = false;
|
|
1247
|
+
payload.levels.forEach((level, index) => {
|
|
1248
|
+
const levelNode = levelNodes[index];
|
|
1249
|
+
const upper = levelNodes[index + 1];
|
|
1250
|
+
(level.stairs ?? []).forEach((stair, i) => {
|
|
1251
|
+
const id = `stair/${index}/${i}`;
|
|
1252
|
+
if (!upper) {
|
|
1253
|
+
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.`);
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
try {
|
|
1257
|
+
const outcome = createStair(written, {
|
|
1258
|
+
x: stair.start[0],
|
|
1259
|
+
z: stair.start[1],
|
|
1260
|
+
rotation: stair.rotation,
|
|
1261
|
+
layout: stair.layout,
|
|
1262
|
+
width: stair.width,
|
|
1263
|
+
...(stair.turn ? { turn: stair.turn } : {}),
|
|
1264
|
+
...(stair.length === undefined ? {} : { length: stair.length }),
|
|
1265
|
+
...(stair.steps === undefined ? {} : { steps: stair.steps }),
|
|
1266
|
+
...(stair.lowerSteps === undefined ? {} : { lowerSteps: stair.lowerSteps }),
|
|
1267
|
+
...(stair.railing ? { railingMode: stair.railing } : {}),
|
|
1268
|
+
...(stair.railingStyle ? { railingStyle: stair.railingStyle } : {}),
|
|
1269
|
+
...(stair.name ? { name: stair.name } : {}),
|
|
1270
|
+
levelId: levelNode.id,
|
|
1271
|
+
toLevelId: upper.id,
|
|
1272
|
+
}, { ...context, activeLevelId: levelNode.id });
|
|
1273
|
+
const result = outcome.result;
|
|
1274
|
+
// create_stair mints fresh ids; the conversion's own keep the stair across revisions.
|
|
1275
|
+
let json = JSON.stringify(outcome.changes);
|
|
1276
|
+
(outcome.changes?.create ?? []).forEach(({ node }, k) => {
|
|
1277
|
+
const kind = node.id.slice(0, node.id.indexOf('_'));
|
|
1278
|
+
json = json.replaceAll(node.id, mint(kind, k ? `${id}/${k}` : id));
|
|
1279
|
+
});
|
|
1280
|
+
const changes = JSON.parse(json);
|
|
1281
|
+
const [made, ...rest] = changes.create;
|
|
1282
|
+
changes.create = [
|
|
1283
|
+
{
|
|
1284
|
+
...made,
|
|
1285
|
+
node: {
|
|
1286
|
+
...made.node,
|
|
1287
|
+
metadata: { ...made.node.metadata, authorScene: { id } },
|
|
1288
|
+
},
|
|
1289
|
+
},
|
|
1290
|
+
...rest,
|
|
1291
|
+
];
|
|
1292
|
+
written = applySceneChanges(written, changes);
|
|
1293
|
+
const finishes = [
|
|
1294
|
+
['body', stair.color],
|
|
1295
|
+
['treads', stair.treads ?? stair.color],
|
|
1296
|
+
['railing', stair.railingColor],
|
|
1297
|
+
];
|
|
1298
|
+
for (const [role, color] of finishes) {
|
|
1299
|
+
const finish = finishRef(color, id);
|
|
1300
|
+
if (!finish)
|
|
1301
|
+
continue;
|
|
1302
|
+
if (!nodeSlotDeclarations(written[made.node.id])?.some((slot) => slot.slotId === role)) {
|
|
1303
|
+
say(`${id}: it has no ${role}, its ${role === 'railing' ? 'railingColor' : 'color'} ignored`);
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
try {
|
|
1307
|
+
const { changes } = paint(written, { targets: [made.node.id], material: finish, role }, paintContext(levelNode.id));
|
|
1308
|
+
written = applySceneChanges(written, changes);
|
|
1309
|
+
}
|
|
1310
|
+
catch (error) {
|
|
1311
|
+
defect(id, 'paint_refused', error.message);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
report.ids[id] = made.node.id;
|
|
1315
|
+
report.stairs++;
|
|
1316
|
+
stairsWritten = true;
|
|
1317
|
+
if (result.slabHoleCut)
|
|
1318
|
+
say(`${id}: ${result.message}`);
|
|
1319
|
+
else
|
|
1320
|
+
warn(id, 'no_floor_opening', result.message);
|
|
1321
|
+
}
|
|
1322
|
+
catch (error) {
|
|
1323
|
+
const e = error;
|
|
1324
|
+
defect(id, e.code ?? 'stair_refused', e.message ?? String(error));
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
});
|
|
1328
|
+
/** The plan footprints of a level's walls as written, each grown by `grow` on both faces. */
|
|
1329
|
+
const wallFootprints = (levelId, grow = 0) => {
|
|
1330
|
+
const walls = Object.values(written).flatMap((node) => node.type === 'wall' && node.parentId === levelId
|
|
1331
|
+
? [grow ? { ...node, thickness: getWallThickness(node) + 2 * grow } : node]
|
|
1332
|
+
: []);
|
|
1333
|
+
const miters = calculateLevelMiters(walls);
|
|
1334
|
+
return walls.map((wall) => getWallPlanFootprint(wall, miters).map(({ x, y }) => [x, y]));
|
|
1335
|
+
};
|
|
1336
|
+
// ---------- voids: openings in a level's floor, the room below rising through them ----------
|
|
1337
|
+
const voidCuts = new Map();
|
|
1338
|
+
const voidsCut = [];
|
|
1339
|
+
payload.levels.forEach((level, index) => {
|
|
1340
|
+
const levelNode = levelNodes[index];
|
|
1341
|
+
(level.voids ?? []).forEach((v, i) => {
|
|
1342
|
+
const id = `void/${index}/${i}`;
|
|
1343
|
+
const label = v.name ? `"${v.name}"` : `void ${i}`;
|
|
1344
|
+
if (index === 0) {
|
|
1345
|
+
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.`);
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
// The cut stops at the faces of the walls standing on this floor, as the floor carries them,
|
|
1349
|
+
// and at the floor's own edge (a void drawn past a curved outside wall).
|
|
1350
|
+
const floor = union(Object.values(written).flatMap((node) => node.type === 'slab' && node.parentId === levelNode.id && node.plateRole === 'base'
|
|
1351
|
+
? [node.polygon]
|
|
1352
|
+
: []));
|
|
1353
|
+
const cut = intersection(difference(v.polygon, union(wallFootprints(levelNode.id))), floor).filter((part) => area([part]) > 1e-4);
|
|
1354
|
+
const opened = area(cut);
|
|
1355
|
+
if (opened < 1e-4) {
|
|
1356
|
+
defect(id, 'no_floor', `No floor of "${level.name}" lies under ${label} clear of its walls: there is nothing to open.`);
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
if (cut.some((part) => part.holes.length))
|
|
1360
|
+
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.`);
|
|
1361
|
+
let cutsCeiling = false;
|
|
1362
|
+
cut.forEach((part, k) => {
|
|
1363
|
+
const plan = cutFloorOpening(written, {
|
|
1364
|
+
levelId: levelNode.id,
|
|
1365
|
+
polygon: part.outer,
|
|
1366
|
+
drawnOn: 'floor',
|
|
1367
|
+
mintId: () => mint('floor-opening', k ? `${id}/${k}` : id),
|
|
1368
|
+
});
|
|
1369
|
+
for (const change of plan.changes) {
|
|
1370
|
+
if (change.op === 'create') {
|
|
1371
|
+
const opening = change.node;
|
|
1372
|
+
cutsCeiling ||= opening.cutsAdjacent;
|
|
1373
|
+
written[opening.id] = {
|
|
1374
|
+
...opening,
|
|
1375
|
+
name: v.name ?? 'Void',
|
|
1376
|
+
metadata: { ...opening.metadata, authorScene: { id } },
|
|
1377
|
+
};
|
|
1378
|
+
if (!k)
|
|
1379
|
+
report.ids[id] = opening.id;
|
|
1380
|
+
}
|
|
1381
|
+
else if (change.op === 'update')
|
|
1382
|
+
written[change.id] = { ...written[change.id], ...change.data };
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
report.voids++;
|
|
1386
|
+
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`}`);
|
|
1387
|
+
voidCuts.set(index, union([...(voidCuts.get(index) ?? []), ...cut]));
|
|
1388
|
+
voidsCut.push({
|
|
1389
|
+
id,
|
|
1390
|
+
index,
|
|
1391
|
+
cut,
|
|
1392
|
+
railing: v.railing === null ? null : (v.railing ?? { height: 1.1 }),
|
|
1393
|
+
});
|
|
1394
|
+
});
|
|
1395
|
+
});
|
|
1396
|
+
// The openings the stairs and voids own cut the floor plates and ceilings they pass, as on load.
|
|
1397
|
+
if (stairsWritten || report.voids)
|
|
1398
|
+
written = {
|
|
1399
|
+
...reconcileSceneStructure({ nodes: written, levelIds, mintId: (kind) => mint(kind) }).nodes,
|
|
1400
|
+
};
|
|
1401
|
+
// ---------- each void's railing: along its open edges, on the floor, as the slab railing looks ----------
|
|
1402
|
+
for (const { id, index, cut, railing } of voidsCut) {
|
|
1403
|
+
const levelNode = levelNodes[index];
|
|
1404
|
+
// Inside the double height the floor's edge round the void continues the walls it rises
|
|
1405
|
+
// between: it wears their inside paint, where the facade's edge keeps the cladding.
|
|
1406
|
+
const insides = (payload.levels[index - 1].walls ?? []).flatMap((w) => w.colorInside ? [w.colorInside] : []);
|
|
1407
|
+
const edge = finishRef(mostCommon(insides), id);
|
|
1408
|
+
if (edge)
|
|
1409
|
+
for (const zone of Object.values(written))
|
|
1410
|
+
if (zone.type === 'zone' &&
|
|
1411
|
+
zone.parentId === levelNode.id &&
|
|
1412
|
+
zone.spaceRole === 'room' &&
|
|
1413
|
+
area(intersection(cut, zone.polygon)) > 1e-4)
|
|
1414
|
+
written[zone.id] = { ...zone, floorEdgeFinish: edge };
|
|
1415
|
+
if (!railing)
|
|
1416
|
+
continue;
|
|
1417
|
+
// A run stops at a wall standing on the edge and where a stair arrives.
|
|
1418
|
+
const arrivals = Object.values(written).flatMap((node) => node.type === 'stair' && node.toLevelId === levelNode.id
|
|
1419
|
+
? [stairArrivalOpening(node, written)]
|
|
1420
|
+
: []);
|
|
1421
|
+
const cover = union([
|
|
1422
|
+
...wallFootprints(levelNode.id, 0.005),
|
|
1423
|
+
...arrivals.filter((ring) => ring.length >= 3),
|
|
1424
|
+
]);
|
|
1425
|
+
const runs = exposedIntervals(cut, [], cover).filter(({ start, end }) => dist(start, end) >= 0.1);
|
|
1426
|
+
const plate = Object.values(written)
|
|
1427
|
+
.filter((node) => node.type === 'slab' && node.parentId === levelNode.id && node.plateRole === 'base')
|
|
1428
|
+
.map((node) => ({ node, shared: area(intersection(cut, node.polygon)) }))
|
|
1429
|
+
.sort((a, b) => b.shared - a.shared)[0]?.node;
|
|
1430
|
+
const fences = runs.map((run, k) => FenceNode.parse({
|
|
1431
|
+
object: 'node',
|
|
1432
|
+
id: mint('fence', `${id}/railing/${k}`),
|
|
1433
|
+
parentId: levelNode.id,
|
|
1434
|
+
visible: true,
|
|
1435
|
+
metadata: { authorScene: { id } },
|
|
1436
|
+
name: 'Railing',
|
|
1437
|
+
start: run.start,
|
|
1438
|
+
end: run.end,
|
|
1439
|
+
height: railing.height,
|
|
1440
|
+
style: 'rail',
|
|
1441
|
+
baseStyle: 'floating',
|
|
1442
|
+
baseHeight: 0,
|
|
1443
|
+
postCap: 'none',
|
|
1444
|
+
...(plate ? { supportSlabId: plate.id } : {}),
|
|
1445
|
+
}));
|
|
1446
|
+
if (!fences.length)
|
|
1447
|
+
continue;
|
|
1448
|
+
const level = written[levelNode.id];
|
|
1449
|
+
written[level.id] = {
|
|
1450
|
+
...level,
|
|
1451
|
+
children: [...level.children, ...fences.map((f) => f.id)],
|
|
1452
|
+
};
|
|
1453
|
+
for (const fence of fences)
|
|
1454
|
+
written[fence.id] = fence;
|
|
1455
|
+
const finish = finishRef(railing.color, id);
|
|
1456
|
+
if (finish)
|
|
1457
|
+
try {
|
|
1458
|
+
const { changes } = paint(written, { targets: fences.map((fence) => fence.id), material: finish }, paintContext(levelNode.id));
|
|
1459
|
+
for (const u of changes?.update ?? [])
|
|
1460
|
+
written[u.id] = { ...written[u.id], ...u.data };
|
|
1461
|
+
}
|
|
1462
|
+
catch (error) {
|
|
1463
|
+
defect(id, 'paint_refused', error.message);
|
|
1464
|
+
}
|
|
1465
|
+
say(`${id}: a ${metres(railing.height)} railing along ${metres(runs.reduce((sum, run) => sum + dist(run.start, run.end), 0))} of its edges`);
|
|
1466
|
+
}
|
|
1467
|
+
// ---------- items, on their floors and ceilings, then each wall's paint ----------
|
|
1468
|
+
payload.levels.forEach((level, index) => {
|
|
1469
|
+
const levelNode = levelNodes[index];
|
|
1470
|
+
const { walls, wallNodes } = levelWalls[index];
|
|
711
1471
|
// ---------- items, hosts first ----------
|
|
712
1472
|
const pending = [...(level.items ?? [])];
|
|
713
1473
|
let progress = true;
|
|
@@ -725,14 +1485,20 @@ export function authorScene(scene, input, context) {
|
|
|
725
1485
|
defect(item.id, 'compile_failed', input.compileErrors?.[item.id] ?? 'The module did not compile.');
|
|
726
1486
|
continue;
|
|
727
1487
|
}
|
|
1488
|
+
if (/^(stairs?|staircases?)$/i.test(item.category))
|
|
1489
|
+
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
1490
|
const world = [item.position[0], item.position[1], item.position[2]];
|
|
729
1491
|
const yaw = (item.rotation * Math.PI) / 180;
|
|
730
1492
|
let placed;
|
|
1493
|
+
if (hostWall && isCurvedWall(hostWall.node)) {
|
|
1494
|
+
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.`);
|
|
1495
|
+
continue;
|
|
1496
|
+
}
|
|
731
1497
|
if (hostWall) {
|
|
732
1498
|
// On the wall as place_items puts a wall-side item there: along it from its start, the
|
|
733
1499
|
// payload's height for its bottom, on the face nearest the point, facing the room.
|
|
734
1500
|
const [width, height] = scriptedSize(compiled.manifest);
|
|
735
|
-
const wallHeight = hostWall.
|
|
1501
|
+
const wallHeight = hostWall.height;
|
|
736
1502
|
if (height > wallHeight) {
|
|
737
1503
|
defect(item.id, 'item_too_tall', `"${item.name}" is ${metres(height)} tall; wall ${item.host} is ${metres(wallHeight)}.`);
|
|
738
1504
|
continue;
|
|
@@ -773,8 +1539,27 @@ export function authorScene(scene, input, context) {
|
|
|
773
1539
|
},
|
|
774
1540
|
};
|
|
775
1541
|
}
|
|
1542
|
+
else if (compiled.mount === 'ceiling' && !item.host) {
|
|
1543
|
+
// From the ceiling of the room its plan point is in, its top on that ceiling.
|
|
1544
|
+
const over = ceilingAt(index, world[0], world[2]);
|
|
1545
|
+
if (!over) {
|
|
1546
|
+
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'.`);
|
|
1547
|
+
continue;
|
|
1548
|
+
}
|
|
1549
|
+
say(`${item.id}: hung from the ceiling of ${over.room.name}, ${metres(over.height)} up`);
|
|
1550
|
+
placed = {
|
|
1551
|
+
compiled,
|
|
1552
|
+
parentId: over.ceiling.id,
|
|
1553
|
+
position: [world[0], 0, world[2]],
|
|
1554
|
+
rotation: item.rotation,
|
|
1555
|
+
frame: { position: [world[0], over.height, world[2]], yaw },
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
776
1558
|
else {
|
|
777
1559
|
const host = hostItemId ? itemFrames.get(item.host) : undefined;
|
|
1560
|
+
const opening = voidCuts.get(index);
|
|
1561
|
+
if (!host && opening && containsPoint(opening, [world[0], world[2]]))
|
|
1562
|
+
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
1563
|
placed = {
|
|
779
1564
|
compiled,
|
|
780
1565
|
parentId: hostItemId ?? levelNode.id,
|
|
@@ -784,7 +1569,7 @@ export function authorScene(scene, input, context) {
|
|
|
784
1569
|
};
|
|
785
1570
|
}
|
|
786
1571
|
try {
|
|
787
|
-
const outcome = addObject(
|
|
1572
|
+
const outcome = addObject(written, {
|
|
788
1573
|
compiled: { ...placed.compiled, nodeId: mint('item', item.id) },
|
|
789
1574
|
name: item.name,
|
|
790
1575
|
category: item.category,
|
|
@@ -795,13 +1580,16 @@ export function authorScene(scene, input, context) {
|
|
|
795
1580
|
...(placed.side ? { side: placed.side } : {}),
|
|
796
1581
|
}, { ...context, activeLevelId: levelNode.id });
|
|
797
1582
|
for (const { node, parentId } of outcome.changes?.create ?? []) {
|
|
798
|
-
|
|
1583
|
+
written[node.id] = {
|
|
799
1584
|
...node,
|
|
800
1585
|
slots: exactSlots(compiled, item.id),
|
|
801
1586
|
...(placed.wallT === undefined ? {} : { wallT: placed.wallT }),
|
|
802
1587
|
};
|
|
803
|
-
const parent =
|
|
804
|
-
parent.
|
|
1588
|
+
const parent = written[parentId ?? levelNode.id];
|
|
1589
|
+
written[parent.id] = {
|
|
1590
|
+
...parent,
|
|
1591
|
+
children: [...(parent.children ?? []), node.id],
|
|
1592
|
+
};
|
|
805
1593
|
itemNodeIds.set(item.id, node.id);
|
|
806
1594
|
itemFrames.set(item.id, placed.frame);
|
|
807
1595
|
report.ids[item.id] = node.id;
|
|
@@ -817,15 +1605,46 @@ export function authorScene(scene, input, context) {
|
|
|
817
1605
|
for (const item of pending)
|
|
818
1606
|
defect(item.id, 'host_missing', `Its host ${item.host} was not placed.`);
|
|
819
1607
|
// ---------- wall paint, through the editor's paint: both faces, or each its own ----------
|
|
1608
|
+
const inside = {
|
|
1609
|
+
left: [],
|
|
1610
|
+
right: [],
|
|
1611
|
+
both: [],
|
|
1612
|
+
neither: [],
|
|
1613
|
+
};
|
|
820
1614
|
for (const w of walls) {
|
|
821
1615
|
const node = wallNodes.get(w.spec.id)?.node;
|
|
822
1616
|
if (!node)
|
|
823
1617
|
continue;
|
|
824
|
-
const faces = {
|
|
825
|
-
a: w.spec.colorLeft ?? w.spec.color,
|
|
826
|
-
b: w.spec.colorRight ?? w.spec.color,
|
|
827
|
-
};
|
|
828
1618
|
const coats = [];
|
|
1619
|
+
if (w.spec.curtain) {
|
|
1620
|
+
// Glass and mullions: no face to paint; its frame takes the colour.
|
|
1621
|
+
const ignored = ['color', 'colorInside', 'colorOutside', 'colorLeft', 'colorRight'].filter((field) => w.spec[field]);
|
|
1622
|
+
if (ignored.length)
|
|
1623
|
+
say(`wall ${w.spec.id}: a curtain wall has no faces to paint, its ${ignored.join(', ')} ignored; curtain.frameColor colours its frame`);
|
|
1624
|
+
const frame = finishRef(w.spec.curtain.frameColor, w.spec.id);
|
|
1625
|
+
if (frame)
|
|
1626
|
+
coats.push({ role: 'curtain-frame', material: frame });
|
|
1627
|
+
}
|
|
1628
|
+
const { colorInside, colorOutside } = w.spec.curtain ? {} : w.spec;
|
|
1629
|
+
// A face is inside when a room of this level stands on it, known once the rooms are derived.
|
|
1630
|
+
const cover = colorInside || colorOutside ? roomFaceCover(written, node.id) : undefined;
|
|
1631
|
+
const meant = (face) => cover && (cover[face] === 'none' ? colorOutside : colorInside);
|
|
1632
|
+
if (cover) {
|
|
1633
|
+
const faces = ['a', 'b'].filter((face) => cover[face] !== 'none');
|
|
1634
|
+
inside[faces.length === 2
|
|
1635
|
+
? 'both'
|
|
1636
|
+
: faces.length === 0
|
|
1637
|
+
? 'neither'
|
|
1638
|
+
: faces[0] === 'a'
|
|
1639
|
+
? 'left'
|
|
1640
|
+
: 'right'].push(w.spec.id);
|
|
1641
|
+
}
|
|
1642
|
+
const faces = w.spec.curtain
|
|
1643
|
+
? {}
|
|
1644
|
+
: {
|
|
1645
|
+
a: w.spec.colorLeft ?? meant('a') ?? w.spec.color,
|
|
1646
|
+
b: w.spec.colorRight ?? meant('b') ?? w.spec.color,
|
|
1647
|
+
};
|
|
829
1648
|
if (faces.a && faces.a === faces.b) {
|
|
830
1649
|
const ref = finishRef(faces.a, w.spec.id);
|
|
831
1650
|
if (ref)
|
|
@@ -839,84 +1658,24 @@ export function authorScene(scene, input, context) {
|
|
|
839
1658
|
}
|
|
840
1659
|
for (const coat of coats) {
|
|
841
1660
|
try {
|
|
842
|
-
const { changes } = paint(
|
|
1661
|
+
const { changes } = paint(written, { targets: [node.id], ...coat }, paintContext(levelNode.id));
|
|
843
1662
|
for (const u of changes?.update ?? [])
|
|
844
|
-
|
|
1663
|
+
written[u.id] = { ...written[u.id], ...u.data };
|
|
845
1664
|
}
|
|
846
1665
|
catch (error) {
|
|
847
1666
|
defect(w.spec.id, 'paint_refused', error.message);
|
|
848
1667
|
}
|
|
849
1668
|
}
|
|
850
1669
|
}
|
|
1670
|
+
const resolved = [
|
|
1671
|
+
inside.left.length && `the left face of ${inside.left.join(', ')}`,
|
|
1672
|
+
inside.right.length && `the right face of ${inside.right.join(', ')}`,
|
|
1673
|
+
inside.both.length && `both faces of ${inside.both.join(', ')}`,
|
|
1674
|
+
inside.neither.length && `neither face of ${inside.neither.join(', ')}`,
|
|
1675
|
+
].filter(Boolean);
|
|
1676
|
+
if (resolved.length)
|
|
1677
|
+
say(`colorInside / colorOutside on "${level.name}", left and right as seen from each wall's start to its end: inside is ${resolved.join('; ')}`);
|
|
851
1678
|
});
|
|
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
1679
|
// ---------- skirting, native on each wall, along the faces its rooms stand on ----------
|
|
921
1680
|
for (const { id, nodeId, levelId, skirting } of skirtings) {
|
|
922
1681
|
const finish = finishRef(skirting.color, id);
|
|
@@ -944,22 +1703,47 @@ export function authorScene(scene, input, context) {
|
|
|
944
1703
|
// Ceilings exist only after structure reconciliation; lights can now use their room's host.
|
|
945
1704
|
payload.levels.forEach((level, index) => {
|
|
946
1705
|
const levelNode = levelNodes[index];
|
|
947
|
-
|
|
948
|
-
|
|
1706
|
+
const entries = level.lights.map((light, i) => ({ id: `light/${index}/${i}`, light }));
|
|
1707
|
+
// A named room with no light of its own (none whose plan point is in it, given or built into
|
|
1708
|
+
// an item) gets a plain ceiling light at its seed.
|
|
1709
|
+
const lit = [
|
|
1710
|
+
...level.lights.map((light) => [light.position[0], light.position[2]]),
|
|
1711
|
+
...(level.items ?? []).flatMap((item) => {
|
|
1712
|
+
const frame = itemFrames.get(item.id);
|
|
1713
|
+
return frame && input.compiled[item.id]?.manifest.lights?.length
|
|
1714
|
+
? [[frame.position[0], frame.position[2]]]
|
|
1715
|
+
: [];
|
|
1716
|
+
}),
|
|
1717
|
+
];
|
|
1718
|
+
const seen = new Set();
|
|
1719
|
+
level.rooms.forEach((room, i) => {
|
|
1720
|
+
const zone = written[report.ids[`room/${index}/${i}`] ?? ''];
|
|
1721
|
+
if (!zone || seen.has(zone.id))
|
|
1722
|
+
return;
|
|
1723
|
+
seen.add(zone.id);
|
|
1724
|
+
const inside = (p) => pointInPolygon(p, zone.polygon) && !zone.holes.some((hole) => pointInPolygon(p, hole));
|
|
1725
|
+
if (lit.some(inside))
|
|
1726
|
+
return;
|
|
1727
|
+
const over = ceilingAt(index, room.seed[0], room.seed[1]);
|
|
1728
|
+
if (!over)
|
|
1729
|
+
return;
|
|
1730
|
+
entries.push({
|
|
1731
|
+
id: roomLightId(index, i),
|
|
1732
|
+
light: {
|
|
1733
|
+
position: [room.seed[0], over.height - 0.1, room.seed[1]],
|
|
1734
|
+
color: '#ffffff',
|
|
1735
|
+
intensity: 1,
|
|
1736
|
+
},
|
|
1737
|
+
room: zone.name ?? room.name,
|
|
1738
|
+
});
|
|
1739
|
+
});
|
|
1740
|
+
for (const { id, light, room: unlit } of entries) {
|
|
949
1741
|
const at = [light.position[0], light.position[1], light.position[2]];
|
|
950
1742
|
let hostId = light.fixture ? itemNodeIds.get(light.fixture) : undefined;
|
|
951
1743
|
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) {
|
|
1744
|
+
const over = light.fixture ? undefined : ceilingAt(index, at[0], at[2]);
|
|
1745
|
+
if (over) {
|
|
1746
|
+
const { room, ceiling } = over;
|
|
963
1747
|
const compiled = input.compiled[id];
|
|
964
1748
|
if (!compiled) {
|
|
965
1749
|
defect(id, 'compile_failed', input.compileErrors?.[id] ?? 'The ceiling point module did not compile.');
|
|
@@ -980,12 +1764,14 @@ export function authorScene(scene, input, context) {
|
|
|
980
1764
|
written[ceiling.id] = { ...ceiling, children: [...ceiling.children, node.id] };
|
|
981
1765
|
hostId = node.id;
|
|
982
1766
|
hostFrame = {
|
|
983
|
-
position: [at[0],
|
|
1767
|
+
position: [at[0], over.height, at[2]],
|
|
984
1768
|
yaw: 0,
|
|
985
1769
|
};
|
|
986
1770
|
report.ids[id] = node.id;
|
|
987
1771
|
report.items++;
|
|
988
|
-
say(
|
|
1772
|
+
say(unlit
|
|
1773
|
+
? `${unlit} had no light: a ceiling light at its centre`
|
|
1774
|
+
: `light at [${at.join(', ')}] has no fixture: a ceiling point in ${room.name}`);
|
|
989
1775
|
}
|
|
990
1776
|
catch (error) {
|
|
991
1777
|
const e = error;
|
|
@@ -1023,6 +1809,9 @@ export function authorScene(scene, input, context) {
|
|
|
1023
1809
|
color: light.color,
|
|
1024
1810
|
intensityRange: [0, light.intensity],
|
|
1025
1811
|
offset: toLocal(hostFrame, at),
|
|
1812
|
+
spot: lightSpot(light, at, hostFrame),
|
|
1813
|
+
shadows: light.shadows,
|
|
1814
|
+
glow: light.glow,
|
|
1026
1815
|
},
|
|
1027
1816
|
];
|
|
1028
1817
|
written[hostNode.id] = {
|
|
@@ -1032,14 +1821,129 @@ export function authorScene(scene, input, context) {
|
|
|
1032
1821
|
report.lights++;
|
|
1033
1822
|
}
|
|
1034
1823
|
});
|
|
1035
|
-
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1824
|
+
// ---------- the roof: on the building's roof level above the top storey, as create_roof puts it ----------
|
|
1825
|
+
const roof = payload.roof;
|
|
1826
|
+
if (roof && (roof.segments.length || roof.items?.length)) {
|
|
1827
|
+
const top = levelNodes.at(-1);
|
|
1828
|
+
const segments = roof.segments.map((spec, i) => {
|
|
1829
|
+
const segment = RoofSegmentNode.parse({
|
|
1830
|
+
roofType: spec.roofType,
|
|
1831
|
+
width: spec.width,
|
|
1832
|
+
depth: spec.depth,
|
|
1833
|
+
wallHeight: 0,
|
|
1834
|
+
rotation: (spec.rotation * Math.PI) / 180,
|
|
1835
|
+
position: [spec.center[0], 0, spec.center[1]],
|
|
1836
|
+
...(spec.pitch !== undefined && { pitch: spec.pitch }),
|
|
1837
|
+
...(spec.gableOverhang !== undefined && { gableOverhang: spec.gableOverhang }),
|
|
1838
|
+
});
|
|
1839
|
+
// The overhang is asked in plan; the segment stores it along its slope (a barrel's, at its ends).
|
|
1840
|
+
const overhang = spec.roofType === 'barrel'
|
|
1841
|
+
? spec.overhang
|
|
1842
|
+
: spec.overhang / getSegmentSlopeFrame(segment).cosTheta;
|
|
1843
|
+
return { ...segment, id: mint('rseg', `roof/segment/${i}`), overhang };
|
|
1844
|
+
});
|
|
1845
|
+
const peak = Math.max(0, ...segments.map((segment) => getActiveRoofHeight(segment)));
|
|
1846
|
+
// Nothing stands above the payload's top level in its candidate: the plan is a roof level.
|
|
1847
|
+
const plan = planRoofLevel(written, top.id, peak);
|
|
1848
|
+
const created = plan.kind === 'roof' ? plan.create : null;
|
|
1849
|
+
const levelId = created ? mint('level', 'level/roof') : plan.levelId;
|
|
1850
|
+
if (created)
|
|
1851
|
+
written = applySceneChanges(written, {
|
|
1852
|
+
create: [{ node: { ...created, id: levelId }, parentId: building.id }],
|
|
1853
|
+
});
|
|
1854
|
+
report.ids['level/roof'] = levelId;
|
|
1855
|
+
// A roof measured from the top storey's floor stands this much lower on the roof level.
|
|
1856
|
+
const offset = plan.storeyOffset;
|
|
1857
|
+
let rises = peak;
|
|
1858
|
+
if (segments.length) {
|
|
1859
|
+
const cx = segments.reduce((sum, s) => sum + s.position[0], 0) / segments.length;
|
|
1860
|
+
const cz = segments.reduce((sum, s) => sum + s.position[2], 0) / segments.length;
|
|
1861
|
+
const node = RoofNode.parse({
|
|
1862
|
+
id: mint('roof', 'roof'),
|
|
1863
|
+
name: roof.name ?? 'Roof',
|
|
1864
|
+
parentId: levelId,
|
|
1865
|
+
position: [cx, 0, cz],
|
|
1866
|
+
rotation: 0,
|
|
1867
|
+
support: { kind: 'walls' },
|
|
1868
|
+
children: segments.map((segment) => segment.id),
|
|
1869
|
+
metadata: { authorScene: { id: 'roof' } },
|
|
1870
|
+
});
|
|
1871
|
+
written = applySceneChanges(written, {
|
|
1872
|
+
create: [
|
|
1873
|
+
{ node, parentId: levelId },
|
|
1874
|
+
...segments.map((segment) => ({
|
|
1875
|
+
node: {
|
|
1876
|
+
...segment,
|
|
1877
|
+
parentId: node.id,
|
|
1878
|
+
position: [segment.position[0] - cx, 0, segment.position[2] - cz],
|
|
1879
|
+
},
|
|
1880
|
+
parentId: node.id,
|
|
1881
|
+
})),
|
|
1882
|
+
],
|
|
1883
|
+
});
|
|
1884
|
+
const elevation = resolveRoofElevation(written[node.id], written);
|
|
1885
|
+
written[node.id] = { ...node, position: [cx, elevation, cz] };
|
|
1886
|
+
rises = elevation + peak;
|
|
1887
|
+
report.ids.roof = node.id;
|
|
1888
|
+
report.roofSegments = segments.length;
|
|
1889
|
+
for (const [role, color] of [
|
|
1890
|
+
['top', roof.color],
|
|
1891
|
+
['wall', roof.wallColor],
|
|
1892
|
+
]) {
|
|
1893
|
+
const finish = finishRef(color, 'roof');
|
|
1894
|
+
if (finish)
|
|
1895
|
+
try {
|
|
1896
|
+
const { changes } = paint(written, { targets: [node.id], material: finish, role }, paintContext(levelId));
|
|
1897
|
+
for (const u of changes?.update ?? [])
|
|
1898
|
+
written[u.id] = { ...written[u.id], ...u.data };
|
|
1899
|
+
}
|
|
1900
|
+
catch (error) {
|
|
1901
|
+
defect('roof', 'paint_refused', error.message);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
for (const item of roof.items ?? []) {
|
|
1906
|
+
const compiled = input.compiled[item.id];
|
|
1907
|
+
if (!compiled) {
|
|
1908
|
+
defect(item.id, 'compile_failed', input.compileErrors?.[item.id] ?? 'The module did not compile.');
|
|
1909
|
+
continue;
|
|
1910
|
+
}
|
|
1911
|
+
try {
|
|
1912
|
+
const outcome = addObject(written, {
|
|
1913
|
+
compiled: { ...compiled, nodeId: mint('item', item.id) },
|
|
1914
|
+
name: item.name,
|
|
1915
|
+
category: item.category,
|
|
1916
|
+
reason: `author_scene: ${item.name}`,
|
|
1917
|
+
position: [item.position[0], item.position[1] + offset, item.position[2]],
|
|
1918
|
+
rotation: item.rotation,
|
|
1919
|
+
parentId: levelId,
|
|
1920
|
+
role: 'roof',
|
|
1921
|
+
}, { ...context, activeLevelId: levelId });
|
|
1922
|
+
for (const { node } of outcome.changes?.create ?? []) {
|
|
1923
|
+
written = applySceneChanges(written, {
|
|
1924
|
+
create: [
|
|
1925
|
+
{
|
|
1926
|
+
node: { ...node, slots: exactSlots(compiled, item.id) },
|
|
1927
|
+
parentId: levelId,
|
|
1928
|
+
},
|
|
1929
|
+
],
|
|
1930
|
+
});
|
|
1931
|
+
report.ids[item.id] = node.id;
|
|
1932
|
+
report.items++;
|
|
1933
|
+
rises = Math.max(rises, item.position[1] + offset + scriptedSize(compiled.manifest)[1]);
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
catch (error) {
|
|
1937
|
+
const e = error;
|
|
1938
|
+
defect(item.id, e.code ?? 'item_refused', e.message ?? String(error));
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
// A roof level made for it is as tall as the roof rises over it.
|
|
1942
|
+
if (created)
|
|
1943
|
+
written[levelId] = { ...written[levelId], height: Math.max(rises, 0.2) };
|
|
1944
|
+
}
|
|
1945
|
+
const nodes = plain(Object.fromEntries(Object.entries(written).filter(([id]) => !scaffoldIds.has(id))));
|
|
1946
|
+
const candidate = { buildingId: building.id, seed, ids: report.ids, nodes };
|
|
1043
1947
|
const outcome = {
|
|
1044
1948
|
result: report,
|
|
1045
1949
|
candidate,
|
|
@@ -1053,24 +1957,88 @@ export function finalizeAuthorScene(scene, outcome) {
|
|
|
1053
1957
|
const building = scene[outcome.buildingId];
|
|
1054
1958
|
if (building?.type !== 'building')
|
|
1055
1959
|
refuse('no_building', 'The target building no longer exists.');
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1960
|
+
const { candidate } = outcome;
|
|
1961
|
+
const current = Object.fromEntries(Object.entries(scene).map(([id, node]) => [id, withoutBookkeeping(node)]));
|
|
1962
|
+
const base = authoredBaseline(scene, building)?.nodes ??
|
|
1963
|
+
Object.fromEntries(queryNodes(current, { type: 'level', parentId: building.id }).map((node) => [node.id, node]));
|
|
1964
|
+
const changes = authoredSceneChanges(current, base, candidate.nodes);
|
|
1965
|
+
// Each written node keeps its own baseline; each written level lists what stands on it and the
|
|
1966
|
+
// building what it holds itself (its units), so a revision finds every node of the last one.
|
|
1967
|
+
const holderOf = (id) => {
|
|
1968
|
+
let node = candidate.nodes[id];
|
|
1969
|
+
while (node && node.type !== 'level') {
|
|
1970
|
+
if (node.parentId === building.id)
|
|
1971
|
+
return building.id;
|
|
1972
|
+
node = node.parentId ? candidate.nodes[node.parentId] : undefined;
|
|
1973
|
+
}
|
|
1974
|
+
return node?.id;
|
|
1975
|
+
};
|
|
1976
|
+
const keyOf = new Map(Object.entries(candidate.ids).map(([key, id]) => [id, key]));
|
|
1977
|
+
const writtenOn = new Map();
|
|
1978
|
+
for (const id of Object.keys(candidate.nodes)) {
|
|
1979
|
+
const holder = holderOf(id);
|
|
1980
|
+
if (!holder)
|
|
1981
|
+
continue;
|
|
1982
|
+
const written = writtenOn.get(holder) ?? { ids: [], keys: {} };
|
|
1983
|
+
written.ids.push(id);
|
|
1984
|
+
const key = keyOf.get(id);
|
|
1985
|
+
if (key)
|
|
1986
|
+
written.keys[key] = id;
|
|
1987
|
+
writtenOn.set(holder, written);
|
|
1988
|
+
}
|
|
1989
|
+
const bookkeeping = (id) => ({
|
|
1990
|
+
authorSceneBase: withoutBookkeeping(candidate.nodes[id]),
|
|
1991
|
+
...(writtenOn.has(id) ? { authorSceneWritten: writtenOn.get(id) } : {}),
|
|
1992
|
+
});
|
|
1993
|
+
const create = (changes.create ?? []).map((entry) => candidate.nodes[entry.node.id]
|
|
1994
|
+
? {
|
|
1995
|
+
...entry,
|
|
1996
|
+
node: {
|
|
1997
|
+
...entry.node,
|
|
1998
|
+
metadata: { ...entry.node.metadata, ...bookkeeping(entry.node.id) },
|
|
1999
|
+
},
|
|
2000
|
+
}
|
|
2001
|
+
: entry);
|
|
2002
|
+
const created = new Set(create.map((entry) => entry.node.id));
|
|
2003
|
+
const removed = new Set(changes.delete ?? []);
|
|
2004
|
+
const updated = new Map((changes.update ?? []).map((entry) => [entry.id, entry]));
|
|
2005
|
+
for (const id of Object.keys(candidate.nodes)) {
|
|
2006
|
+
const now = scene[id];
|
|
2007
|
+
if (created.has(id) || removed.has(id) || !now)
|
|
2008
|
+
continue;
|
|
2009
|
+
const kept = { ...bookkeeping(id) };
|
|
2010
|
+
const stored = Object.fromEntries(BOOKKEEPING.flatMap((key) => now.metadata && key in now.metadata ? [[key, now.metadata[key]]] : []));
|
|
2011
|
+
const entry = updated.get(id);
|
|
2012
|
+
if (!entry && equalJson(stored, kept))
|
|
2013
|
+
continue;
|
|
2014
|
+
const metadata = (entry?.data.metadata ??
|
|
2015
|
+
current[id]?.metadata ??
|
|
2016
|
+
{});
|
|
2017
|
+
updated.set(id, {
|
|
2018
|
+
id,
|
|
2019
|
+
data: { ...entry?.data, metadata: { ...metadata, ...kept } },
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
const state = { version: 2, buildingId: building.id, seed: candidate.seed };
|
|
2023
|
+
updated.set(building.id, {
|
|
2024
|
+
id: building.id,
|
|
2025
|
+
data: {
|
|
2026
|
+
...updated.get(building.id)?.data,
|
|
2027
|
+
metadata: {
|
|
2028
|
+
...withoutBookkeeping(building).metadata,
|
|
2029
|
+
authorSceneState: state,
|
|
2030
|
+
...(writtenOn.has(building.id) ? { authorSceneWritten: writtenOn.get(building.id) } : {}),
|
|
2031
|
+
},
|
|
2032
|
+
},
|
|
2033
|
+
});
|
|
1062
2034
|
return {
|
|
1063
2035
|
...outcome,
|
|
1064
2036
|
changes: {
|
|
1065
2037
|
...changes,
|
|
2038
|
+
create,
|
|
1066
2039
|
...(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
|
-
],
|
|
2040
|
+
update: [...updated.values()],
|
|
1074
2041
|
},
|
|
1075
2042
|
};
|
|
1076
2043
|
}
|
|
2044
|
+
const equalJson = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|