@pascal-app/core 1.0.4 → 1.0.5
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 +102 -90
- package/dist/agent-operations/__fixtures__/add-object-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/add-object-cases.js +301 -1
- package/dist/agent-operations/__fixtures__/add-stair-cases.d.ts +3 -0
- package/dist/agent-operations/__fixtures__/add-stair-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/add-stair-cases.js +176 -0
- package/dist/agent-operations/__fixtures__/build-room-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/build-room-cases.js +410 -40
- package/dist/agent-operations/__fixtures__/cases.d.ts +18 -3
- package/dist/agent-operations/__fixtures__/cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/cases.js +183 -4
- package/dist/agent-operations/__fixtures__/corner-window-cases.d.ts +3 -0
- package/dist/agent-operations/__fixtures__/corner-window-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/corner-window-cases.js +155 -0
- package/dist/agent-operations/__fixtures__/design-brief-cases.d.ts +14 -0
- package/dist/agent-operations/__fixtures__/design-brief-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/design-brief-cases.js +709 -0
- package/dist/agent-operations/__fixtures__/finish-cases.d.ts +7 -0
- package/dist/agent-operations/__fixtures__/finish-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/finish-cases.js +758 -0
- package/dist/agent-operations/__fixtures__/lot-info-cases.d.ts +3 -0
- package/dist/agent-operations/__fixtures__/lot-info-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/lot-info-cases.js +370 -0
- package/dist/agent-operations/__fixtures__/merge-windows-cases.d.ts +3 -0
- package/dist/agent-operations/__fixtures__/merge-windows-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/merge-windows-cases.js +321 -0
- package/dist/agent-operations/__fixtures__/native-materials.d.ts +2237 -0
- package/dist/agent-operations/__fixtures__/native-materials.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/native-materials.js +82 -0
- package/dist/agent-operations/__fixtures__/node-read-cases.d.ts +4 -0
- package/dist/agent-operations/__fixtures__/node-read-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/node-read-cases.js +639 -0
- package/dist/agent-operations/__fixtures__/notation-cases.d.ts +24 -0
- package/dist/agent-operations/__fixtures__/notation-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/notation-cases.js +356 -0
- package/dist/agent-operations/__fixtures__/place-items-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/place-items-cases.js +80 -2
- package/dist/agent-operations/__fixtures__/roof-bearing-cases.d.ts +19726 -0
- package/dist/agent-operations/__fixtures__/roof-bearing-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/roof-bearing-cases.js +106 -0
- package/dist/agent-operations/__fixtures__/roof-cases.d.ts +9 -0
- package/dist/agent-operations/__fixtures__/roof-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/roof-cases.js +382 -0
- package/dist/agent-operations/__fixtures__/room-cases.d.ts +2 -1
- package/dist/agent-operations/__fixtures__/room-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/room-cases.js +136 -1
- package/dist/agent-operations/__fixtures__/scene-check-cases.d.ts +4 -0
- package/dist/agent-operations/__fixtures__/scene-check-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/scene-check-cases.js +417 -0
- package/dist/agent-operations/__fixtures__/search-assets-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/search-assets-cases.js +15 -3
- package/dist/agent-operations/__fixtures__/site-cases.d.ts +290 -0
- package/dist/agent-operations/__fixtures__/site-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/site-cases.js +780 -0
- package/dist/agent-operations/__fixtures__/structure-cases.d.ts +1 -0
- package/dist/agent-operations/__fixtures__/structure-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/structure-cases.js +630 -3
- package/dist/agent-operations/__fixtures__/verify-scene-cases.d.ts +311 -1
- package/dist/agent-operations/__fixtures__/verify-scene-cases.d.ts.map +1 -1
- package/dist/agent-operations/__fixtures__/verify-scene-cases.js +59 -11
- package/dist/agent-operations/__fixtures__/view-assets-cases.d.ts +3 -0
- package/dist/agent-operations/__fixtures__/view-assets-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/view-assets-cases.js +155 -0
- package/dist/agent-operations/__fixtures__/view-scene-cases.d.ts +50 -0
- package/dist/agent-operations/__fixtures__/view-scene-cases.d.ts.map +1 -0
- package/dist/agent-operations/__fixtures__/view-scene-cases.js +425 -0
- package/dist/agent-operations/achieved.d.ts +5 -0
- package/dist/agent-operations/achieved.d.ts.map +1 -1
- package/dist/agent-operations/achieved.js +21 -1
- package/dist/agent-operations/add-column.d.ts.map +1 -1
- package/dist/agent-operations/add-column.js +3 -1
- package/dist/agent-operations/add-fence.d.ts +18 -0
- package/dist/agent-operations/add-fence.d.ts.map +1 -0
- package/dist/agent-operations/add-fence.js +48 -0
- package/dist/agent-operations/add-level.d.ts +4 -1
- package/dist/agent-operations/add-level.d.ts.map +1 -1
- package/dist/agent-operations/add-level.js +48 -12
- package/dist/agent-operations/add-object.d.ts +38 -9
- package/dist/agent-operations/add-object.d.ts.map +1 -1
- package/dist/agent-operations/add-object.js +172 -11
- package/dist/agent-operations/add-wall.d.ts +1 -3
- package/dist/agent-operations/add-wall.d.ts.map +1 -1
- package/dist/agent-operations/add-wall.js +35 -9
- package/dist/agent-operations/apply-changes.d.ts +7 -1
- package/dist/agent-operations/apply-changes.d.ts.map +1 -1
- package/dist/agent-operations/apply-changes.js +54 -22
- package/dist/agent-operations/apply-outcome.d.ts +6 -0
- package/dist/agent-operations/apply-outcome.d.ts.map +1 -1
- package/dist/agent-operations/apply-outcome.js +23 -8
- package/dist/agent-operations/asset-views.d.ts +75 -0
- package/dist/agent-operations/asset-views.d.ts.map +1 -0
- package/dist/agent-operations/asset-views.js +234 -0
- package/dist/agent-operations/author-scene.d.ts +157 -0
- package/dist/agent-operations/author-scene.d.ts.map +1 -0
- package/dist/agent-operations/author-scene.js +1076 -0
- package/dist/agent-operations/authored-scene-merge.d.ts +4 -0
- package/dist/agent-operations/authored-scene-merge.d.ts.map +1 -0
- package/dist/agent-operations/authored-scene-merge.js +96 -0
- package/dist/agent-operations/batch.d.ts +22 -0
- package/dist/agent-operations/batch.d.ts.map +1 -0
- package/dist/agent-operations/batch.js +104 -0
- package/dist/agent-operations/ceiling-object-observations.d.ts +31 -0
- package/dist/agent-operations/ceiling-object-observations.d.ts.map +1 -0
- package/dist/agent-operations/ceiling-object-observations.js +111 -0
- package/dist/agent-operations/corner-window.d.ts +19 -0
- package/dist/agent-operations/corner-window.d.ts.map +1 -0
- package/dist/agent-operations/corner-window.js +84 -0
- package/dist/agent-operations/create-roof.d.ts +32 -0
- package/dist/agent-operations/create-roof.d.ts.map +1 -0
- package/dist/agent-operations/create-roof.js +304 -0
- package/dist/agent-operations/create-room.d.ts +2 -0
- package/dist/agent-operations/create-room.d.ts.map +1 -1
- package/dist/agent-operations/create-room.js +73 -5
- package/dist/agent-operations/create-stair.d.ts +6 -28
- package/dist/agent-operations/create-stair.d.ts.map +1 -1
- package/dist/agent-operations/create-stair.js +187 -104
- package/dist/agent-operations/design-brief.d.ts +38 -0
- package/dist/agent-operations/design-brief.d.ts.map +1 -0
- package/dist/agent-operations/design-brief.js +300 -0
- package/dist/agent-operations/designs.d.ts +24 -0
- package/dist/agent-operations/designs.d.ts.map +1 -0
- package/dist/agent-operations/designs.js +48 -0
- package/dist/agent-operations/door-clearance.d.ts +16 -1
- package/dist/agent-operations/door-clearance.d.ts.map +1 -1
- package/dist/agent-operations/door-clearance.js +61 -4
- package/dist/agent-operations/duplicate-level.d.ts.map +1 -1
- package/dist/agent-operations/duplicate-level.js +2 -1
- package/dist/agent-operations/find-by-type.d.ts +14 -7
- package/dist/agent-operations/find-by-type.d.ts.map +1 -1
- package/dist/agent-operations/find-by-type.js +266 -43
- package/dist/agent-operations/furnish-room.d.ts +22 -1
- package/dist/agent-operations/furnish-room.d.ts.map +1 -1
- package/dist/agent-operations/furnish-room.js +66 -19
- package/dist/agent-operations/furniture-kind.d.ts +22 -0
- package/dist/agent-operations/furniture-kind.d.ts.map +1 -0
- package/dist/agent-operations/furniture-kind.js +69 -0
- package/dist/agent-operations/get-node.d.ts +5 -2
- package/dist/agent-operations/get-node.d.ts.map +1 -1
- package/dist/agent-operations/get-node.js +82 -10
- package/dist/agent-operations/hosted-services.d.ts +2 -1
- package/dist/agent-operations/hosted-services.d.ts.map +1 -1
- package/dist/agent-operations/index.d.ts +281 -69
- package/dist/agent-operations/index.d.ts.map +1 -1
- package/dist/agent-operations/index.js +66 -5
- package/dist/agent-operations/item-space.d.ts +22 -0
- package/dist/agent-operations/item-space.d.ts.map +1 -0
- package/dist/agent-operations/item-space.js +69 -0
- package/dist/agent-operations/layout-clearance.d.ts +52 -3
- package/dist/agent-operations/layout-clearance.d.ts.map +1 -1
- package/dist/agent-operations/layout-clearance.js +340 -30
- package/dist/agent-operations/level-height-fill.d.ts +12 -0
- package/dist/agent-operations/level-height-fill.d.ts.map +1 -0
- package/dist/agent-operations/level-height-fill.js +59 -0
- package/dist/agent-operations/level-reads.d.ts +5 -1
- package/dist/agent-operations/level-reads.d.ts.map +1 -1
- package/dist/agent-operations/level-reads.js +10 -3
- package/dist/agent-operations/list-levels.d.ts.map +1 -1
- package/dist/agent-operations/list-levels.js +23 -0
- package/dist/agent-operations/material-preset.d.ts.map +1 -1
- package/dist/agent-operations/material-preset.js +3 -9
- package/dist/agent-operations/material-refs.d.ts +7 -1
- package/dist/agent-operations/material-refs.d.ts.map +1 -1
- package/dist/agent-operations/material-refs.js +10 -5
- package/dist/agent-operations/merge-windows.d.ts +6 -0
- package/dist/agent-operations/merge-windows.d.ts.map +1 -0
- package/dist/agent-operations/merge-windows.js +27 -0
- package/dist/agent-operations/move-roofs.d.ts +9 -0
- package/dist/agent-operations/move-roofs.d.ts.map +1 -0
- package/dist/agent-operations/move-roofs.js +29 -0
- package/dist/agent-operations/paint.d.ts +2 -1
- package/dist/agent-operations/paint.d.ts.map +1 -1
- package/dist/agent-operations/paint.js +163 -53
- package/dist/agent-operations/photo-elevation.d.ts +155 -0
- package/dist/agent-operations/photo-elevation.d.ts.map +1 -0
- package/dist/agent-operations/photo-elevation.js +761 -0
- package/dist/agent-operations/place-in-room.d.ts +29 -0
- package/dist/agent-operations/place-in-room.d.ts.map +1 -0
- package/dist/agent-operations/place-in-room.js +403 -0
- package/dist/agent-operations/place-items.d.ts.map +1 -1
- package/dist/agent-operations/place-items.js +15 -2
- package/dist/agent-operations/plan-geometry.d.ts.map +1 -1
- package/dist/agent-operations/plan-geometry.js +3 -0
- package/dist/agent-operations/point-context.d.ts +139 -0
- package/dist/agent-operations/point-context.d.ts.map +1 -0
- package/dist/agent-operations/point-context.js +1 -0
- package/dist/agent-operations/project-files.d.ts +9 -0
- package/dist/agent-operations/project-files.d.ts.map +1 -0
- package/dist/agent-operations/project-files.js +25 -0
- package/dist/agent-operations/record-made.d.ts +18 -0
- package/dist/agent-operations/record-made.d.ts.map +1 -0
- package/dist/agent-operations/record-made.js +28 -0
- package/dist/agent-operations/roof-footprint.d.ts +24 -0
- package/dist/agent-operations/roof-footprint.d.ts.map +1 -0
- package/dist/agent-operations/roof-footprint.js +242 -0
- package/dist/agent-operations/roof-writes.d.ts +20 -0
- package/dist/agent-operations/roof-writes.d.ts.map +1 -0
- package/dist/agent-operations/roof-writes.js +165 -0
- package/dist/agent-operations/room-findings.d.ts +54 -0
- package/dist/agent-operations/room-findings.d.ts.map +1 -0
- package/dist/agent-operations/room-findings.js +143 -0
- package/dist/agent-operations/room-floor-script.d.ts +23 -0
- package/dist/agent-operations/room-floor-script.d.ts.map +1 -0
- package/dist/agent-operations/room-floor-script.js +54 -0
- package/dist/agent-operations/room-structure.d.ts +2 -2
- package/dist/agent-operations/room-structure.d.ts.map +1 -1
- package/dist/agent-operations/scene-checkpoint.d.ts +94 -0
- package/dist/agent-operations/scene-checkpoint.d.ts.map +1 -0
- package/dist/agent-operations/scene-checkpoint.js +176 -0
- package/dist/agent-operations/scene-queries.d.ts +3750 -3319
- package/dist/agent-operations/scene-queries.d.ts.map +1 -1
- package/dist/agent-operations/scene-queries.js +13 -23
- package/dist/agent-operations/scene-review.d.ts +52 -0
- package/dist/agent-operations/scene-review.d.ts.map +1 -0
- package/dist/agent-operations/scene-review.js +106 -0
- package/dist/agent-operations/scene-view.d.ts +82 -15
- package/dist/agent-operations/scene-view.d.ts.map +1 -1
- package/dist/agent-operations/scene-view.js +408 -51
- package/dist/agent-operations/search-assets.d.ts +2 -2
- package/dist/agent-operations/search-assets.d.ts.map +1 -1
- package/dist/agent-operations/search-assets.js +4 -3
- package/dist/agent-operations/search-materials.d.ts +9 -3
- package/dist/agent-operations/search-materials.d.ts.map +1 -1
- package/dist/agent-operations/search-materials.js +83 -29
- package/dist/agent-operations/set-checkpoint.d.ts +9 -0
- package/dist/agent-operations/set-checkpoint.d.ts.map +1 -0
- package/dist/agent-operations/set-checkpoint.js +31 -0
- package/dist/agent-operations/set-ground-level.d.ts +12 -0
- package/dist/agent-operations/set-ground-level.d.ts.map +1 -0
- package/dist/agent-operations/set-ground-level.js +61 -0
- package/dist/agent-operations/site.d.ts +21 -0
- package/dist/agent-operations/site.d.ts.map +1 -0
- package/dist/agent-operations/site.js +123 -0
- package/dist/agent-operations/types.d.ts +66 -7
- package/dist/agent-operations/types.d.ts.map +1 -1
- package/dist/agent-operations/verify-scene.d.ts +39 -5
- package/dist/agent-operations/verify-scene.d.ts.map +1 -1
- package/dist/agent-operations/verify-scene.js +223 -44
- package/dist/agent-operations/wall-face-script.d.ts +28 -0
- package/dist/agent-operations/wall-face-script.d.ts.map +1 -0
- package/dist/agent-operations/wall-face-script.js +62 -0
- package/dist/agent-operations/wall-object-observations.d.ts +41 -0
- package/dist/agent-operations/wall-object-observations.d.ts.map +1 -0
- package/dist/agent-operations/wall-object-observations.js +249 -0
- package/dist/agent-operations/wall-opening.d.ts +23 -23
- package/dist/agent-operations/wall-opening.d.ts.map +1 -1
- package/dist/agent-operations/wall-skirting.d.ts +24 -0
- package/dist/agent-operations/wall-skirting.d.ts.map +1 -0
- package/dist/agent-operations/wall-skirting.js +85 -0
- package/dist/agent-tools/add-object.d.ts +11 -7
- package/dist/agent-tools/add-object.d.ts.map +1 -1
- package/dist/agent-tools/add-object.js +17 -11
- package/dist/agent-tools/assets.d.ts +8 -0
- package/dist/agent-tools/assets.d.ts.map +1 -1
- package/dist/agent-tools/assets.js +13 -1
- package/dist/agent-tools/author-scene.d.ts +651 -0
- package/dist/agent-tools/author-scene.d.ts.map +1 -0
- package/dist/agent-tools/author-scene.js +252 -0
- package/dist/agent-tools/batch.d.ts +25 -0
- package/dist/agent-tools/batch.d.ts.map +1 -0
- package/dist/agent-tools/batch.js +61 -0
- package/dist/agent-tools/checkpoints.d.ts +13 -0
- package/dist/agent-tools/checkpoints.d.ts.map +1 -0
- package/dist/agent-tools/checkpoints.js +16 -0
- package/dist/agent-tools/clear-scene.d.ts.map +1 -1
- package/dist/agent-tools/collections.d.ts +6 -6
- package/dist/agent-tools/collections.d.ts.map +1 -1
- package/dist/agent-tools/columns.d.ts +18 -18
- package/dist/agent-tools/columns.js +2 -2
- package/dist/agent-tools/corner-window.d.ts +46 -0
- package/dist/agent-tools/corner-window.d.ts.map +1 -0
- package/dist/agent-tools/corner-window.js +47 -0
- package/dist/agent-tools/create-room.d.ts +26 -25
- package/dist/agent-tools/create-room.d.ts.map +1 -1
- package/dist/agent-tools/create-room.js +11 -4
- package/dist/agent-tools/design-brief.d.ts +408 -0
- package/dist/agent-tools/design-brief.d.ts.map +1 -0
- package/dist/agent-tools/design-brief.js +149 -0
- package/dist/agent-tools/designs.d.ts +34 -0
- package/dist/agent-tools/designs.d.ts.map +1 -0
- package/dist/agent-tools/designs.js +50 -0
- package/dist/agent-tools/fences.d.ts +28 -0
- package/dist/agent-tools/fences.d.ts.map +1 -0
- package/dist/agent-tools/fences.js +31 -0
- package/dist/agent-tools/find-by-type.d.ts +8 -1
- package/dist/agent-tools/find-by-type.d.ts.map +1 -1
- package/dist/agent-tools/find-by-type.js +23 -2
- package/dist/agent-tools/furnish-room.d.ts +10 -8
- package/dist/agent-tools/furnish-room.d.ts.map +1 -1
- package/dist/agent-tools/furnish-room.js +3 -1
- package/dist/agent-tools/hosted-services.d.ts +177 -141
- package/dist/agent-tools/hosted-services.d.ts.map +1 -1
- package/dist/agent-tools/hosted-services.js +47 -14
- package/dist/agent-tools/index.d.ts +894 -215
- package/dist/agent-tools/index.d.ts.map +1 -1
- package/dist/agent-tools/index.js +47 -3
- package/dist/agent-tools/levels.d.ts +11 -3
- package/dist/agent-tools/levels.d.ts.map +1 -1
- package/dist/agent-tools/levels.js +16 -5
- package/dist/agent-tools/measurement.d.ts +38 -20
- package/dist/agent-tools/measurement.d.ts.map +1 -1
- package/dist/agent-tools/measurement.js +117 -67
- package/dist/agent-tools/merge-windows.d.ts +10 -0
- package/dist/agent-tools/merge-windows.d.ts.map +1 -0
- package/dist/agent-tools/merge-windows.js +12 -0
- package/dist/agent-tools/nodes.d.ts.map +1 -1
- package/dist/agent-tools/nodes.js +1 -1
- package/dist/agent-tools/notation-inventory.d.ts +16 -0
- package/dist/agent-tools/notation-inventory.d.ts.map +1 -0
- package/dist/agent-tools/notation-inventory.js +105 -0
- package/dist/agent-tools/paint.d.ts +5 -5
- package/dist/agent-tools/paint.d.ts.map +1 -1
- package/dist/agent-tools/paint.js +1 -1
- package/dist/agent-tools/photo-elevation.d.ts +32 -0
- package/dist/agent-tools/photo-elevation.d.ts.map +1 -0
- package/dist/agent-tools/photo-elevation.js +73 -0
- package/dist/agent-tools/place-in-room.d.ts +35 -0
- package/dist/agent-tools/place-in-room.d.ts.map +1 -0
- package/dist/agent-tools/place-in-room.js +61 -0
- package/dist/agent-tools/place-items.d.ts +1 -1
- package/dist/agent-tools/place-items.js +2 -2
- package/dist/agent-tools/project-files.d.ts +189 -0
- package/dist/agent-tools/project-files.d.ts.map +1 -0
- package/dist/agent-tools/project-files.js +99 -0
- package/dist/agent-tools/refusal.d.ts.map +1 -1
- package/dist/agent-tools/retired.d.ts +21 -0
- package/dist/agent-tools/retired.d.ts.map +1 -0
- package/dist/agent-tools/retired.js +29 -0
- package/dist/agent-tools/roofs.d.ts +100 -0
- package/dist/agent-tools/roofs.d.ts.map +1 -0
- package/dist/agent-tools/roofs.js +96 -0
- package/dist/agent-tools/room-structure.d.ts +46 -38
- package/dist/agent-tools/room-structure.d.ts.map +1 -1
- package/dist/agent-tools/room-structure.js +30 -27
- package/dist/agent-tools/search-materials.d.ts +22 -3
- package/dist/agent-tools/search-materials.d.ts.map +1 -1
- package/dist/agent-tools/search-materials.js +13 -14
- package/dist/agent-tools/site.d.ts +27 -0
- package/dist/agent-tools/site.d.ts.map +1 -0
- package/dist/agent-tools/site.js +40 -0
- package/dist/agent-tools/sources.d.ts +12 -0
- package/dist/agent-tools/sources.d.ts.map +1 -0
- package/dist/agent-tools/sources.js +11 -0
- package/dist/agent-tools/stairs.d.ts +48 -28
- package/dist/agent-tools/stairs.d.ts.map +1 -1
- package/dist/agent-tools/stairs.js +65 -22
- package/dist/agent-tools/verify-scene.d.ts.map +1 -1
- package/dist/agent-tools/verify-scene.js +1 -1
- package/dist/agent-tools/view-scene.d.ts +50 -12
- package/dist/agent-tools/view-scene.d.ts.map +1 -1
- package/dist/agent-tools/view-scene.js +74 -31
- package/dist/agent-tools/wall-openings.d.ts +77 -73
- package/dist/agent-tools/wall-openings.d.ts.map +1 -1
- package/dist/agent-tools/wall-openings.js +9 -5
- package/dist/agent-tools/walls.d.ts +4 -4
- package/dist/agent-tools/walls.d.ts.map +1 -1
- package/dist/agent-tools/walls.js +7 -2
- package/dist/building/__fixtures__/wall-opening-cases.d.ts +454 -250
- package/dist/building/__fixtures__/wall-opening-cases.d.ts.map +1 -1
- package/dist/building/__fixtures__/wall-opening-cases.js +62 -2
- package/dist/building/corner-window.d.ts +56 -0
- package/dist/building/corner-window.d.ts.map +1 -0
- package/dist/building/corner-window.js +179 -0
- package/dist/building/ground-level.d.ts +26 -0
- package/dist/building/ground-level.d.ts.map +1 -0
- package/dist/building/ground-level.js +58 -0
- package/dist/building/index.d.ts +5 -0
- package/dist/building/index.d.ts.map +1 -1
- package/dist/building/index.js +5 -0
- package/dist/building/level-duplication.d.ts +7472 -6613
- package/dist/building/level-duplication.d.ts.map +1 -1
- package/dist/building/level-duplication.js +18 -4
- package/dist/building/level-insertion.d.ts +55 -0
- package/dist/building/level-insertion.d.ts.map +1 -0
- package/dist/building/level-insertion.js +35 -0
- package/dist/building/merge-windows.d.ts +58 -0
- package/dist/building/merge-windows.d.ts.map +1 -0
- package/dist/building/merge-windows.js +189 -0
- package/dist/building/opening-style-presets.d.ts +2 -2
- package/dist/building/opening-style-presets.d.ts.map +1 -1
- package/dist/building/photo-elevation.d.ts +125 -0
- package/dist/building/photo-elevation.d.ts.map +1 -0
- package/dist/building/photo-elevation.js +404 -0
- package/dist/building/wall-gaps.d.ts +13 -0
- package/dist/building/wall-gaps.d.ts.map +1 -0
- package/dist/building/wall-gaps.js +158 -0
- package/dist/building/wall-openings.d.ts +154 -119
- package/dist/building/wall-openings.d.ts.map +1 -1
- package/dist/building/wall-openings.js +37 -9
- package/dist/capture/schema.d.ts +22 -22
- package/dist/capture/source.d.ts.map +1 -1
- package/dist/capture/source.js +1 -1
- package/dist/commands/structure/create-zone.d.ts +12 -1
- package/dist/commands/structure/create-zone.d.ts.map +1 -1
- package/dist/commands/structure/create-zone.js +49 -0
- package/dist/commands/structure/divide-zone.d.ts +3 -3
- package/dist/commands/structure/index.d.ts +1 -1
- package/dist/commands/structure/index.d.ts.map +1 -1
- package/dist/commands/structure/index.js +1 -1
- package/dist/commands/structure/set-floor-foundation.d.ts +6 -6
- package/dist/commands/structure/set-zone-intent.d.ts +4 -0
- package/dist/commands/structure/set-zone-intent.d.ts.map +1 -1
- package/dist/commands/structure/set-zone-intent.js +19 -0
- package/dist/commands/structure/shared.d.ts +3642 -3213
- package/dist/commands/structure/shared.d.ts.map +1 -1
- package/dist/commands/structure/zone-wall-merge.d.ts +4149 -3334
- package/dist/commands/structure/zone-wall-merge.d.ts.map +1 -1
- package/dist/contracts/reference-inventory.d.ts.map +1 -1
- package/dist/contracts/reference-inventory.js +106 -26
- package/dist/events/bus.d.ts +22 -3
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/capture-sessions.d.ts +5 -0
- package/dist/events/capture-sessions.d.ts.map +1 -0
- package/dist/events/capture-sessions.js +31 -0
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +1 -2
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +4 -6
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +3608 -3179
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/support-host-patch.js +11 -2
- package/dist/hooks/spatial-grid/use-spatial-query.d.ts +3 -3
- package/dist/hooks/spatial-grid/wall-spatial-grid.d.ts.map +1 -1
- package/dist/index.d.ts +37 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -9
- package/dist/lib/__fixtures__/mezzanine.d.ts +11800 -10132
- package/dist/lib/__fixtures__/mezzanine.d.ts.map +1 -1
- package/dist/lib/absorb-wall-separators.d.ts +3642 -3213
- package/dist/lib/absorb-wall-separators.d.ts.map +1 -1
- package/dist/lib/angle-accumulator.d.ts.map +1 -1
- package/dist/lib/artifact-store.d.ts.map +1 -1
- package/dist/lib/below-grade-footprints.d.ts +65 -0
- package/dist/lib/below-grade-footprints.d.ts.map +1 -0
- package/dist/lib/below-grade-footprints.js +181 -0
- package/dist/lib/building-footprint.d.ts +33 -0
- package/dist/lib/building-footprint.d.ts.map +1 -0
- package/dist/lib/building-footprint.js +89 -0
- package/dist/lib/corner-pair.d.ts +1129 -0
- package/dist/lib/corner-pair.d.ts.map +1 -0
- package/dist/lib/corner-pair.js +130 -0
- package/dist/lib/corner-snap.d.ts +54 -0
- package/dist/lib/corner-snap.d.ts.map +1 -0
- package/dist/lib/corner-snap.js +125 -0
- package/dist/lib/critically-damped.d.ts +11 -0
- package/dist/lib/critically-damped.d.ts.map +1 -0
- package/dist/lib/critically-damped.js +14 -0
- package/dist/lib/deepest-drawn.d.ts +18 -0
- package/dist/lib/deepest-drawn.d.ts.map +1 -0
- package/dist/lib/deepest-drawn.js +65 -0
- package/dist/lib/door-operation.d.ts +1 -1
- package/dist/lib/exterior-wall-loops.d.ts +17 -0
- package/dist/lib/exterior-wall-loops.d.ts.map +1 -0
- package/dist/lib/exterior-wall-loops.js +334 -0
- package/dist/lib/finish-script.d.ts +61 -0
- package/dist/lib/finish-script.d.ts.map +1 -0
- package/dist/lib/finish-script.js +86 -0
- package/dist/lib/floor-intent-changes.d.ts.map +1 -1
- package/dist/lib/floor-intent-changes.js +4 -4
- package/dist/lib/floor-plate-paint.d.ts +3 -1
- package/dist/lib/floor-plate-paint.d.ts.map +1 -1
- package/dist/lib/floor-plate-paint.js +24 -4
- package/dist/lib/floor-plates.d.ts +50 -50
- package/dist/lib/floor-script.d.ts +24 -0
- package/dist/lib/floor-script.d.ts.map +1 -0
- package/dist/lib/floor-script.js +51 -0
- package/dist/lib/foundation-construction.d.ts +73 -0
- package/dist/lib/foundation-construction.d.ts.map +1 -0
- package/dist/lib/foundation-construction.js +242 -0
- package/dist/lib/foundation-standard.d.ts +30 -0
- package/dist/lib/foundation-standard.d.ts.map +1 -0
- package/dist/lib/foundation-standard.js +30 -0
- package/dist/lib/foundation-wall.d.ts +36 -0
- package/dist/lib/foundation-wall.d.ts.map +1 -0
- package/dist/lib/foundation-wall.js +66 -0
- package/dist/lib/generic-foundation.d.ts +40 -0
- package/dist/lib/generic-foundation.d.ts.map +1 -0
- package/dist/lib/generic-foundation.js +200 -0
- package/dist/lib/geometry-script-node.d.ts +32 -3
- package/dist/lib/geometry-script-node.d.ts.map +1 -1
- package/dist/lib/geometry-script-node.js +26 -0
- package/dist/lib/grade-standing.d.ts +38 -0
- package/dist/lib/grade-standing.d.ts.map +1 -0
- package/dist/lib/grade-standing.js +137 -0
- package/dist/lib/ground-level.d.ts +20 -0
- package/dist/lib/ground-level.d.ts.map +1 -0
- package/dist/lib/ground-level.js +49 -0
- package/dist/lib/ground-over-part.d.ts +13 -0
- package/dist/lib/ground-over-part.d.ts.map +1 -0
- package/dist/lib/ground-over-part.js +94 -0
- package/dist/lib/ground-seat.d.ts +47 -0
- package/dist/lib/ground-seat.d.ts.map +1 -0
- package/dist/lib/ground-seat.js +61 -0
- package/dist/lib/ground-window.d.ts +39 -0
- package/dist/lib/ground-window.d.ts.map +1 -0
- package/dist/lib/ground-window.js +147 -0
- package/dist/lib/opening-floor-datum.d.ts.map +1 -1
- package/dist/lib/opening-floor-datum.js +2 -3
- package/dist/lib/own-floor-intent.d.ts.map +1 -1
- package/dist/lib/own-floor-intent.js +4 -4
- package/dist/lib/plate-surface.d.ts.map +1 -1
- package/dist/lib/plate-surface.js +3 -1
- package/dist/lib/point-ask.d.ts +9 -0
- package/dist/lib/point-ask.d.ts.map +1 -0
- package/dist/lib/point-ask.js +36 -0
- package/dist/lib/room-drawn-floor.d.ts +1 -1
- package/dist/lib/room-drawn-floor.d.ts.map +1 -1
- package/dist/lib/room-floor-feasibility.d.ts.map +1 -1
- package/dist/lib/room-floor-feasibility.js +3 -3
- package/dist/lib/room-graph.d.ts.map +1 -1
- package/dist/lib/room-graph.js +2 -0
- package/dist/lib/room-name.d.ts +2 -0
- package/dist/lib/room-name.d.ts.map +1 -1
- package/dist/lib/room-name.js +2 -0
- package/dist/lib/room-topology-index.d.ts.map +1 -1
- package/dist/lib/room-topology-index.js +1 -1
- package/dist/lib/room-zone-adoption.d.ts.map +1 -1
- package/dist/lib/script-inputs-key.d.ts +3 -0
- package/dist/lib/script-inputs-key.d.ts.map +1 -0
- package/dist/lib/script-inputs-key.js +13 -0
- package/dist/lib/setback-envelope.d.ts +95 -5
- package/dist/lib/setback-envelope.d.ts.map +1 -1
- package/dist/lib/setback-envelope.js +351 -35
- package/dist/lib/site-flatwork.d.ts +11 -0
- package/dist/lib/site-flatwork.d.ts.map +1 -0
- package/dist/lib/site-flatwork.js +11 -0
- package/dist/lib/site-ground-height.d.ts +13 -0
- package/dist/lib/site-ground-height.d.ts.map +1 -0
- package/dist/lib/site-ground-height.js +16 -0
- package/dist/lib/site-placement.d.ts +362 -0
- package/dist/lib/site-placement.d.ts.map +1 -0
- package/dist/lib/site-placement.js +942 -0
- package/dist/lib/snap-ease.d.ts +20 -0
- package/dist/lib/snap-ease.d.ts.map +1 -0
- package/dist/lib/snap-ease.js +50 -0
- package/dist/lib/structure-kernel.d.ts +1 -1
- package/dist/lib/structure-kernel.d.ts.map +1 -1
- package/dist/lib/structure-kernel.js +2 -0
- package/dist/lib/surface-support-references.d.ts +4 -0
- package/dist/lib/surface-support-references.d.ts.map +1 -0
- package/dist/lib/surface-support-references.js +19 -0
- package/dist/lib/terrain-support-query.d.ts +10 -0
- package/dist/lib/terrain-support-query.d.ts.map +1 -1
- package/dist/lib/terrain-support-query.js +1 -1
- package/dist/lib/wall-face-script.d.ts +66 -0
- package/dist/lib/wall-face-script.d.ts.map +1 -0
- package/dist/lib/wall-face-script.js +235 -0
- package/dist/lib/zone-colors.d.ts +1 -1
- package/dist/lib/zone-colors.d.ts.map +1 -1
- package/dist/lib/zone-quantities.d.ts.map +1 -1
- package/dist/lib/zone-quantities.js +4 -5
- package/dist/material-kinds.d.ts +9 -0
- package/dist/material-kinds.d.ts.map +1 -0
- package/dist/material-kinds.js +30 -0
- package/dist/material-library.d.ts +4 -4
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +72 -26
- package/dist/node-slots/index.d.ts +1 -0
- package/dist/node-slots/index.d.ts.map +1 -1
- package/dist/node-slots/index.js +7 -1
- package/dist/node-slots/roof.d.ts +5 -0
- package/dist/node-slots/roof.d.ts.map +1 -0
- package/dist/node-slots/roof.js +12 -0
- package/dist/node-slots/stair.d.ts +2 -0
- package/dist/node-slots/stair.d.ts.map +1 -1
- package/dist/node-slots/stair.js +6 -2
- package/dist/procedural-items/design-placement.d.ts.map +1 -1
- package/dist/procedural-items/design.d.ts +19 -19
- package/dist/procedural-items/fixtures.d.ts +297 -297
- package/dist/procedural-items/fixtures.d.ts.map +1 -1
- package/dist/procedural-items/integration.d.ts +106 -106
- package/dist/procedural-items/joints.d.ts +4 -4
- package/dist/procedural-items/library-colors.d.ts +22 -22
- package/dist/procedural-items/library-colors.d.ts.map +1 -1
- package/dist/procedural-items/motion-controller.d.ts.map +1 -1
- package/dist/procedural-items/node.d.ts +291 -291
- package/dist/procedural-items/query.d.ts +1 -1
- package/dist/procedural-items/query.d.ts.map +1 -1
- package/dist/procedural-items/recipe.d.ts +11 -11
- package/dist/procedural-items/recipe.d.ts.map +1 -1
- package/dist/procedural-items/section.d.ts.map +1 -1
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/scene-api.d.ts +1 -0
- package/dist/registry/scene-api.d.ts.map +1 -1
- package/dist/registry/scene-api.js +3 -0
- package/dist/registry/subtree.d.ts.map +1 -1
- package/dist/registry/subtree.js +2 -0
- package/dist/registry/types.d.ts +120 -1
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/schema/assembly.d.ts +41 -41
- package/dist/schema/asset-url.d.ts +1 -1
- package/dist/schema/asset-url.d.ts.map +1 -1
- package/dist/schema/base.d.ts +9 -9
- package/dist/schema/base.d.ts.map +1 -1
- package/dist/schema/camera.d.ts +1 -1
- package/dist/schema/collection-templates.d.ts +20 -20
- package/dist/schema/collection-templates.d.ts.map +1 -1
- package/dist/schema/geometry-metadata.d.ts +7 -7
- package/dist/schema/geometry-metadata.d.ts.map +1 -1
- package/dist/schema/geometry-source.d.ts +26 -16
- package/dist/schema/geometry-source.d.ts.map +1 -1
- package/dist/schema/geometry-source.js +14 -0
- package/dist/schema/index.d.ts +10 -7
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +10 -7
- package/dist/schema/material.d.ts +33 -33
- package/dist/schema/nodes/barrel-roof.d.ts +3 -0
- package/dist/schema/nodes/barrel-roof.d.ts.map +1 -0
- package/dist/schema/nodes/barrel-roof.js +11 -0
- package/dist/schema/nodes/block.d.ts +8 -8
- package/dist/schema/nodes/box-vent.d.ts +13 -13
- package/dist/schema/nodes/building.d.ts +9 -8
- package/dist/schema/nodes/building.d.ts.map +1 -1
- package/dist/schema/nodes/building.js +2 -0
- package/dist/schema/nodes/cabinet.d.ts +130 -129
- package/dist/schema/nodes/cabinet.d.ts.map +1 -1
- package/dist/schema/nodes/cabinet.js +1 -1
- package/dist/schema/nodes/ceiling.d.ts +15 -15
- package/dist/schema/nodes/chimney.d.ts +23 -23
- package/dist/schema/nodes/column.d.ts +207 -199
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +2 -0
- package/dist/schema/nodes/construction-dimension.d.ts +34 -34
- package/dist/schema/nodes/cupola.d.ts +15 -15
- package/dist/schema/nodes/curtain-wall.d.ts +21 -20
- package/dist/schema/nodes/curtain-wall.d.ts.map +1 -1
- package/dist/schema/nodes/curtain-wall.js +23 -0
- package/dist/schema/nodes/door.d.ts +50 -43
- package/dist/schema/nodes/door.d.ts.map +1 -1
- package/dist/schema/nodes/door.js +19 -0
- package/dist/schema/nodes/dormer.d.ts +37 -36
- package/dist/schema/nodes/dormer.d.ts.map +1 -1
- package/dist/schema/nodes/downspout.d.ts +17 -17
- package/dist/schema/nodes/duct-fitting.d.ts +24 -24
- package/dist/schema/nodes/duct-segment.d.ts +14 -14
- package/dist/schema/nodes/duct-terminal.d.ts +12 -12
- package/dist/schema/nodes/elevator.d.ts +15 -15
- package/dist/schema/nodes/eyebrow-vent.d.ts +13 -13
- package/dist/schema/nodes/fence-feature.d.ts +37 -37
- package/dist/schema/nodes/fence-types.d.ts +32 -0
- package/dist/schema/nodes/fence-types.d.ts.map +1 -0
- package/dist/schema/nodes/fence-types.js +22 -0
- package/dist/schema/nodes/fence.d.ts +43 -72
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +4 -20
- package/dist/schema/nodes/floor-opening.d.ts +8 -8
- package/dist/schema/nodes/guide.d.ts +8 -8
- package/dist/schema/nodes/gutter.d.ts +14 -14
- package/dist/schema/nodes/gutter.d.ts.map +1 -1
- package/dist/schema/nodes/gutter.js +6 -5
- package/dist/schema/nodes/hvac-equipment.d.ts +13 -13
- package/dist/schema/nodes/imported-mesh.d.ts +8 -8
- package/dist/schema/nodes/item.d.ts +30 -25
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/lean-to-extension.d.ts +36 -36
- package/dist/schema/nodes/level.d.ts +9 -9
- package/dist/schema/nodes/lineset.d.ts +8 -8
- package/dist/schema/nodes/liquid-line.d.ts +8 -8
- package/dist/schema/nodes/measurement.d.ts +8 -8
- package/dist/schema/nodes/opening-types.d.ts +13 -13
- package/dist/schema/nodes/opening-types.d.ts.map +1 -1
- package/dist/schema/nodes/pipe-fitting.d.ts +15 -15
- package/dist/schema/nodes/pipe-segment.d.ts +11 -11
- package/dist/schema/nodes/pipe-trap.d.ts +9 -9
- package/dist/schema/nodes/ridge-vent.d.ts +14 -14
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -1
- package/dist/schema/nodes/ridge-vent.js +3 -1
- package/dist/schema/nodes/roof-glazing-config.d.ts +90 -0
- package/dist/schema/nodes/roof-glazing-config.d.ts.map +1 -0
- package/dist/schema/nodes/roof-glazing-config.js +34 -0
- package/dist/schema/nodes/roof-glazing.d.ts +57 -0
- package/dist/schema/nodes/roof-glazing.d.ts.map +1 -0
- package/dist/schema/nodes/roof-glazing.js +514 -0
- package/dist/schema/nodes/roof-segment-resize.d.ts +4 -0
- package/dist/schema/nodes/roof-segment-resize.d.ts.map +1 -0
- package/dist/schema/nodes/roof-segment-resize.js +21 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts +5 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-shape.js +44 -0
- package/dist/schema/nodes/roof-segment-walls.d.ts +1 -1
- package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-walls.js +24 -0
- package/dist/schema/nodes/roof-segment.d.ts +153 -46
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +91 -20
- package/dist/schema/nodes/roof-types.d.ts +14 -0
- package/dist/schema/nodes/roof-types.d.ts.map +1 -0
- package/dist/schema/nodes/roof-types.js +14 -0
- package/dist/schema/nodes/roof.d.ts +49 -49
- package/dist/schema/nodes/scan.d.ts +8 -8
- package/dist/schema/nodes/separator.d.ts +8 -8
- package/dist/schema/nodes/shelf.d.ts +16 -16
- package/dist/schema/nodes/site-facts.d.ts +64 -0
- package/dist/schema/nodes/site-facts.d.ts.map +1 -0
- package/dist/schema/nodes/site-facts.js +73 -0
- package/dist/schema/nodes/site.d.ts +105 -63
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/site.js +7 -70
- package/dist/schema/nodes/skylight.d.ts +41 -41
- package/dist/schema/nodes/skylight.d.ts.map +1 -1
- package/dist/schema/nodes/slab.d.ts +21 -21
- package/dist/schema/nodes/solar-panel.d.ts +21 -21
- package/dist/schema/nodes/spawn.d.ts +8 -8
- package/dist/schema/nodes/stair-construction.d.ts +3 -3
- package/dist/schema/nodes/stair-defaults.d.ts +16 -0
- package/dist/schema/nodes/stair-defaults.d.ts.map +1 -0
- package/dist/schema/nodes/stair-defaults.js +15 -0
- package/dist/schema/nodes/stair-segment.d.ts +22 -22
- package/dist/schema/nodes/stair-types.d.ts +29 -0
- package/dist/schema/nodes/stair-types.d.ts.map +1 -0
- package/dist/schema/nodes/stair-types.js +23 -0
- package/dist/schema/nodes/stair.d.ts +51 -75
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +6 -20
- package/dist/schema/nodes/structural-grid.d.ts +8 -8
- package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -2
- package/dist/schema/nodes/turbine-vent.d.ts +13 -13
- package/dist/schema/nodes/unit.d.ts +10 -10
- package/dist/schema/nodes/unit.d.ts.map +1 -1
- package/dist/schema/nodes/wall.d.ts +479 -146
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +19 -0
- package/dist/schema/nodes/window.d.ts +55 -39
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +10 -0
- package/dist/schema/nodes/zone.d.ts +231 -12
- package/dist/schema/nodes/zone.d.ts.map +1 -1
- package/dist/schema/nodes/zone.js +10 -0
- package/dist/schema/provenance.d.ts +21 -21
- package/dist/schema/scene-material.d.ts +5 -5
- package/dist/schema/site-presentation.d.ts +100 -0
- package/dist/schema/site-presentation.d.ts.map +1 -0
- package/dist/schema/site-presentation.js +83 -0
- package/dist/schema/types.d.ts +1954 -1414
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts.map +1 -1
- package/dist/services/index.d.ts +5 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +5 -1
- package/dist/services/level-height.d.ts.map +1 -1
- package/dist/services/level-height.js +7 -3
- package/dist/services/port-connectivity.d.ts +4 -0
- package/dist/services/port-connectivity.d.ts.map +1 -1
- package/dist/services/port-connectivity.js +35 -0
- package/dist/services/project-file-sink.d.ts +9 -0
- package/dist/services/project-file-sink.d.ts.map +1 -0
- package/dist/services/project-file-sink.js +13 -0
- package/dist/services/roof-bearing.d.ts +62 -0
- package/dist/services/roof-bearing.d.ts.map +1 -0
- package/dist/services/roof-bearing.js +234 -0
- package/dist/services/roof-level.d.ts +121 -0
- package/dist/services/roof-level.d.ts.map +1 -0
- package/dist/services/roof-level.js +280 -0
- package/dist/services/roof-move.d.ts +83 -0
- package/dist/services/roof-move.d.ts.map +1 -0
- package/dist/services/roof-move.js +316 -0
- package/dist/services/stair-surface-snap.d.ts +14 -0
- package/dist/services/stair-surface-snap.d.ts.map +1 -0
- package/dist/services/stair-surface-snap.js +116 -0
- package/dist/services/storey-datum.d.ts +12 -0
- package/dist/services/storey-datum.d.ts.map +1 -0
- package/dist/services/storey-datum.js +18 -0
- package/dist/services/storey.d.ts +51 -5
- package/dist/services/storey.d.ts.map +1 -1
- package/dist/services/storey.js +125 -8
- package/dist/solar-panel-presets.d.ts +2 -2
- package/dist/store/actions/node-actions.d.ts +3613 -3182
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +46 -1
- package/dist/store/derived-node-guard.d.ts.map +1 -1
- package/dist/store/history-control.d.ts +39 -0
- package/dist/store/history-control.d.ts.map +1 -1
- package/dist/store/history-control.js +84 -4
- package/dist/store/scene-annotations.d.ts +7267 -6409
- package/dist/store/scene-annotations.d.ts.map +1 -1
- package/dist/store/scene-preview.d.ts +16 -0
- package/dist/store/scene-preview.d.ts.map +1 -0
- package/dist/store/scene-preview.js +144 -0
- package/dist/store/use-live-node-overrides.d.ts.map +1 -1
- package/dist/store/use-live-terrain.d.ts.map +1 -1
- package/dist/store/use-live-transforms.d.ts.map +1 -1
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +37 -4
- package/dist/systems/elevator/elevator-dispatch.js +1 -1
- package/dist/systems/fence/fence-features.d.ts.map +1 -1
- package/dist/systems/owned-floor-openings.d.ts.map +1 -1
- package/dist/systems/owned-floor-openings.js +24 -3
- package/dist/systems/roof/roof-elevation-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-elevation-system.js +9 -3
- package/dist/systems/roof/roof-elevation.d.ts +5 -0
- package/dist/systems/roof/roof-elevation.d.ts.map +1 -1
- package/dist/systems/roof/roof-elevation.js +91 -62
- package/dist/systems/slab/__fixtures__/floor-step.d.ts +4623 -3516
- package/dist/systems/slab/__fixtures__/floor-step.d.ts.map +1 -1
- package/dist/systems/slab/__fixtures__/raised-room.d.ts +5149 -4076
- package/dist/systems/slab/__fixtures__/raised-room.d.ts.map +1 -1
- package/dist/systems/slab/slab-support.d.ts +6 -3
- package/dist/systems/slab/slab-support.d.ts.map +1 -1
- package/dist/systems/slab/slab-support.js +78 -10
- package/dist/systems/stair/stair-detail-budget.d.ts +2 -2
- package/dist/systems/stair/stair-detail-budget.d.ts.map +1 -1
- package/dist/systems/stair/stair-footprint.d.ts +6 -0
- package/dist/systems/stair/stair-footprint.d.ts.map +1 -1
- package/dist/systems/stair/stair-footprint.js +27 -14
- package/dist/systems/stair/stair-layout.d.ts +2 -2
- package/dist/systems/stair/stair-opening-preview.d.ts +3643 -3214
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
- package/dist/systems/stair/stair-presets.d.ts +73 -65
- package/dist/systems/stair/stair-presets.d.ts.map +1 -1
- package/dist/systems/stair/stair-presets.js +16 -9
- package/dist/systems/stair/stair-rail-path.d.ts +1 -1
- package/dist/systems/stair/stair-rise-query.d.ts +16 -0
- package/dist/systems/stair/stair-rise-query.d.ts.map +1 -1
- package/dist/systems/stair/stair-rise-query.js +57 -7
- package/dist/systems/stair/stair-sizing.d.ts +101 -99
- package/dist/systems/stair/stair-sizing.d.ts.map +1 -1
- package/dist/systems/wall/wall-assembly.d.ts +11 -0
- package/dist/systems/wall/wall-assembly.d.ts.map +1 -1
- package/dist/systems/wall/wall-assembly.js +21 -0
- package/dist/systems/wall/wall-curve.d.ts +5 -0
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +13 -0
- package/dist/systems/wall/wall-frame.d.ts.map +1 -1
- package/dist/systems/wall/wall-frame.js +8 -0
- package/dist/systems/wall/wall-room-sides.d.ts +8 -0
- package/dist/systems/wall/wall-room-sides.d.ts.map +1 -1
- package/dist/systems/wall/wall-room-sides.js +60 -7
- package/dist/systems/wall/wall-top.d.ts +7 -4
- package/dist/systems/wall/wall-top.d.ts.map +1 -1
- package/dist/systems/wall/wall-top.js +14 -6
- package/dist/systems/wall/wall-topology.d.ts +4 -0
- package/dist/systems/wall/wall-topology.d.ts.map +1 -1
- package/dist/systems/wall/wall-topology.js +4 -2
- package/dist/systems/wall/wall-trim.d.ts +7 -0
- package/dist/systems/wall/wall-trim.d.ts.map +1 -0
- package/dist/systems/wall/wall-trim.js +10 -0
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +8 -0
- package/dist/utils/ensure-scene-openings.d.ts +7420 -6562
- package/dist/utils/ensure-scene-openings.d.ts.map +1 -1
- package/dist/utils/floor-piece-adoption.d.ts +2 -2
- package/dist/utils/floor-plate-migration.d.ts +3746 -3317
- package/dist/utils/floor-plate-migration.d.ts.map +1 -1
- package/dist/utils/floor-plate-migration.js +2 -2
- package/dist/utils/heal-scene-graph.d.ts +12 -0
- package/dist/utils/heal-scene-graph.d.ts.map +1 -1
- package/dist/utils/heal-scene-graph.js +40 -23
- package/dist/utils/legacy-wall-datums.d.ts.map +1 -1
- package/dist/utils/legacy-wall-datums.js +2 -2
- package/dist/utils/owned-floor-opening-migration.d.ts +3642 -3213
- package/dist/utils/owned-floor-opening-migration.d.ts.map +1 -1
- package/dist/utils/reconcile-structure-on-load.d.ts +3730 -7777
- package/dist/utils/reconcile-structure-on-load.d.ts.map +1 -1
- package/dist/utils/scene-migrations.d.ts +1 -1
- package/dist/utils/scene-migrations.d.ts.map +1 -1
- package/dist/utils/scene-migrations.js +1 -1
- package/dist/utils/semantic-equal.d.ts +3 -0
- package/dist/utils/semantic-equal.d.ts.map +1 -0
- package/dist/utils/semantic-equal.js +26 -0
- package/dist/utils/structure-id.d.ts.map +1 -1
- package/dist/utils/types.d.ts.map +1 -1
- package/dist/utils/wall-face-migration.d.ts +1 -1
- package/dist/utils/wall-face-migration.d.ts.map +1 -1
- package/package.json +36 -10
|
@@ -0,0 +1,761 @@
|
|
|
1
|
+
import { refuse } from '../agent-tools/refusal.js';
|
|
2
|
+
import { applyHomography, cameraFaceHomography, faceAtWidth, faceCoordinates, faceThroughCorner, invertHomography, lineIntersection, multiplyHomography, pointOnFace, solveHomography, solvePhotoCamera, vanishingPoint, vanishingResidualPx, } from '../building/photo-elevation.js';
|
|
3
|
+
import { getLevelElevations } from '../services/storey.js';
|
|
4
|
+
/** Longest side of a drawing, and the finest scale worth drawing a facade at. */
|
|
5
|
+
const MAX_SIDE = 1200;
|
|
6
|
+
const MAX_PX_PER_METRE = 60;
|
|
7
|
+
/** Below this, windows of a photo carry too few pixels for panes. */
|
|
8
|
+
const FEW_PX_PER_METRE = 25;
|
|
9
|
+
/** A floor-to-floor height under this is a modelling default, not a building's: it squashes the drawing. */
|
|
10
|
+
const LOW_STOREY = 2.6;
|
|
11
|
+
/** Faces whose walls are this close to square are taken to meet at a right angle. */
|
|
12
|
+
const SQUARE = 0.1;
|
|
13
|
+
/** How far apart, as a share of the photo's longer side, two picks of one corner may be. */
|
|
14
|
+
const SAME_CORNER = 0.03;
|
|
15
|
+
/** Storeys the photo measures this much off the scene's are flagged. */
|
|
16
|
+
const STOREY_MISMATCH = 0.1;
|
|
17
|
+
/** Two faces putting their shared corner this much apart in depth disagree. */
|
|
18
|
+
const CORNER_MISMATCH = 0.05;
|
|
19
|
+
const ASSUMPTIONS = "Square pixels, no skew, no lens distortion, plumb walls, and faces whose walls meet square meet square in the photo. The principal point is taken at the photo's centre across (a photo cropped sideways breaks it); its height is fitted from the lines, since a perspective-corrected photo keeps its verticals parallel by moving it toward the horizon.";
|
|
20
|
+
const inside = ([x, y]) => x >= 0 && x <= 1 && y >= 0 && y <= 1;
|
|
21
|
+
const mean = (values) => values.reduce((sum, v) => sum + v, 0) / values.length;
|
|
22
|
+
function faceWalls(nodes, wallIds) {
|
|
23
|
+
const walls = wallIds.map((id) => {
|
|
24
|
+
const wall = nodes[id];
|
|
25
|
+
if (!wall)
|
|
26
|
+
refuse('wall_not_found', `Wall not found: ${id}.`, { wallId: id });
|
|
27
|
+
if (wall.type !== 'wall')
|
|
28
|
+
refuse('not_a_wall', `Node ${id} is a ${wall.type}, not a wall.`, { wallId: id });
|
|
29
|
+
return wall;
|
|
30
|
+
});
|
|
31
|
+
const [first] = walls;
|
|
32
|
+
if (!first || walls.some((wall) => wall.parentId !== first.parentId))
|
|
33
|
+
refuse('walls_not_one_face', 'Pass the walls of one face on one storey.', { wallIds });
|
|
34
|
+
const length = Math.hypot(first.end[0] - first.start[0], first.end[1] - first.start[1]);
|
|
35
|
+
const axis = [
|
|
36
|
+
(first.end[0] - first.start[0]) / length,
|
|
37
|
+
(first.end[1] - first.start[1]) / length,
|
|
38
|
+
];
|
|
39
|
+
const origin = first.start;
|
|
40
|
+
const points = walls.flatMap((wall) => [wall.start, wall.end]);
|
|
41
|
+
const off = ([x, z]) => Math.abs(-(x - origin[0]) * axis[1] + (z - origin[1]) * axis[0]);
|
|
42
|
+
if (points.some((point) => off(point) > 0.15))
|
|
43
|
+
refuse('walls_not_one_face', 'These walls are not one straight face: pass the walls along one side only, end to end.', { wallIds });
|
|
44
|
+
const along = points.map(([x, z]) => (x - origin[0]) * axis[0] + (z - origin[1]) * axis[1]);
|
|
45
|
+
const ends = [
|
|
46
|
+
points[along.indexOf(Math.min(...along))],
|
|
47
|
+
points[along.indexOf(Math.max(...along))],
|
|
48
|
+
];
|
|
49
|
+
return { width: Math.max(...along) - Math.min(...along), axis, ends, levelId: first.parentId };
|
|
50
|
+
}
|
|
51
|
+
function readFace(nodes, elevations, face, index, px) {
|
|
52
|
+
const name = face.name ?? `Face ${index + 1}`;
|
|
53
|
+
const picked = [
|
|
54
|
+
...(face.edges.left ?? []),
|
|
55
|
+
...(face.edges.right ?? []),
|
|
56
|
+
...face.rows.flatMap((r) => r.points),
|
|
57
|
+
...(face.lines ?? []).flat(),
|
|
58
|
+
];
|
|
59
|
+
if (picked.some((point) => !inside(point)))
|
|
60
|
+
refuse('point_outside_image', `${name}: every point is a fraction of the image, 0 to 1 across and down.`, { face: name });
|
|
61
|
+
if (!face.edges.left && !face.edges.right)
|
|
62
|
+
refuse('face_needs_an_edge', `${name}: pick at least one of its vertical edges; a face whose far edge leaves the photo gives the corner it shares with another face.`, { face: name });
|
|
63
|
+
const { width, axis, ends, levelId } = faceWalls(nodes, face.wallIds);
|
|
64
|
+
const rows = face.rows.map((row) => {
|
|
65
|
+
const level = elevations.get(row.levelId);
|
|
66
|
+
if (!level || nodes[row.levelId]?.type !== 'level')
|
|
67
|
+
refuse('level_not_found', `Level not found: ${row.levelId}.`, { levelId: row.levelId });
|
|
68
|
+
return {
|
|
69
|
+
levelId: row.levelId,
|
|
70
|
+
points: segment(row.points, px),
|
|
71
|
+
baseY: level.baseY,
|
|
72
|
+
...(row.height !== undefined ? { height: row.height } : {}),
|
|
73
|
+
y: level.baseY + (row.height ?? 0),
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
// Lowest first, whatever order the rows came in: the close-ups show the extremes.
|
|
77
|
+
rows.sort((a, b) => a.y - b.y);
|
|
78
|
+
if (new Set(rows.map((row) => Math.round(row.y * 1000))).size < 2)
|
|
79
|
+
refuse('rows_one_storey', `${name}: pick the same feature on at least two storeys, or two lines at heights you know on one (the floor at 0 and the eaves at the wall's height, say): the drawing needs a height to scale by.`, { face: name });
|
|
80
|
+
return {
|
|
81
|
+
name,
|
|
82
|
+
width,
|
|
83
|
+
axis,
|
|
84
|
+
ends,
|
|
85
|
+
levelId,
|
|
86
|
+
...(face.edges.left ? { left: segment(face.edges.left, px) } : {}),
|
|
87
|
+
...(face.edges.right ? { right: segment(face.edges.right, px) } : {}),
|
|
88
|
+
rows,
|
|
89
|
+
points: (face.points ?? []).map((point) => {
|
|
90
|
+
if (!inside(point.point))
|
|
91
|
+
refuse('point_outside_image', `${name}: every point is a fraction of the image, 0 to 1 across and down.`, { face: name });
|
|
92
|
+
const floor = elevations.get(point.levelId ?? levelId);
|
|
93
|
+
if (!floor)
|
|
94
|
+
refuse('level_not_found', `Level not found: ${point.levelId}.`, { levelId: point.levelId });
|
|
95
|
+
return { from: [point.along, floor.baseY + point.height], to: px(point.point) };
|
|
96
|
+
}),
|
|
97
|
+
lines: (face.lines ?? []).map((line) => segment(line, px)),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const segment = (points, px) => [
|
|
101
|
+
px(points[0]),
|
|
102
|
+
px(points[1]),
|
|
103
|
+
];
|
|
104
|
+
/** Where each row meets each picked edge, lowest row first. */
|
|
105
|
+
function cornersOf(face) {
|
|
106
|
+
const corners = [];
|
|
107
|
+
for (const row of face.rows)
|
|
108
|
+
for (const [edge, edgeLine] of [
|
|
109
|
+
['left', face.left],
|
|
110
|
+
['right', face.right],
|
|
111
|
+
]) {
|
|
112
|
+
if (!edgeLine)
|
|
113
|
+
continue;
|
|
114
|
+
const point = lineIntersection(row.points, edgeLine);
|
|
115
|
+
if (!point)
|
|
116
|
+
refuse('face_degenerate', `${face.name}: a row runs parallel to an edge.`, {
|
|
117
|
+
face: face.name,
|
|
118
|
+
});
|
|
119
|
+
corners.push({ edge, levelId: row.levelId, point: point, rowLine: row.points, edgeLine });
|
|
120
|
+
}
|
|
121
|
+
return corners;
|
|
122
|
+
}
|
|
123
|
+
/** The scene's storeys in the drawing: the picked feature on each, from the lowest row's. */
|
|
124
|
+
function featureLinesOf(nodes, elevations, face, extent) {
|
|
125
|
+
const lowest = face.rows[0].baseY;
|
|
126
|
+
const buildingId = elevations.get(face.rows[0].levelId)?.buildingId ?? null;
|
|
127
|
+
return [...elevations.entries()]
|
|
128
|
+
.filter(([, level]) => level.buildingId === buildingId)
|
|
129
|
+
.map(([levelId, level]) => ({ levelId, y: level.baseY - lowest, level }))
|
|
130
|
+
.filter(({ y }) => y >= extent.bottom && y <= extent.top)
|
|
131
|
+
.sort((a, b) => a.level.ordinal - b.level.ordinal)
|
|
132
|
+
.map(({ levelId, y }) => ({
|
|
133
|
+
levelId,
|
|
134
|
+
name: nodes[levelId]?.name ?? levelId,
|
|
135
|
+
y: (extent.top - y) * extent.pxPerMetre,
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
/** The rows' storeys: how many lie between the lowest and the highest, and their mean height. */
|
|
139
|
+
function sceneStorey(elevations, face) {
|
|
140
|
+
const ordinals = face.rows.map((row) => elevations.get(row.levelId).ordinal);
|
|
141
|
+
const storeys = Math.max(...ordinals) - Math.min(...ordinals);
|
|
142
|
+
return storeys > 0
|
|
143
|
+
? { storeys, height: (face.rows.at(-1).baseY - face.rows[0].baseY) / storeys }
|
|
144
|
+
: null;
|
|
145
|
+
}
|
|
146
|
+
function commonWarnings(residualPx, sourcePxPerMetre) {
|
|
147
|
+
const fewest = Math.min(sourcePxPerMetre.left, sourcePxPerMetre.right);
|
|
148
|
+
return [
|
|
149
|
+
...(residualPx > 3
|
|
150
|
+
? [
|
|
151
|
+
`The picked points disagree by ${residualPx.toFixed(1)} px: check the close-ups and pick again.`,
|
|
152
|
+
]
|
|
153
|
+
: []),
|
|
154
|
+
...(fewest < FEW_PX_PER_METRE
|
|
155
|
+
? [
|
|
156
|
+
`Only ${fewest.toFixed(0)} photo pixels per metre at one edge: panes and thin panels may not be readable there.`,
|
|
157
|
+
]
|
|
158
|
+
: []),
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* A face from its own picks alone: its homography from the rows × edges correspondences, its
|
|
163
|
+
* heights from the storeys. The way for one face, and for several when no camera fits them.
|
|
164
|
+
*/
|
|
165
|
+
function planFromOwnPicks(nodes, elevations, face) {
|
|
166
|
+
const { name, width, rows } = face;
|
|
167
|
+
const lowest = rows[0].y;
|
|
168
|
+
const left = face.left;
|
|
169
|
+
const right = face.right;
|
|
170
|
+
const corners = cornersOf(face);
|
|
171
|
+
// Each row against each edge: face metres from the left end and the lowest row, to pixels.
|
|
172
|
+
const pairs = rows.flatMap((row) => [
|
|
173
|
+
[0, left],
|
|
174
|
+
[width, right],
|
|
175
|
+
].map(([x, edge]) => ({
|
|
176
|
+
from: [x, row.y - lowest],
|
|
177
|
+
to: lineIntersection(row.points, edge),
|
|
178
|
+
})));
|
|
179
|
+
pairs.push(...face.points.map(({ from, to }) => ({ from: [from[0], from[1] - lowest], to })));
|
|
180
|
+
const toPhoto = solveHomography(pairs);
|
|
181
|
+
const fromPhoto = toPhoto && invertHomography(toPhoto);
|
|
182
|
+
if (!toPhoto || !fromPhoto)
|
|
183
|
+
refuse('face_degenerate', `${name}: the edges and rows do not span a face.`, { face: name });
|
|
184
|
+
const residualPx = Math.sqrt(pairs.reduce((sum, { from, to }) => {
|
|
185
|
+
const [u, v] = applyHomography(toPhoto, from);
|
|
186
|
+
return sum + (u - to[0]) ** 2 + (v - to[1]) ** 2;
|
|
187
|
+
}, 0) / pairs.length);
|
|
188
|
+
const heights = [...left, ...right].map((point) => applyHomography(fromPhoto, point)?.[1] ?? 0);
|
|
189
|
+
const bottom = Math.max(Math.min(...heights), -30);
|
|
190
|
+
const top = Math.min(Math.max(...heights), bottom + 60);
|
|
191
|
+
if (!(width > 0.5) || !(top - bottom > 0.5))
|
|
192
|
+
refuse('face_degenerate', `${name}: the face is too small to draw.`, { face: name });
|
|
193
|
+
const pxPerMetre = Math.min(MAX_PX_PER_METRE, MAX_SIDE / Math.max(width, top - bottom));
|
|
194
|
+
// Drawing pixel (u, v) is the face point (u / s, top − v / s).
|
|
195
|
+
const toFace = [1 / pxPerMetre, 0, 0, 0, -1 / pxPerMetre, top, 0, 0, 1];
|
|
196
|
+
const density = (edge) => {
|
|
197
|
+
const on = corners.filter((corner) => corner.edge === edge);
|
|
198
|
+
const byHeight = on.map((corner, i) => ({ corner, y: rows[i].y })).sort((a, b) => a.y - b.y);
|
|
199
|
+
const [low, high] = [byHeight[0], byHeight.at(-1)];
|
|
200
|
+
const metres = high.y - low.y;
|
|
201
|
+
return metres > 0
|
|
202
|
+
? Math.hypot(high.corner.point[0] - low.corner.point[0], high.corner.point[1] - low.corner.point[1]) / metres
|
|
203
|
+
: 0;
|
|
204
|
+
};
|
|
205
|
+
const sourcePxPerMetre = { left: density('left'), right: density('right') };
|
|
206
|
+
// The rows' storeys are the vertical scale: their mean floor-to-floor height, lowest to highest.
|
|
207
|
+
const storey = sceneStorey(elevations, face)?.height ?? null;
|
|
208
|
+
const warnings = [
|
|
209
|
+
...(storey !== null && storey < LOW_STOREY
|
|
210
|
+
? [
|
|
211
|
+
`The storeys here average ${storey.toFixed(2)} m floor to floor, and they set this drawing's vertical scale. A residential storey is usually 2.8–3.2 m: if the photo's windows fill most of a storey, raise the storeys first, then straighten again.`,
|
|
212
|
+
]
|
|
213
|
+
: []),
|
|
214
|
+
...commonWarnings(residualPx, sourcePxPerMetre),
|
|
215
|
+
];
|
|
216
|
+
const plan = {
|
|
217
|
+
name,
|
|
218
|
+
widthMeters: width,
|
|
219
|
+
leftMeters: 0,
|
|
220
|
+
rightMeters: width,
|
|
221
|
+
topMeters: top,
|
|
222
|
+
bottomMeters: bottom,
|
|
223
|
+
pxPerMetre,
|
|
224
|
+
outWidth: Math.round(width * pxPerMetre),
|
|
225
|
+
outHeight: Math.round((top - bottom) * pxPerMetre),
|
|
226
|
+
toSource: multiplyHomography(toPhoto, toFace),
|
|
227
|
+
sourcePxPerMetre,
|
|
228
|
+
residualPx,
|
|
229
|
+
featureLines: featureLinesOf(nodes, elevations, face, { bottom, top, pxPerMetre }),
|
|
230
|
+
corners,
|
|
231
|
+
warnings,
|
|
232
|
+
};
|
|
233
|
+
return { plan, toPhoto: toPhoto, bottom, top };
|
|
234
|
+
}
|
|
235
|
+
const FAILURES = {
|
|
236
|
+
needs_second_direction: {
|
|
237
|
+
code: 'camera_needs_second_direction',
|
|
238
|
+
message: 'One face gives the camera one horizontal direction, and it needs a second: the other face of the corner in the same call, or vertical lines that converge in the photo.',
|
|
239
|
+
},
|
|
240
|
+
at_infinity: {
|
|
241
|
+
code: 'vanishing_point_at_infinity',
|
|
242
|
+
message: "The picked lines run parallel in the photo, so their vanishing points are at infinity and do not fix the camera's focal length: pick each face's rows on storeys far apart, on faces seen at an angle.",
|
|
243
|
+
},
|
|
244
|
+
unsolved: {
|
|
245
|
+
code: 'camera_unsolved',
|
|
246
|
+
message: 'These lines fit no camera with square pixels and its principal point centred across the photo: a photo cropped sideways, a corner that is not square, or a line picked off.',
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
/** Mean distance, in photo pixels, of a segment's ends from the line through another. */
|
|
250
|
+
function offLine(points, line) {
|
|
251
|
+
const [[x1, y1], [x2, y2]] = line;
|
|
252
|
+
const length = Math.hypot(x2 - x1, y2 - y1) || 1;
|
|
253
|
+
return mean(points.map(([x, y]) => Math.abs((x2 - x1) * (y1 - y) - (x1 - x) * (y2 - y1)) / length));
|
|
254
|
+
}
|
|
255
|
+
function solveCamera(faces, verticals, image) {
|
|
256
|
+
const edges = faces.flatMap((face) => [face.left, face.right].filter((edge) => !!edge));
|
|
257
|
+
const vertical = vanishingPoint([...edges, ...verticals], image);
|
|
258
|
+
const vanishing = faces.map((face) => {
|
|
259
|
+
const point = vanishingPoint([...face.rows.map((row) => row.points), ...face.lines], image);
|
|
260
|
+
if (!point)
|
|
261
|
+
refuse('face_degenerate', `${face.name}: its rows lie on one line.`, { face: face.name });
|
|
262
|
+
return point;
|
|
263
|
+
});
|
|
264
|
+
const perpendicular = [];
|
|
265
|
+
for (let i = 0; i < faces.length; i++)
|
|
266
|
+
for (let j = i + 1; j < faces.length; j++) {
|
|
267
|
+
const [a, b] = [faces[i].axis, faces[j].axis];
|
|
268
|
+
if (Math.abs(a[0] * b[0] + a[1] * b[1]) < SQUARE)
|
|
269
|
+
perpendicular.push([i, j]);
|
|
270
|
+
}
|
|
271
|
+
const solved = solvePhotoCamera({
|
|
272
|
+
photo: image,
|
|
273
|
+
vertical,
|
|
274
|
+
faces: faces.map((face, index) => ({
|
|
275
|
+
vanishing: vanishing[index],
|
|
276
|
+
probe: face.rows[0].points[0],
|
|
277
|
+
})),
|
|
278
|
+
perpendicular,
|
|
279
|
+
});
|
|
280
|
+
return 'failure' in solved ? FAILURES[solved.failure] : solved;
|
|
281
|
+
}
|
|
282
|
+
function cameraSummary(camera, first, focalSpread) {
|
|
283
|
+
const degrees = (radians) => (radians * 180) / Math.PI;
|
|
284
|
+
const columns = [first.along, first.up, first.normal];
|
|
285
|
+
return {
|
|
286
|
+
focalPx: camera.focal,
|
|
287
|
+
focal35mm: (camera.focal * Math.hypot(36, 24)) / Math.hypot(camera.width, camera.height),
|
|
288
|
+
horizontalFovDeg: degrees(2 * Math.atan(camera.width / 2 / camera.focal)),
|
|
289
|
+
principalPointY: camera.centre[1],
|
|
290
|
+
pitchDeg: degrees(Math.asin(camera.up[2])),
|
|
291
|
+
rollDeg: degrees(Math.atan2(camera.up[0], -camera.up[1])),
|
|
292
|
+
rotation: [0, 1, 2].flatMap((row) => columns.map((column) => column[row])),
|
|
293
|
+
assumptions: ASSUMPTIONS,
|
|
294
|
+
warnings: focalSpread > 1.1
|
|
295
|
+
? [
|
|
296
|
+
`The picked lines disagree on the camera by ${((focalSpread - 1) * 100).toFixed(0)}%: pick each line longer, far apart from the others, and check the faces meet square.`,
|
|
297
|
+
]
|
|
298
|
+
: [],
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Each face from the shared camera: its plane from its walls' width between its two edges, or,
|
|
303
|
+
* when its far edge leaves the photo, through the corner it shares with such a face. The drawing
|
|
304
|
+
* is metric both ways, so the rows' storeys are measured rather than assumed.
|
|
305
|
+
*/
|
|
306
|
+
function planWithCamera(nodes, elevations, faces, camera, axes, anchors = []) {
|
|
307
|
+
const tolerance = SAME_CORNER * Math.max(camera.width, camera.height);
|
|
308
|
+
const planes = faces.map(() => null);
|
|
309
|
+
const cornerWarnings = faces.map(() => []);
|
|
310
|
+
for (const [index, face] of faces.entries()) {
|
|
311
|
+
if (!face.left || !face.right)
|
|
312
|
+
continue;
|
|
313
|
+
const plane = faceAtWidth(camera, axes[index], { left: face.left, right: face.right }, face.width);
|
|
314
|
+
if (!plane)
|
|
315
|
+
refuse('face_degenerate', `${face.name}: its edges and the camera disagree: is its left edge the photo's left?`, { face: face.name });
|
|
316
|
+
planes[index] = plane;
|
|
317
|
+
// Two faces sharing a corner should put it at one depth: wrong walls or a stray edge do not.
|
|
318
|
+
for (const [other, otherFace] of faces.entries()) {
|
|
319
|
+
const otherPlane = planes[other];
|
|
320
|
+
if (other === index || !otherPlane)
|
|
321
|
+
continue;
|
|
322
|
+
for (const edge of [face.left, face.right])
|
|
323
|
+
for (const otherEdge of [otherFace.left, otherFace.right]) {
|
|
324
|
+
if (offLine(edge, otherEdge) > tolerance)
|
|
325
|
+
continue;
|
|
326
|
+
const ratio = mean(edge.map((pixel) => {
|
|
327
|
+
const here = pointOnFace(camera, plane, pixel);
|
|
328
|
+
const there = pointOnFace(camera, otherPlane, pixel);
|
|
329
|
+
return here && there ? Math.hypot(...here) / Math.hypot(...there) : 1;
|
|
330
|
+
}));
|
|
331
|
+
if (Math.abs(ratio - 1) > CORNER_MISMATCH)
|
|
332
|
+
cornerWarnings[index].push(`Its corner with ${otherFace.name} comes out ${(Math.abs(ratio - 1) * 100).toFixed(0)}% ${ratio > 1 ? 'farther' : 'nearer'} than ${otherFace.name} puts it: the walls of one of the two faces, or one of their edges, is off.`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
for (const [index, face] of faces.entries()) {
|
|
337
|
+
if (planes[index])
|
|
338
|
+
continue;
|
|
339
|
+
const corner = (face.left ?? face.right);
|
|
340
|
+
let partner = null;
|
|
341
|
+
for (const [other, otherFace] of faces.entries()) {
|
|
342
|
+
const plane = otherFace.left && otherFace.right ? planes[other] : null;
|
|
343
|
+
if (!plane)
|
|
344
|
+
continue;
|
|
345
|
+
for (const edge of [otherFace.left, otherFace.right]) {
|
|
346
|
+
const distance = offLine(corner, edge);
|
|
347
|
+
if (distance <= tolerance && (!partner || distance < partner.distance))
|
|
348
|
+
partner = { plane, distance };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (!partner)
|
|
352
|
+
refuse('corner_not_shared', `${face.name} shows one edge: it is placed from the corner it shares with a face of this call whose two edges show. Pick that corner the same on both faces.`, { face: face.name });
|
|
353
|
+
const plane = faceThroughCorner(camera, axes[index], partner.plane, corner);
|
|
354
|
+
if (!plane)
|
|
355
|
+
refuse('face_degenerate', `${face.name}: its corner is behind the camera.`, {
|
|
356
|
+
face: face.name,
|
|
357
|
+
});
|
|
358
|
+
planes[index] = plane;
|
|
359
|
+
}
|
|
360
|
+
return faces.map((face, index) => {
|
|
361
|
+
const plane = planes[index];
|
|
362
|
+
const { name, width, rows } = face;
|
|
363
|
+
const at = (pixel) => {
|
|
364
|
+
const point = faceCoordinates(camera, plane, pixel);
|
|
365
|
+
if (!point)
|
|
366
|
+
refuse('face_degenerate', `${name}: a picked point falls behind the camera.`, {
|
|
367
|
+
face: name,
|
|
368
|
+
});
|
|
369
|
+
return point;
|
|
370
|
+
};
|
|
371
|
+
const rowHeights = rows.map((row) => mean(row.points.map((pixel) => at(pixel)[1])));
|
|
372
|
+
const datum = rowHeights[0];
|
|
373
|
+
const across = (edge) => mean(edge.map((pixel) => at(pixel)[0]));
|
|
374
|
+
const origin = [face.left ? across(face.left) : across(face.right) - width, datum];
|
|
375
|
+
const toPhoto = cameraFaceHomography(camera, plane, origin);
|
|
376
|
+
anchors[index] = {
|
|
377
|
+
plane,
|
|
378
|
+
origin,
|
|
379
|
+
bottom: Math.min(...[face.left, face.right]
|
|
380
|
+
.filter((edge) => !!edge)
|
|
381
|
+
.flat()
|
|
382
|
+
.map((pixel) => at(pixel)[1] - datum)),
|
|
383
|
+
};
|
|
384
|
+
const edges = [face.left, face.right].filter((edge) => !!edge);
|
|
385
|
+
const heights = edges.flat().map((pixel) => at(pixel)[1] - datum);
|
|
386
|
+
const bottom = Math.max(Math.min(...heights), -30);
|
|
387
|
+
const top = Math.min(Math.max(...heights), bottom + 60);
|
|
388
|
+
// A face with one edge is drawn from that corner to where it leaves the photo.
|
|
389
|
+
let [left, right] = [0, width];
|
|
390
|
+
if (!face.left || !face.right) {
|
|
391
|
+
const seen = (x, z) => {
|
|
392
|
+
const w = toPhoto[6] * x + toPhoto[7] * z + toPhoto[8];
|
|
393
|
+
if (!(w > 0))
|
|
394
|
+
return false;
|
|
395
|
+
const u = (toPhoto[0] * x + toPhoto[1] * z + toPhoto[2]) / w;
|
|
396
|
+
const v = (toPhoto[3] * x + toPhoto[4] * z + toPhoto[5]) / w;
|
|
397
|
+
return u >= 0 && u <= camera.width && v >= 0 && v <= camera.height;
|
|
398
|
+
};
|
|
399
|
+
const [cornerX, farX] = face.left ? [0, width] : [width, 0];
|
|
400
|
+
let reach = cornerX;
|
|
401
|
+
for (const z of [bottom, top, (bottom + top) / 2, ...rowHeights.map((h) => h - datum)]) {
|
|
402
|
+
if (!seen(cornerX, z))
|
|
403
|
+
continue;
|
|
404
|
+
let [shown, hidden] = [cornerX, farX];
|
|
405
|
+
if (seen(farX, z))
|
|
406
|
+
shown = farX;
|
|
407
|
+
else
|
|
408
|
+
for (let step = 0; step < 40; step++) {
|
|
409
|
+
const middle = (shown + hidden) / 2;
|
|
410
|
+
if (seen(middle, z))
|
|
411
|
+
shown = middle;
|
|
412
|
+
else
|
|
413
|
+
hidden = middle;
|
|
414
|
+
}
|
|
415
|
+
if (Math.abs(shown - cornerX) > Math.abs(reach - cornerX))
|
|
416
|
+
reach = shown;
|
|
417
|
+
}
|
|
418
|
+
;
|
|
419
|
+
[left, right] = face.left ? [0, reach] : [reach, width];
|
|
420
|
+
}
|
|
421
|
+
if (!(right - left > 0.5) || !(top - bottom > 0.5))
|
|
422
|
+
refuse('face_degenerate', `${name}: the face is too small to draw.`, { face: name });
|
|
423
|
+
const pxPerMetre = Math.min(MAX_PX_PER_METRE, MAX_SIDE / Math.max(right - left, top - bottom));
|
|
424
|
+
// Drawing pixel (u, v) is the face point (left + u / s, top − v / s).
|
|
425
|
+
const toFace = [1 / pxPerMetre, 0, left, 0, -1 / pxPerMetre, top, 0, 0, 1];
|
|
426
|
+
const residualPx = Math.sqrt(mean([
|
|
427
|
+
...[...rows.map((row) => row.points), ...face.lines].map((line) => vanishingResidualPx(camera, plane.along, [line]) ** 2),
|
|
428
|
+
...edges.map((edge) => vanishingResidualPx(camera, plane.up, [edge]) ** 2),
|
|
429
|
+
]));
|
|
430
|
+
const highest = rowHeights.at(-1) - datum;
|
|
431
|
+
const density = (x) => {
|
|
432
|
+
const low = applyHomography(toPhoto, [x, 0]);
|
|
433
|
+
const high = applyHomography(toPhoto, [x, highest]);
|
|
434
|
+
return low && high && highest > 0
|
|
435
|
+
? Math.hypot(high[0] - low[0], high[1] - low[1]) / highest
|
|
436
|
+
: 0;
|
|
437
|
+
};
|
|
438
|
+
const sourcePxPerMetre = { left: density(left), right: density(right) };
|
|
439
|
+
const scene = sceneStorey(elevations, face);
|
|
440
|
+
const storeyHeight = scene ? { photo: highest / scene.storeys, scene: scene.height } : undefined;
|
|
441
|
+
const warnings = [
|
|
442
|
+
...(storeyHeight &&
|
|
443
|
+
Math.abs(storeyHeight.photo - storeyHeight.scene) > STOREY_MISMATCH * storeyHeight.photo
|
|
444
|
+
? [
|
|
445
|
+
`The photo measures ${storeyHeight.photo.toFixed(2)} m floor to floor on this face, the scene's storeys ${storeyHeight.scene.toFixed(2)} m. The photo's figure comes from the picked lines: if a close-up is off or the lines are short, pick again before changing anything. This drawing takes its scale from the camera and the face's walls, not from the storeys: if those are the right walls, set the storeys to the photo's height before building the facade, or its units will not fit.`,
|
|
446
|
+
]
|
|
447
|
+
: []),
|
|
448
|
+
...commonWarnings(residualPx, sourcePxPerMetre),
|
|
449
|
+
...cornerWarnings[index],
|
|
450
|
+
];
|
|
451
|
+
return {
|
|
452
|
+
name,
|
|
453
|
+
widthMeters: width,
|
|
454
|
+
leftMeters: left,
|
|
455
|
+
rightMeters: right,
|
|
456
|
+
topMeters: top,
|
|
457
|
+
bottomMeters: bottom,
|
|
458
|
+
pxPerMetre,
|
|
459
|
+
outWidth: Math.round((right - left) * pxPerMetre),
|
|
460
|
+
outHeight: Math.round((top - bottom) * pxPerMetre),
|
|
461
|
+
toSource: multiplyHomography(toPhoto, toFace),
|
|
462
|
+
sourcePxPerMetre,
|
|
463
|
+
residualPx,
|
|
464
|
+
featureLines: featureLinesOf(nodes, elevations, face, { bottom, top, pxPerMetre }),
|
|
465
|
+
corners: cornersOf(face),
|
|
466
|
+
...(storeyHeight ? { storeyHeight } : {}),
|
|
467
|
+
warnings,
|
|
468
|
+
};
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Plans the straightening of each face of a photo, pixels being the host's. Several faces, or one
|
|
473
|
+
* whose far edge leaves the photo, share the photo's one camera, found from the vanishing points
|
|
474
|
+
* of the picked lines: each face is then K·[along up origin], metric from its walls' width, and
|
|
475
|
+
* the storeys are measured. One face with both edges, or faces no camera fits, are each fitted
|
|
476
|
+
* from their own rows × edges, their heights from the storeys.
|
|
477
|
+
*/
|
|
478
|
+
export function planPhotoElevation(nodes, input, image) {
|
|
479
|
+
const plan = planOnce(nodes, input, image);
|
|
480
|
+
const pose = plan.camera?.pose;
|
|
481
|
+
if (!pose)
|
|
482
|
+
return plan;
|
|
483
|
+
// How far the pose can be trusted: solved again with every pick moved by about the picks' own
|
|
484
|
+
// error, the same draws every time. A minimal set of picks fits exactly and shows no error, so
|
|
485
|
+
// an agent's pick is taken to be off by a few pixels at least.
|
|
486
|
+
const sigma = Math.max(PICK_ERROR * Math.max(image.width, image.height), mean(plan.faces.map((face) => face.residualPx)));
|
|
487
|
+
let state = 1;
|
|
488
|
+
const random = () => {
|
|
489
|
+
state = (state * 1103515245 + 12345) % 2147483648;
|
|
490
|
+
return state / 2147483648;
|
|
491
|
+
};
|
|
492
|
+
const move = ([x, y]) => [
|
|
493
|
+
Math.min(1, Math.max(0, x + ((random() * 2 - 1) * sigma) / image.width)),
|
|
494
|
+
Math.min(1, Math.max(0, y + ((random() * 2 - 1) * sigma) / image.height)),
|
|
495
|
+
];
|
|
496
|
+
const offsets = [];
|
|
497
|
+
for (let draw = 0; draw < SPREAD_DRAWS; draw++) {
|
|
498
|
+
const moved = {
|
|
499
|
+
...input,
|
|
500
|
+
faces: input.faces.map((face) => ({
|
|
501
|
+
...face,
|
|
502
|
+
edges: {
|
|
503
|
+
...(face.edges.left ? { left: face.edges.left.map(move) } : {}),
|
|
504
|
+
...(face.edges.right ? { right: face.edges.right.map(move) } : {}),
|
|
505
|
+
},
|
|
506
|
+
rows: face.rows.map((row) => ({ ...row, points: row.points.map(move) })),
|
|
507
|
+
...(face.lines ? { lines: face.lines.map((line) => line.map(move)) } : {}),
|
|
508
|
+
...(face.points
|
|
509
|
+
? { points: face.points.map((point) => ({ ...point, point: move(point.point) })) }
|
|
510
|
+
: {}),
|
|
511
|
+
})),
|
|
512
|
+
...(input.verticals ? { verticals: input.verticals.map((line) => line.map(move)) } : {}),
|
|
513
|
+
};
|
|
514
|
+
try {
|
|
515
|
+
const other = planOnce(nodes, moved, image).camera?.pose;
|
|
516
|
+
if (other)
|
|
517
|
+
offsets.push(Math.hypot(...[0, 1, 2].map((k) => other.position[k] - pose.position[k])));
|
|
518
|
+
}
|
|
519
|
+
catch {
|
|
520
|
+
// A draw the picks cannot hold gives no pose.
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
const spread = Math.round(Math.sqrt(mean(offsets.map((d) => d * d))) * 100) / 100;
|
|
524
|
+
const distance = Math.hypot(...[0, 1, 2].map((k) => pose.target[k] - pose.position[k]));
|
|
525
|
+
return {
|
|
526
|
+
...plan,
|
|
527
|
+
camera: {
|
|
528
|
+
...plan.camera,
|
|
529
|
+
pose: { ...pose, spread },
|
|
530
|
+
warnings: [
|
|
531
|
+
...plan.camera.warnings,
|
|
532
|
+
...(input.faces.every((face) => !face.points?.length && !face.lines?.length)
|
|
533
|
+
? [
|
|
534
|
+
"Only the face's ends and its rows were picked: they fit exactly and say nothing of their own error, so the spread assumes a pick is off by 0.5% of the photo. Place window and door corners (points) to measure it, and to steady the pose.",
|
|
535
|
+
]
|
|
536
|
+
: []),
|
|
537
|
+
...(spread > SHAKY * distance
|
|
538
|
+
? [
|
|
539
|
+
`The pose moves about ${spread} m when the picks move by their own error, ${Math.round((spread / distance) * 100)}% of the camera's distance: a render from it will not line up. Place more points on the face (points: window and door corners, eave ends), and pick them closer.`,
|
|
540
|
+
]
|
|
541
|
+
: []),
|
|
542
|
+
],
|
|
543
|
+
},
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* How far off an agent's pick is, at least, as a share of the photo's longer side: the measuring
|
|
548
|
+
* session's ±4 px on a 1600 px photo was under 0.2%, and the spread came out a third too small.
|
|
549
|
+
*/
|
|
550
|
+
const PICK_ERROR = 0.005;
|
|
551
|
+
/** Draws of the picks the pose's spread is measured on. */
|
|
552
|
+
const SPREAD_DRAWS = 24;
|
|
553
|
+
/** A pose moving more than this share of its distance is too unsure to render from. */
|
|
554
|
+
const SHAKY = 0.1;
|
|
555
|
+
function planOnce(nodes, input, image) {
|
|
556
|
+
const elevations = getLevelElevations(nodes);
|
|
557
|
+
const px = ([fx, fy]) => [fx * image.width, fy * image.height];
|
|
558
|
+
const faces = input.faces.map((face, index) => readFace(nodes, elevations, face, index, px));
|
|
559
|
+
const verticals = input.verticals ?? [];
|
|
560
|
+
if (verticals.flat().some((point) => !inside(point)))
|
|
561
|
+
refuse('point_outside_image', 'Every point of the verticals is a fraction of the image, 0 to 1 across and down.');
|
|
562
|
+
const oneEdge = faces.some((face) => !face.left || !face.right);
|
|
563
|
+
if (faces.length === 1 && !oneEdge) {
|
|
564
|
+
const face = faces[0];
|
|
565
|
+
const { plan, toPhoto, bottom, top } = planFromOwnPicks(nodes, elevations, face);
|
|
566
|
+
const seen = faceCamera(toPhoto, image, { width: face.width, height: top - bottom }, input.focal35mm);
|
|
567
|
+
return {
|
|
568
|
+
faces: [plan],
|
|
569
|
+
camera: {
|
|
570
|
+
...cameraSummary(seen.camera, seen.axes, 1),
|
|
571
|
+
...(seen.focalAssumed ? { assumptions: `${ASSUMPTIONS} ${FRONTAL_ASSUMPTION}` } : {}),
|
|
572
|
+
pose: photoPose(nodes, elevations, [face], seen.camera, [{ ...seen.anchor, bottom }], seen.focalAssumed),
|
|
573
|
+
},
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
const solved = solveCamera(faces, verticals.map((line) => segment(line, px)), image);
|
|
577
|
+
if ('code' in solved) {
|
|
578
|
+
if (oneEdge)
|
|
579
|
+
refuse(solved.code, solved.message);
|
|
580
|
+
const warning = `No camera fits this photo (${solved.message}) Each face is straightened from its own picks instead, its heights from the storeys.`;
|
|
581
|
+
return {
|
|
582
|
+
faces: faces.map((face) => {
|
|
583
|
+
const { plan } = planFromOwnPicks(nodes, elevations, face);
|
|
584
|
+
return { ...plan, warnings: [warning, ...plan.warnings] };
|
|
585
|
+
}),
|
|
586
|
+
camera: null,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
const anchors = [];
|
|
590
|
+
const plans = planWithCamera(nodes, elevations, faces, solved.camera, solved.faces, anchors);
|
|
591
|
+
return {
|
|
592
|
+
faces: plans,
|
|
593
|
+
camera: {
|
|
594
|
+
...cameraSummary(solved.camera, solved.faces[0], solved.focalSpread),
|
|
595
|
+
pose: photoPose(nodes, elevations, faces, solved.camera, anchors, false),
|
|
596
|
+
},
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/** A face's depth changing less than this share across it shows no focal length. */
|
|
600
|
+
const FRONTAL = 0.05;
|
|
601
|
+
/** A phone's main camera, on a 36 × 24 mm frame. */
|
|
602
|
+
const PHONE_35MM = 26;
|
|
603
|
+
const FRONTAL_ASSUMPTION = "One face seen square on does not show the focal length: it was taken (focal35mm, else a phone camera's 26 mm), and the camera's distance follows from it.";
|
|
604
|
+
/**
|
|
605
|
+
* The camera that sees a face through `toPhoto` (face metres from its left end and lowest row, to
|
|
606
|
+
* photo pixels), its principal point at the photo's centre: H = K·[r₁ r₂ t]. The focal length
|
|
607
|
+
* comes from r₁ ⟂ r₂ and |r₁| = |r₂| when the face recedes; square on, it is taken.
|
|
608
|
+
*/
|
|
609
|
+
function faceCamera(toPhoto, image, extent, focal35mm) {
|
|
610
|
+
const [cx, cy] = [image.width / 2, image.height / 2];
|
|
611
|
+
const column = (k) => {
|
|
612
|
+
const z = toPhoto[6 + k];
|
|
613
|
+
return [toPhoto[k] - cx * z, toPhoto[3 + k] - cy * z, z];
|
|
614
|
+
};
|
|
615
|
+
const [a, b, c] = [column(0), column(1), column(2)];
|
|
616
|
+
const recede = Math.max(Math.abs(a[2] * extent.width), Math.abs(b[2] * extent.height)) / Math.abs(c[2]);
|
|
617
|
+
const longer = Math.max(image.width, image.height);
|
|
618
|
+
// r₁ ⟂ r₂ and |r₁| = |r₂| each give f²; each is only as good as its denominator, which a face
|
|
619
|
+
// turned but not tilted (or tilted but not turned) drives to nought for the first.
|
|
620
|
+
const conditioned = [
|
|
621
|
+
{ squared: -(a[0] * b[0] + a[1] * b[1]) / (a[2] * b[2]), weight: Math.abs(a[2] * b[2]) },
|
|
622
|
+
{
|
|
623
|
+
squared: (a[0] ** 2 + a[1] ** 2 - b[0] ** 2 - b[1] ** 2) / (b[2] ** 2 - a[2] ** 2),
|
|
624
|
+
weight: Math.abs(b[2] ** 2 - a[2] ** 2),
|
|
625
|
+
},
|
|
626
|
+
]
|
|
627
|
+
.filter(({ squared }) => Number.isFinite(squared) && squared > 0)
|
|
628
|
+
.map(({ squared, weight }) => ({ focal: Math.sqrt(squared), weight }))
|
|
629
|
+
.filter(({ focal }) => focal >= 0.25 * longer && focal <= 10 * longer)
|
|
630
|
+
.sort((p, q) => q.weight - p.weight);
|
|
631
|
+
const read = focal35mm === undefined && recede > FRONTAL ? conditioned[0]?.focal : undefined;
|
|
632
|
+
const focalAssumed = read === undefined;
|
|
633
|
+
const focal = read ?? ((focal35mm ?? PHONE_35MM) * Math.hypot(image.width, image.height)) / Math.hypot(36, 24);
|
|
634
|
+
const ray = (h) => [h[0] / focal, h[1] / focal, h[2]];
|
|
635
|
+
const [g1, g2, g3] = [ray(a), ray(b), ray(c)];
|
|
636
|
+
const norm = (v) => Math.hypot(...v);
|
|
637
|
+
const scale = ((g3[2] > 0 ? 1 : -1) * 2) / (norm(g1) + norm(g2));
|
|
638
|
+
const along = v3.scale(g1, scale / norm(v3.scale(g1, scale)));
|
|
639
|
+
const upRaw = v3.scale(g2, scale);
|
|
640
|
+
const up = v3.scale(v3.add(upRaw, v3.scale(along, -v3.dot(upRaw, along))), 1 / norm(v3.add(upRaw, v3.scale(along, -v3.dot(upRaw, along)))));
|
|
641
|
+
const normal = [
|
|
642
|
+
along[1] * up[2] - along[2] * up[1],
|
|
643
|
+
along[2] * up[0] - along[0] * up[2],
|
|
644
|
+
along[0] * up[1] - along[1] * up[0],
|
|
645
|
+
];
|
|
646
|
+
const t = v3.scale(g3, scale);
|
|
647
|
+
return {
|
|
648
|
+
camera: { ...image, focal, centre: [cx, cy], up },
|
|
649
|
+
axes: { along, up, normal },
|
|
650
|
+
anchor: {
|
|
651
|
+
plane: { along, up, normal, distance: v3.dot(normal, t) },
|
|
652
|
+
origin: [v3.dot(along, t), v3.dot(up, t)],
|
|
653
|
+
},
|
|
654
|
+
focalAssumed,
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
const v3 = {
|
|
658
|
+
add: (a, b) => [a[0] + b[0], a[1] + b[1], a[2] + b[2]],
|
|
659
|
+
scale: (a, k) => [a[0] * k, a[1] * k, a[2] * k],
|
|
660
|
+
dot: (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
|
|
661
|
+
};
|
|
662
|
+
/**
|
|
663
|
+
* The camera in world coordinates. Each face's axes are known in the camera (along, up, out) and
|
|
664
|
+
* in the world (its walls' direction, up, out), and so is one of its points: the walls' left end
|
|
665
|
+
* at the rows' datum. The side the camera stands on is the one every face agrees on; one face
|
|
666
|
+
* alone leaves it open. The datum's height is the rows' feature height above their floor, or else
|
|
667
|
+
* the edges' foot is taken for the ground.
|
|
668
|
+
*/
|
|
669
|
+
function photoPose(nodes, elevations, faces, camera, anchors, focalAssumed) {
|
|
670
|
+
if (!faces.length)
|
|
671
|
+
return null;
|
|
672
|
+
const ground = Math.min(...[...elevations.values()].map((level) => level.baseY));
|
|
673
|
+
const rowHeight = faces[0].rows[0].height;
|
|
674
|
+
const anchoredBy = rowHeight !== undefined ? 'row height' : 'edge base';
|
|
675
|
+
// Each face's camera and world axes for one side of it (sign ±1 along its walls).
|
|
676
|
+
const place = (index, sign) => {
|
|
677
|
+
const face = faces[index];
|
|
678
|
+
const { plane, origin, bottom } = anchors[index];
|
|
679
|
+
const along = [sign * face.axis[0], 0, sign * face.axis[1]];
|
|
680
|
+
const up = [0, 1, 0];
|
|
681
|
+
const normal = [-along[2], 0, along[0]];
|
|
682
|
+
const left = sign > 0 ? face.ends[0] : face.ends[1];
|
|
683
|
+
const row = face.rows[0];
|
|
684
|
+
const datumY = row.height !== undefined ? row.baseY + row.height : ground - bottom;
|
|
685
|
+
const world = [left[0], datumY, left[1]];
|
|
686
|
+
const position = v3.add(world, v3.add(v3.add(v3.scale(along, -origin[0]), v3.scale(up, -origin[1])), v3.scale(normal, -plane.distance)));
|
|
687
|
+
// Camera axes (x right, y down, z forward) in the world.
|
|
688
|
+
const axis = (k) => v3.add(v3.add(v3.scale(along, plane.along[k]), v3.scale(up, plane.up[k])), v3.scale(normal, plane.normal[k]));
|
|
689
|
+
return { position, right: axis(0), down: axis(1), forward: axis(2) };
|
|
690
|
+
};
|
|
691
|
+
let best = null;
|
|
692
|
+
if (faces.length === 1) {
|
|
693
|
+
// One face: the camera stands outside the building, away from its walls' middle.
|
|
694
|
+
const walls = Object.values(nodes).filter((node) => node.type === 'wall' && node.parentId === faces[0].levelId);
|
|
695
|
+
const middle = walls.flatMap((wall) => [wall.start, wall.end]);
|
|
696
|
+
const cx = mean(middle.map((p) => p[0]));
|
|
697
|
+
const cz = mean(middle.map((p) => p[1]));
|
|
698
|
+
const away = (p) => Math.hypot(p.position[0] - cx, p.position[2] - cz);
|
|
699
|
+
const [a, b] = [place(0, 1), place(0, -1)];
|
|
700
|
+
best = { spread: 0, places: [away(a) >= away(b) ? a : b] };
|
|
701
|
+
}
|
|
702
|
+
else
|
|
703
|
+
for (const first of [1, -1]) {
|
|
704
|
+
const head = place(0, first);
|
|
705
|
+
const rest = faces.slice(1).map((_, i) => {
|
|
706
|
+
const [a, b] = [place(i + 1, 1), place(i + 1, -1)];
|
|
707
|
+
const gap = (p) => Math.hypot(...[0, 1, 2].map((k) => p.position[k] - head.position[k]));
|
|
708
|
+
return gap(a) <= gap(b) ? a : b;
|
|
709
|
+
});
|
|
710
|
+
const places = [head, ...rest];
|
|
711
|
+
const spread = Math.max(...rest.map((p) => Math.hypot(...[0, 1, 2].map((k) => p.position[k] - head.position[k]))));
|
|
712
|
+
if (!best || spread < best.spread)
|
|
713
|
+
best = { spread, places };
|
|
714
|
+
}
|
|
715
|
+
const chosen = best.places[0];
|
|
716
|
+
const depth = v3.dot(v3.add(v3.add(v3.scale(anchors[0].plane.along, anchors[0].origin[0]), v3.scale(anchors[0].plane.up, anchors[0].origin[1])), v3.scale(anchors[0].plane.normal, anchors[0].plane.distance)), [0, 0, 1]);
|
|
717
|
+
const project = (point) => {
|
|
718
|
+
const d = [
|
|
719
|
+
point[0] - chosen.position[0],
|
|
720
|
+
point[1] - chosen.position[1],
|
|
721
|
+
point[2] - chosen.position[2],
|
|
722
|
+
];
|
|
723
|
+
const z = v3.dot(d, chosen.forward);
|
|
724
|
+
return [
|
|
725
|
+
(camera.focal * v3.dot(d, chosen.right)) / z + camera.centre[0],
|
|
726
|
+
(camera.focal * v3.dot(d, chosen.down)) / z + camera.centre[1],
|
|
727
|
+
];
|
|
728
|
+
};
|
|
729
|
+
// Each picked edge against the vertical through its walls' end, projected.
|
|
730
|
+
const residuals = faces.flatMap((face, index) => {
|
|
731
|
+
const sign = best.places[index].right;
|
|
732
|
+
const leftFirst = v3.dot(sign, [face.axis[0], 0, face.axis[1]]) > 0;
|
|
733
|
+
const [leftEnd, rightEnd] = leftFirst ? face.ends : [face.ends[1], face.ends[0]];
|
|
734
|
+
return [
|
|
735
|
+
[face.left, leftEnd],
|
|
736
|
+
[face.right, rightEnd],
|
|
737
|
+
].flatMap(([edge, end]) => {
|
|
738
|
+
if (!edge)
|
|
739
|
+
return [];
|
|
740
|
+
const line = [
|
|
741
|
+
project([end[0], ground, end[1]]),
|
|
742
|
+
project([end[0], ground + 10, end[1]]),
|
|
743
|
+
];
|
|
744
|
+
return [offLine(edge, line)];
|
|
745
|
+
});
|
|
746
|
+
});
|
|
747
|
+
const round = (value) => Math.round(value * 1000) / 1000;
|
|
748
|
+
const r3 = (v) => [round(v[0]), round(v[1]), round(v[2])];
|
|
749
|
+
return {
|
|
750
|
+
projection: 'perspective',
|
|
751
|
+
position: r3(chosen.position),
|
|
752
|
+
target: r3(v3.add(chosen.position, v3.scale(chosen.forward, depth))),
|
|
753
|
+
up: r3(v3.scale(chosen.down, -1)),
|
|
754
|
+
fov: round((2 * Math.atan(camera.height / 2 / camera.focal) * 180) / Math.PI),
|
|
755
|
+
aspect: round(camera.width / camera.height),
|
|
756
|
+
shift: round((camera.centre[1] - camera.height / 2) / camera.height),
|
|
757
|
+
anchoredBy,
|
|
758
|
+
focalAssumed,
|
|
759
|
+
edgeResidualPx: round(Math.sqrt(mean(residuals.map((r) => r * r)))),
|
|
760
|
+
};
|
|
761
|
+
}
|