@ifc-lite/viewer 1.17.3 → 1.17.6
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/.turbo/turbo-build.log +39 -30
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +132 -0
- package/DESKTOP_CONTRACT_VERSION +1 -0
- package/dist/assets/arrow-CZ5kQ26f.js +20 -0
- package/dist/assets/basketViewActivator-86rgogji.js +1 -0
- package/dist/assets/{bcf-D5-QWGO9.js → bcf-DOG9_WPX.js} +1 -1
- package/dist/assets/{browser-CKs-FY1P.js → browser-C5TFR7sH.js} +1 -1
- package/dist/assets/cesium-ADbP7waU.css +1 -0
- package/dist/assets/cesium-DUOzBlqv.js +17817 -0
- package/dist/assets/drawing-2d-DoxKMqbO.js +257 -0
- package/dist/assets/{exporters-C_6J153K.js → exporters-CcPS9MK5.js} +2898 -2380
- package/dist/assets/geometry.worker-BFUYA08u.js +1 -0
- package/dist/assets/ids-DQ5jY0E8.js +1 -0
- package/dist/assets/ifc-lite_bg-BINvzoCP.wasm +0 -0
- package/dist/assets/{index-jhBr1wbn.js → index-Bfms9I4A.js} +41036 -34613
- package/dist/assets/index-_bfZsDCC.css +1 -0
- package/dist/assets/{maplibre-gl-BpvwNKKy.js → maplibre-gl-CGLcoNXc.js} +1 -1
- package/dist/assets/native-bridge-DUyLCMZS.js +429 -0
- package/dist/assets/{sandbox-B79eavQ3.js → sandbox-C8575tul.js} +4342 -4324
- package/dist/assets/{server-client-D3bUPJJc.js → server-client-BuZK7OST.js} +4 -4
- package/dist/assets/tauri-core-stub-D8Fa-u43.js +1 -0
- package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +1 -0
- package/dist/assets/tauri-fs-stub-BdeRC7aK.js +1 -0
- package/dist/assets/wasm-bridge-JsqEGDV8.js +1 -0
- package/dist/assets/{zip-B-jFFAGa.js → zip-DBEtpeu6.js} +3 -3
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json +1 -0
- package/dist/cesium/Assets/Images/bing_maps_credit.png +0 -0
- package/dist/cesium/Assets/Images/cesium_credit.png +0 -0
- package/dist/cesium/Assets/Images/google_earth_credit.png +0 -0
- package/dist/cesium/Assets/Images/ion-credit.png +0 -0
- package/dist/cesium/Assets/Textures/LensFlare/DirtMask.jpg +0 -0
- package/dist/cesium/Assets/Textures/LensFlare/StarBurst.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/0/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/0/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/tilemapresource.xml +14 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mx.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_my.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mz.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_px.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_py.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_pz.jpg +0 -0
- package/dist/cesium/Assets/Textures/maki/airfield.png +0 -0
- package/dist/cesium/Assets/Textures/maki/airport.png +0 -0
- package/dist/cesium/Assets/Textures/maki/alcohol-shop.png +0 -0
- package/dist/cesium/Assets/Textures/maki/america-football.png +0 -0
- package/dist/cesium/Assets/Textures/maki/art-gallery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bakery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bank.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bar.png +0 -0
- package/dist/cesium/Assets/Textures/maki/baseball.png +0 -0
- package/dist/cesium/Assets/Textures/maki/basketball.png +0 -0
- package/dist/cesium/Assets/Textures/maki/beer.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bicycle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/building.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bus.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cafe.png +0 -0
- package/dist/cesium/Assets/Textures/maki/camera.png +0 -0
- package/dist/cesium/Assets/Textures/maki/campsite.png +0 -0
- package/dist/cesium/Assets/Textures/maki/car.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cemetery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cesium.png +0 -0
- package/dist/cesium/Assets/Textures/maki/chemist.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cinema.png +0 -0
- package/dist/cesium/Assets/Textures/maki/circle-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/circle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/city.png +0 -0
- package/dist/cesium/Assets/Textures/maki/clothing-store.png +0 -0
- package/dist/cesium/Assets/Textures/maki/college.png +0 -0
- package/dist/cesium/Assets/Textures/maki/commercial.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cricket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cross.png +0 -0
- package/dist/cesium/Assets/Textures/maki/dam.png +0 -0
- package/dist/cesium/Assets/Textures/maki/danger.png +0 -0
- package/dist/cesium/Assets/Textures/maki/disability.png +0 -0
- package/dist/cesium/Assets/Textures/maki/dog-park.png +0 -0
- package/dist/cesium/Assets/Textures/maki/embassy.png +0 -0
- package/dist/cesium/Assets/Textures/maki/emergency-telephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/entrance.png +0 -0
- package/dist/cesium/Assets/Textures/maki/farm.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fast-food.png +0 -0
- package/dist/cesium/Assets/Textures/maki/ferry.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fire-station.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fuel.png +0 -0
- package/dist/cesium/Assets/Textures/maki/garden.png +0 -0
- package/dist/cesium/Assets/Textures/maki/gift.png +0 -0
- package/dist/cesium/Assets/Textures/maki/golf.png +0 -0
- package/dist/cesium/Assets/Textures/maki/grocery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/hairdresser.png +0 -0
- package/dist/cesium/Assets/Textures/maki/harbor.png +0 -0
- package/dist/cesium/Assets/Textures/maki/heart.png +0 -0
- package/dist/cesium/Assets/Textures/maki/heliport.png +0 -0
- package/dist/cesium/Assets/Textures/maki/hospital.png +0 -0
- package/dist/cesium/Assets/Textures/maki/ice-cream.png +0 -0
- package/dist/cesium/Assets/Textures/maki/industrial.png +0 -0
- package/dist/cesium/Assets/Textures/maki/land-use.png +0 -0
- package/dist/cesium/Assets/Textures/maki/laundry.png +0 -0
- package/dist/cesium/Assets/Textures/maki/library.png +0 -0
- package/dist/cesium/Assets/Textures/maki/lighthouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/lodging.png +0 -0
- package/dist/cesium/Assets/Textures/maki/logging.png +0 -0
- package/dist/cesium/Assets/Textures/maki/london-underground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/marker-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/marker.png +0 -0
- package/dist/cesium/Assets/Textures/maki/minefield.png +0 -0
- package/dist/cesium/Assets/Textures/maki/mobilephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/monument.png +0 -0
- package/dist/cesium/Assets/Textures/maki/museum.png +0 -0
- package/dist/cesium/Assets/Textures/maki/music.png +0 -0
- package/dist/cesium/Assets/Textures/maki/oil-well.png +0 -0
- package/dist/cesium/Assets/Textures/maki/park.png +0 -0
- package/dist/cesium/Assets/Textures/maki/park2.png +0 -0
- package/dist/cesium/Assets/Textures/maki/parking-garage.png +0 -0
- package/dist/cesium/Assets/Textures/maki/parking.png +0 -0
- package/dist/cesium/Assets/Textures/maki/pharmacy.png +0 -0
- package/dist/cesium/Assets/Textures/maki/pitch.png +0 -0
- package/dist/cesium/Assets/Textures/maki/place-of-worship.png +0 -0
- package/dist/cesium/Assets/Textures/maki/playground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/police.png +0 -0
- package/dist/cesium/Assets/Textures/maki/polling-place.png +0 -0
- package/dist/cesium/Assets/Textures/maki/post.png +0 -0
- package/dist/cesium/Assets/Textures/maki/prison.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-above.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-light.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-metro.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-underground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-christian.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-jewish.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-muslim.png +0 -0
- package/dist/cesium/Assets/Textures/maki/restaurant.png +0 -0
- package/dist/cesium/Assets/Textures/maki/roadblock.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rocket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/school.png +0 -0
- package/dist/cesium/Assets/Textures/maki/scooter.png +0 -0
- package/dist/cesium/Assets/Textures/maki/shop.png +0 -0
- package/dist/cesium/Assets/Textures/maki/skiing.png +0 -0
- package/dist/cesium/Assets/Textures/maki/slaughterhouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/soccer.png +0 -0
- package/dist/cesium/Assets/Textures/maki/square-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/square.png +0 -0
- package/dist/cesium/Assets/Textures/maki/star-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/star.png +0 -0
- package/dist/cesium/Assets/Textures/maki/suitcase.png +0 -0
- package/dist/cesium/Assets/Textures/maki/swimming.png +0 -0
- package/dist/cesium/Assets/Textures/maki/telephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/tennis.png +0 -0
- package/dist/cesium/Assets/Textures/maki/theatre.png +0 -0
- package/dist/cesium/Assets/Textures/maki/toilets.png +0 -0
- package/dist/cesium/Assets/Textures/maki/town-hall.png +0 -0
- package/dist/cesium/Assets/Textures/maki/town.png +0 -0
- package/dist/cesium/Assets/Textures/maki/triangle-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/triangle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/village.png +0 -0
- package/dist/cesium/Assets/Textures/maki/warehouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/waste-basket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/water.png +0 -0
- package/dist/cesium/Assets/Textures/maki/wetland.png +0 -0
- package/dist/cesium/Assets/Textures/maki/zoo.png +0 -0
- package/dist/cesium/Assets/Textures/moonSmall.jpg +0 -0
- package/dist/cesium/Assets/Textures/pin.svg +1 -0
- package/dist/cesium/Assets/Textures/waterNormals.jpg +0 -0
- package/dist/cesium/Assets/Textures/waterNormalsSmall.jpg +0 -0
- package/dist/cesium/Assets/approximateTerrainHeights.json +1 -0
- package/dist/cesium/ThirdParty/Workers/package.json +1 -0
- package/dist/cesium/ThirdParty/Workers/zip-web-worker.js +1 -0
- package/dist/cesium/ThirdParty/basis_transcoder.wasm +0 -0
- package/dist/cesium/ThirdParty/draco_decoder.wasm +0 -0
- package/dist/cesium/ThirdParty/google-earth-dbroot-parser.js +1 -0
- package/dist/cesium/ThirdParty/wasm_splats_bg.wasm +0 -0
- package/dist/cesium/ThirdParty/zip-module.wasm +0 -0
- package/dist/cesium/Widgets/Animation/Animation.css +127 -0
- package/dist/cesium/Widgets/Animation/lighter.css +70 -0
- package/dist/cesium/Widgets/BaseLayerPicker/BaseLayerPicker.css +108 -0
- package/dist/cesium/Widgets/BaseLayerPicker/lighter.css +22 -0
- package/dist/cesium/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css +102 -0
- package/dist/cesium/Widgets/CesiumInspector/CesiumInspector.css +113 -0
- package/dist/cesium/Widgets/CesiumWidget/CesiumWidget.css +119 -0
- package/dist/cesium/Widgets/CesiumWidget/lighter.css +14 -0
- package/dist/cesium/Widgets/FullscreenButton/FullscreenButton.css +8 -0
- package/dist/cesium/Widgets/Geocoder/Geocoder.css +70 -0
- package/dist/cesium/Widgets/Geocoder/lighter.css +17 -0
- package/dist/cesium/Widgets/I3SBuildingSceneLayerExplorer/I3SBuildingSceneLayerExplorer.css +27 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldHillshade.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldImagery.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldOcean.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/azureAerial.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/azureRoads.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingAerial.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingAerialLabels.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingRoads.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/blueMarble.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/earthAtNight.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleContour.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleRoadmap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleSatellite.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleSatelliteLabels.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapQuestOpenStreetMap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxSatellite.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxStreets.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxTerrain.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/naturalEarthII.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/openStreetMap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/sentinel-2.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmooth.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmoothDark.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stamenToner.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stamenWatercolor.png +0 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/Mouse.svg +84 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseLeft.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseMiddle.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseRight.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/Touch.svg +120 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchDrag.svg +129 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchRotate.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchTilt.svg +135 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchZoom.svg +74 -0
- package/dist/cesium/Widgets/Images/TerrainProviders/CesiumWorldTerrain.png +0 -0
- package/dist/cesium/Widgets/Images/TerrainProviders/Ellipsoid.png +0 -0
- package/dist/cesium/Widgets/Images/TimelineIcons.png +0 -0
- package/dist/cesium/Widgets/Images/info-loading.gif +0 -0
- package/dist/cesium/Widgets/InfoBox/InfoBox.css +92 -0
- package/dist/cesium/Widgets/InfoBox/InfoBoxDescription.css +178 -0
- package/dist/cesium/Widgets/NavigationHelpButton/NavigationHelpButton.css +93 -0
- package/dist/cesium/Widgets/NavigationHelpButton/lighter.css +38 -0
- package/dist/cesium/Widgets/PerformanceWatchdog/PerformanceWatchdog.css +15 -0
- package/dist/cesium/Widgets/ProjectionPicker/ProjectionPicker.css +38 -0
- package/dist/cesium/Widgets/SceneModePicker/SceneModePicker.css +56 -0
- package/dist/cesium/Widgets/SelectionIndicator/SelectionIndicator.css +20 -0
- package/dist/cesium/Widgets/Timeline/Timeline.css +103 -0
- package/dist/cesium/Widgets/Timeline/lighter.css +23 -0
- package/dist/cesium/Widgets/VRButton/VRButton.css +8 -0
- package/dist/cesium/Widgets/Viewer/Viewer.css +107 -0
- package/dist/cesium/Widgets/VoxelInspector/VoxelInspector.css +16 -0
- package/dist/cesium/Widgets/lighter.css +237 -0
- package/dist/cesium/Widgets/lighterShared.css +46 -0
- package/dist/cesium/Widgets/shared.css +103 -0
- package/dist/cesium/Widgets/widgets.css +1342 -0
- package/dist/cesium/Workers/chunk-23ZQ2IVV.js +29 -0
- package/dist/cesium/Workers/chunk-2EQO3Q56.js +26 -0
- package/dist/cesium/Workers/chunk-2MJIIVP4.js +26 -0
- package/dist/cesium/Workers/chunk-2TE5NTVD.js +26 -0
- package/dist/cesium/Workers/chunk-2ZBHLJST.js +26 -0
- package/dist/cesium/Workers/chunk-5TJMAQVL.js +26 -0
- package/dist/cesium/Workers/chunk-6BD4U3VO.js +26 -0
- package/dist/cesium/Workers/chunk-7TVGLKQF.js +26 -0
- package/dist/cesium/Workers/chunk-BTSYJ5XU.js +26 -0
- package/dist/cesium/Workers/chunk-BXMEEOCS.js +63 -0
- package/dist/cesium/Workers/chunk-BYLCY7GP.js +29 -0
- package/dist/cesium/Workers/chunk-CTHM3W6I.js +26 -0
- package/dist/cesium/Workers/chunk-CUUSNIVQ.js +26 -0
- package/dist/cesium/Workers/chunk-E3JOOS3S.js +26 -0
- package/dist/cesium/Workers/chunk-E7KYDCM5.js +26 -0
- package/dist/cesium/Workers/chunk-EDVBB7SS.js +27 -0
- package/dist/cesium/Workers/chunk-EFBN7QNX.js +26 -0
- package/dist/cesium/Workers/chunk-EQ4YRVWL.js +26 -0
- package/dist/cesium/Workers/chunk-F6PRE7D6.js +26 -0
- package/dist/cesium/Workers/chunk-FC4ZZ65J.js +26 -0
- package/dist/cesium/Workers/chunk-FFBVWF2L.js +26 -0
- package/dist/cesium/Workers/chunk-GBAA6GVX.js +26 -0
- package/dist/cesium/Workers/chunk-ICALLYLG.js +26 -0
- package/dist/cesium/Workers/chunk-ILRYTWTP.js +26 -0
- package/dist/cesium/Workers/chunk-IRNLBSEJ.js +26 -0
- package/dist/cesium/Workers/chunk-IX4VMHEV.js +26 -0
- package/dist/cesium/Workers/chunk-L6QHHACZ.js +26 -0
- package/dist/cesium/Workers/chunk-LI2ZSORM.js +26 -0
- package/dist/cesium/Workers/chunk-LSLE2RL4.js +26 -0
- package/dist/cesium/Workers/chunk-M4HLDBCG.js +26 -0
- package/dist/cesium/Workers/chunk-MJHHSGEH.js +26 -0
- package/dist/cesium/Workers/chunk-NMVKML6W.js +26 -0
- package/dist/cesium/Workers/chunk-OCWJRAXS.js +26 -0
- package/dist/cesium/Workers/chunk-OIRKANTH.js +26 -0
- package/dist/cesium/Workers/chunk-OIT7J4IC.js +26 -0
- package/dist/cesium/Workers/chunk-OLZ3FYUM.js +26 -0
- package/dist/cesium/Workers/chunk-Q5BPHJQF.js +26 -0
- package/dist/cesium/Workers/chunk-QFM5DCMQ.js +26 -0
- package/dist/cesium/Workers/chunk-QKUIYMGC.js +28 -0
- package/dist/cesium/Workers/chunk-S44JILQT.js +26 -0
- package/dist/cesium/Workers/chunk-SLT4J352.js +26 -0
- package/dist/cesium/Workers/chunk-SQMIIXB7.js +26 -0
- package/dist/cesium/Workers/chunk-TJ4XLGBQ.js +26 -0
- package/dist/cesium/Workers/chunk-TNSUQXWK.js +27 -0
- package/dist/cesium/Workers/chunk-UBOGZS7F.js +26 -0
- package/dist/cesium/Workers/chunk-V3OSTMM6.js +26 -0
- package/dist/cesium/Workers/chunk-V7QEYVP3.js +26 -0
- package/dist/cesium/Workers/chunk-VUKYSU4H.js +26 -0
- package/dist/cesium/Workers/chunk-W37FE5GR.js +26 -0
- package/dist/cesium/Workers/chunk-WBOV35NL.js +26 -0
- package/dist/cesium/Workers/chunk-WPMZLB3Y.js +26 -0
- package/dist/cesium/Workers/chunk-WWWZVEEH.js +26 -0
- package/dist/cesium/Workers/chunk-XFIQ5DEQ.js +28 -0
- package/dist/cesium/Workers/chunk-XQHLGIO7.js +26 -0
- package/dist/cesium/Workers/chunk-XUSCFAVF.js +26 -0
- package/dist/cesium/Workers/chunk-YP7I5QBZ.js +26 -0
- package/dist/cesium/Workers/chunk-Z3QF2EHT.js +26 -0
- package/dist/cesium/Workers/combineGeometry.js +26 -0
- package/dist/cesium/Workers/createBoxGeometry.js +26 -0
- package/dist/cesium/Workers/createBoxOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCircleGeometry.js +26 -0
- package/dist/cesium/Workers/createCircleOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCoplanarPolygonGeometry.js +26 -0
- package/dist/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCorridorGeometry.js +26 -0
- package/dist/cesium/Workers/createCorridorOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCylinderGeometry.js +26 -0
- package/dist/cesium/Workers/createCylinderOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipseGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipseOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipsoidGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipsoidOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createFrustumGeometry.js +26 -0
- package/dist/cesium/Workers/createFrustumOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createGeometry.js +26 -0
- package/dist/cesium/Workers/createGroundPolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createPlaneGeometry.js +26 -0
- package/dist/cesium/Workers/createPlaneOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolygonGeometry.js +26 -0
- package/dist/cesium/Workers/createPolygonOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineVolumeGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineVolumeOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createRectangleGeometry.js +26 -0
- package/dist/cesium/Workers/createRectangleOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createSimplePolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createSphereGeometry.js +26 -0
- package/dist/cesium/Workers/createSphereOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createTaskProcessorWorker.js +26 -0
- package/dist/cesium/Workers/createVectorTileClampedPolylines.js +26 -0
- package/dist/cesium/Workers/createVectorTileGeometries.js +26 -0
- package/dist/cesium/Workers/createVectorTilePoints.js +26 -0
- package/dist/cesium/Workers/createVectorTilePolygons.js +26 -0
- package/dist/cesium/Workers/createVectorTilePolylines.js +26 -0
- package/dist/cesium/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -0
- package/dist/cesium/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +26 -0
- package/dist/cesium/Workers/createVerticesFromHeightmap.js +26 -0
- package/dist/cesium/Workers/createVerticesFromQuantizedTerrainMesh.js +26 -0
- package/dist/cesium/Workers/createWallGeometry.js +26 -0
- package/dist/cesium/Workers/createWallOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/decodeDraco.js +26 -0
- package/dist/cesium/Workers/decodeGoogleEarthEnterprisePacket.js +26 -0
- package/dist/cesium/Workers/decodeI3S.js +26 -0
- package/dist/cesium/Workers/gaussianSplatSorter.js +26 -0
- package/dist/cesium/Workers/gaussianSplatTextureGenerator.js +26 -0
- package/dist/cesium/Workers/incrementallyBuildTerrainPicker.js +26 -0
- package/dist/cesium/Workers/transcodeKTX2.js +56 -0
- package/dist/cesium/Workers/transferTypedArrayTest.js +26 -0
- package/dist/cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
- package/dist/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -0
- package/dist/index.html +13 -10
- package/index.html +1 -0
- package/package.json +18 -15
- package/src/App.tsx +7 -9
- package/src/components/viewer/BCFPanel.tsx +46 -4
- package/src/components/viewer/CesiumOverlay.tsx +715 -0
- package/src/components/viewer/CesiumSettingsDialog.tsx +100 -0
- package/src/components/viewer/ChatPanel.tsx +232 -90
- package/src/components/viewer/CommandPalette.tsx +6 -1
- package/src/components/viewer/DesktopEntitlementBanner.tsx +74 -0
- package/src/components/viewer/ExportChangesButton.tsx +6 -1
- package/src/components/viewer/ExportDialog.tsx +22 -6
- package/src/components/viewer/HierarchyPanel.tsx +196 -0
- package/src/components/viewer/IDSPanel.tsx +52 -3
- package/src/components/viewer/KeyboardShortcutsDialog.tsx +1 -1
- package/src/components/viewer/MainToolbar.tsx +355 -28
- package/src/components/viewer/PropertiesPanel.tsx +234 -81
- package/src/components/viewer/ScriptPanel.tsx +34 -8
- package/src/components/viewer/SettingsPage.tsx +581 -0
- package/src/components/viewer/StatusBar.tsx +17 -1
- package/src/components/viewer/ThemeSwitch.tsx +63 -7
- package/src/components/viewer/ViewerLayout.tsx +48 -6
- package/src/components/viewer/Viewport.tsx +61 -8
- package/src/components/viewer/ViewportContainer.tsx +265 -28
- package/src/components/viewer/ViewportOverlays.tsx +132 -27
- package/src/components/viewer/bcf/BCFTopicDetail.tsx +4 -4
- package/src/components/viewer/chat/ModelSelector.tsx +90 -54
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +229 -55
- package/src/components/viewer/properties/LocationMap.tsx +462 -19
- package/src/components/viewer/properties/ModelMetadataPanel.tsx +1 -1
- package/src/components/viewer/selectionHandlers.ts +4 -3
- package/src/components/viewer/tools/SectionCapControls.tsx +237 -0
- package/src/components/viewer/tools/SectionPanel.tsx +39 -18
- package/src/components/viewer/useAnimationLoop.ts +13 -1
- package/src/components/viewer/useGeometryStreaming.ts +127 -40
- package/src/components/viewer/useMouseControls.ts +4 -1
- package/src/components/viewer/useRenderUpdates.ts +1 -1
- package/src/hooks/ids/idsDataAccessor.ts +60 -24
- package/src/hooks/ingest/viewerModelIngest.ts +280 -0
- package/src/hooks/useIDS.ts +1 -1
- package/src/hooks/useIfc.ts +7 -1
- package/src/hooks/useIfcCache.ts +28 -15
- package/src/hooks/useIfcFederation.ts +378 -291
- package/src/hooks/useIfcLoader.ts +1657 -130
- package/src/hooks/useIfcServer.ts +0 -69
- package/src/hooks/useViewControls.ts +13 -5
- package/src/index.css +484 -10
- package/src/lib/desktop/desktopEntitlementEvents.ts +39 -0
- package/src/lib/desktop-entitlement.ts +43 -0
- package/src/lib/desktop-product.ts +124 -0
- package/src/lib/geo/cesium-bridge.ts +318 -0
- package/src/lib/geo/effective-georef.test.ts +73 -0
- package/src/lib/geo/effective-georef.ts +111 -0
- package/src/lib/geo/reproject.ts +249 -37
- package/src/lib/llm/byok-guard.test.ts +77 -0
- package/src/lib/llm/byok-guard.ts +39 -0
- package/src/lib/llm/free-models.test.ts +0 -6
- package/src/lib/llm/models.ts +104 -42
- package/src/lib/llm/stream-client.ts +74 -110
- package/src/lib/llm/stream-direct.test.ts +130 -0
- package/src/lib/llm/stream-direct.ts +316 -0
- package/src/lib/llm/types.ts +14 -2
- package/src/lib/recent-files.ts +2 -1
- package/src/main.tsx +1 -10
- package/src/services/analysis-extensions.ts +125 -0
- package/src/services/api-keys.ts +73 -0
- package/src/services/app-navigation.ts +13 -0
- package/src/services/bsdd.ts +53 -4
- package/src/services/cacheService.ts +1 -1
- package/src/services/desktop-cache.ts +43 -0
- package/src/services/desktop-export.ts +77 -0
- package/src/services/desktop-harness.ts +196 -0
- package/src/services/desktop-logger.ts +20 -0
- package/src/services/desktop-native-metadata.ts +207 -0
- package/src/services/desktop-panel-actions.ts +43 -0
- package/src/services/desktop-preferences.ts +44 -0
- package/src/services/file-dialog.ts +147 -0
- package/src/services/tauri-core-stub.ts +7 -0
- package/src/services/tauri-dialog-stub.ts +7 -0
- package/src/services/tauri-fs-stub.ts +7 -0
- package/src/store/constants.ts +20 -2
- package/src/store/index.ts +52 -7
- package/src/store/slices/cesiumSlice.ts +127 -0
- package/src/store/slices/chatSlice.test.ts +6 -76
- package/src/store/slices/chatSlice.ts +21 -58
- package/src/store/slices/dataSlice.ts +139 -28
- package/src/store/slices/desktopEntitlementSlice.ts +86 -0
- package/src/store/slices/loadingSlice.ts +14 -2
- package/src/store/slices/modelSlice.ts +58 -3
- package/src/store/slices/sectionSlice.test.ts +87 -7
- package/src/store/slices/sectionSlice.ts +151 -5
- package/src/store/slices/uiSlice.ts +28 -5
- package/src/store/types.ts +122 -2
- package/src/store.ts +1 -1
- package/src/utils/desktopModelSnapshot.ts +358 -0
- package/src/utils/ifcConfig.ts +6 -1
- package/src/utils/nativeSpatialDataStore.ts +253 -0
- package/src/utils/serverDataModel.ts +4 -0
- package/src/utils/spatialHierarchy.ts +10 -11
- package/src/utils/viewportUtils.ts +7 -2
- package/src/vite-env.d.ts +0 -4
- package/vite.config.ts +24 -0
- package/dist/assets/arrow-DJf2ErbF.js +0 -20
- package/dist/assets/basketViewActivator-aojwdomq.js +0 -1
- package/dist/assets/desktop-cache-oPzaWXYE.js +0 -1
- package/dist/assets/drawing-2d-gWfpdfYe.js +0 -257
- package/dist/assets/geometry.worker-Nz9_YIqh.js +0 -1
- package/dist/assets/ids-B4jTqB1O.js +0 -1
- package/dist/assets/ifc-lite_bg-eSkBTizQ.wasm +0 -0
- package/dist/assets/index-pbE7itQS.css +0 -1
- package/dist/assets/native-bridge-DSIyEYXG.js +0 -113
- package/dist/assets/wasm-bridge-B0J07fZZ.js +0 -1
- package/src/components/viewer/UpgradePage.tsx +0 -69
- package/src/lib/llm/ClerkChatSync.tsx +0 -74
- package/src/lib/llm/clerk-auth.ts +0 -62
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CesiumOverlay — renders a CesiumJS globe behind the WebGPU canvas,
|
|
7
|
+
* providing real-world 3D context (terrain, buildings, imagery) for
|
|
8
|
+
* georeferenced IFC models.
|
|
9
|
+
*
|
|
10
|
+
* Architecture:
|
|
11
|
+
* - A separate <div> behind the WebGPU <canvas> (z-index layering)
|
|
12
|
+
* - WebGPU canvas uses transparent clear color so Cesium shows through
|
|
13
|
+
* - Camera is synchronized every frame from the IFC viewer camera
|
|
14
|
+
* - CesiumJS is lazy-loaded on first activation to avoid bundle bloat
|
|
15
|
+
* - User controls remain on the WebGPU canvas; Cesium's are disabled
|
|
16
|
+
*
|
|
17
|
+
* Live edit support:
|
|
18
|
+
* - When georef props change (e.g. user edits EPSG, eastings, rotation),
|
|
19
|
+
* the coordinate bridge is rebuilt and the globe flies to the new location
|
|
20
|
+
* - The Cesium viewer itself is NOT recreated — only the bridge is updated
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { useEffect, useRef, useState } from 'react';
|
|
24
|
+
import { useViewerStore } from '@/store';
|
|
25
|
+
import type { MapConversion, ProjectedCRS } from '@ifc-lite/parser';
|
|
26
|
+
import type { CoordinateInfo, GeometryResult } from '@ifc-lite/geometry';
|
|
27
|
+
import { getGlobalRenderer } from '@/hooks/useBCF';
|
|
28
|
+
import { createCesiumBridge, type CesiumBridge } from '@/lib/geo/cesium-bridge';
|
|
29
|
+
|
|
30
|
+
// Lazy-loaded Cesium module and CSS
|
|
31
|
+
let cesiumPromise: Promise<typeof import('cesium')> | null = null;
|
|
32
|
+
let cesiumModule: typeof import('cesium') | null = null;
|
|
33
|
+
function loadCesium() {
|
|
34
|
+
if (!cesiumPromise) {
|
|
35
|
+
cesiumPromise = Promise.all([
|
|
36
|
+
import('cesium'),
|
|
37
|
+
import('cesium/Build/Cesium/Widgets/widgets.css'),
|
|
38
|
+
]).then(([cesium]) => {
|
|
39
|
+
cesiumModule = cesium;
|
|
40
|
+
return cesium;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return cesiumPromise;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Build a minimal GLB with all geometry merged into a SINGLE mesh.
|
|
48
|
+
* This is MUCH faster than GLTFExporter (which creates one glTF node per IFC mesh).
|
|
49
|
+
* For a 42K mesh model: GLTFExporter takes seconds, this takes ~100ms.
|
|
50
|
+
*/
|
|
51
|
+
function buildMergedGLB(meshes: import('@ifc-lite/geometry').MeshData[]): Uint8Array {
|
|
52
|
+
// Pass 1: calculate total sizes
|
|
53
|
+
let totalVerts = 0;
|
|
54
|
+
let totalIdxs = 0;
|
|
55
|
+
for (const m of meshes) {
|
|
56
|
+
if (!m.positions?.length || !m.indices?.length) continue;
|
|
57
|
+
totalVerts += m.positions.length / 3;
|
|
58
|
+
totalIdxs += m.indices.length;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Allocate merged buffers
|
|
62
|
+
const positions = new Float32Array(totalVerts * 3);
|
|
63
|
+
const colors = new Uint8Array(totalVerts * 4);
|
|
64
|
+
const indices = new Uint32Array(totalIdxs);
|
|
65
|
+
|
|
66
|
+
// Pass 2: merge
|
|
67
|
+
let vertOff = 0;
|
|
68
|
+
let idxOff = 0;
|
|
69
|
+
for (const m of meshes) {
|
|
70
|
+
if (!m.positions?.length || !m.indices?.length) continue;
|
|
71
|
+
const nv = m.positions.length / 3;
|
|
72
|
+
positions.set(m.positions, vertOff * 3);
|
|
73
|
+
// Vertex colors from mesh color
|
|
74
|
+
const r = Math.round((m.color?.[0] ?? 0.7) * 255);
|
|
75
|
+
const g = Math.round((m.color?.[1] ?? 0.7) * 255);
|
|
76
|
+
const b = Math.round((m.color?.[2] ?? 0.7) * 255);
|
|
77
|
+
const a = Math.round((m.color?.[3] ?? 1.0) * 255);
|
|
78
|
+
for (let i = 0; i < nv; i++) {
|
|
79
|
+
const ci = (vertOff + i) * 4;
|
|
80
|
+
colors[ci] = r; colors[ci + 1] = g; colors[ci + 2] = b; colors[ci + 3] = a;
|
|
81
|
+
}
|
|
82
|
+
for (let i = 0; i < m.indices.length; i++) {
|
|
83
|
+
indices[idxOff + i] = m.indices[i] + vertOff;
|
|
84
|
+
}
|
|
85
|
+
vertOff += nv;
|
|
86
|
+
idxOff += m.indices.length;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Compute bounds
|
|
90
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
91
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
92
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
93
|
+
const x = positions[i], y = positions[i + 1], z = positions[i + 2];
|
|
94
|
+
if (x < minX) minX = x; if (x > maxX) maxX = x;
|
|
95
|
+
if (y < minY) minY = y; if (y > maxY) maxY = y;
|
|
96
|
+
if (z < minZ) minZ = z; if (z > maxZ) maxZ = z;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Build minimal glTF JSON
|
|
100
|
+
const posByteLen = positions.byteLength;
|
|
101
|
+
const colByteLen = colors.byteLength;
|
|
102
|
+
const idxByteLen = indices.byteLength;
|
|
103
|
+
const totalBinLen = posByteLen + colByteLen + idxByteLen;
|
|
104
|
+
|
|
105
|
+
const gltf = {
|
|
106
|
+
asset: { version: '2.0', generator: 'IFC-Lite-Cesium' },
|
|
107
|
+
scene: 0,
|
|
108
|
+
scenes: [{ nodes: [0] }],
|
|
109
|
+
nodes: [{ mesh: 0 }],
|
|
110
|
+
meshes: [{ primitives: [{ attributes: { POSITION: 0, COLOR_0: 1 }, indices: 2 }] }],
|
|
111
|
+
accessors: [
|
|
112
|
+
{ bufferView: 0, componentType: 5126, count: totalVerts, type: 'VEC3', min: [minX, minY, minZ], max: [maxX, maxY, maxZ] },
|
|
113
|
+
{ bufferView: 1, componentType: 5121, count: totalVerts, type: 'VEC4', normalized: true },
|
|
114
|
+
{ bufferView: 2, componentType: 5125, count: totalIdxs, type: 'SCALAR' },
|
|
115
|
+
],
|
|
116
|
+
bufferViews: [
|
|
117
|
+
{ buffer: 0, byteOffset: 0, byteLength: posByteLen, target: 34962 },
|
|
118
|
+
{ buffer: 0, byteOffset: posByteLen, byteLength: colByteLen, target: 34962 },
|
|
119
|
+
{ buffer: 0, byteOffset: posByteLen + colByteLen, byteLength: idxByteLen, target: 34963 },
|
|
120
|
+
],
|
|
121
|
+
buffers: [{ byteLength: totalBinLen }],
|
|
122
|
+
extensionsUsed: ['KHR_materials_unlit'],
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const jsonStr = JSON.stringify(gltf);
|
|
126
|
+
const jsonBuf = new TextEncoder().encode(jsonStr);
|
|
127
|
+
// Pad JSON to 4-byte alignment
|
|
128
|
+
const jsonPad = (4 - (jsonBuf.length % 4)) % 4;
|
|
129
|
+
const jsonChunkLen = jsonBuf.length + jsonPad;
|
|
130
|
+
// Pad binary to 4-byte alignment
|
|
131
|
+
const binPad = (4 - (totalBinLen % 4)) % 4;
|
|
132
|
+
const binChunkLen = totalBinLen + binPad;
|
|
133
|
+
|
|
134
|
+
// GLB: 12-byte header + 8-byte JSON chunk header + JSON + 8-byte BIN chunk header + BIN
|
|
135
|
+
const glbLen = 12 + 8 + jsonChunkLen + 8 + binChunkLen;
|
|
136
|
+
const glb = new ArrayBuffer(glbLen);
|
|
137
|
+
const view = new DataView(glb);
|
|
138
|
+
let off = 0;
|
|
139
|
+
|
|
140
|
+
// GLB header
|
|
141
|
+
view.setUint32(off, 0x46546C67, true); off += 4; // magic "glTF"
|
|
142
|
+
view.setUint32(off, 2, true); off += 4; // version
|
|
143
|
+
view.setUint32(off, glbLen, true); off += 4; // total length
|
|
144
|
+
|
|
145
|
+
// JSON chunk
|
|
146
|
+
view.setUint32(off, jsonChunkLen, true); off += 4;
|
|
147
|
+
view.setUint32(off, 0x4E4F534A, true); off += 4; // "JSON"
|
|
148
|
+
new Uint8Array(glb, off, jsonBuf.length).set(jsonBuf); off += jsonBuf.length;
|
|
149
|
+
for (let i = 0; i < jsonPad; i++) view.setUint8(off++, 0x20); // space padding
|
|
150
|
+
|
|
151
|
+
// BIN chunk
|
|
152
|
+
view.setUint32(off, binChunkLen, true); off += 4;
|
|
153
|
+
view.setUint32(off, 0x004E4942, true); off += 4; // "BIN\0"
|
|
154
|
+
new Uint8Array(glb, off, posByteLen).set(new Uint8Array(positions.buffer)); off += posByteLen;
|
|
155
|
+
new Uint8Array(glb, off, colByteLen).set(colors); off += colByteLen;
|
|
156
|
+
new Uint8Array(glb, off, idxByteLen).set(new Uint8Array(indices.buffer)); off += idxByteLen;
|
|
157
|
+
|
|
158
|
+
return new Uint8Array(glb);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Build a Cesium model matrix for placing the IFC model in ECEF.
|
|
163
|
+
* Extracted as a pure function so it can be called from both
|
|
164
|
+
* the GLB load effect (initial) and the matrix update effect (instant).
|
|
165
|
+
*/
|
|
166
|
+
function buildModelMatrix(
|
|
167
|
+
Cesium: typeof import('cesium'),
|
|
168
|
+
bridge: CesiumBridge,
|
|
169
|
+
mapConversion: MapConversion | undefined,
|
|
170
|
+
coordinateInfo: CoordinateInfo | undefined,
|
|
171
|
+
clamp: boolean,
|
|
172
|
+
terrainH: number | null,
|
|
173
|
+
) {
|
|
174
|
+
const hScale = mapConversion?.scale ?? 1.0;
|
|
175
|
+
const absc = mapConversion?.xAxisAbscissa ?? 1.0;
|
|
176
|
+
const ordi = mapConversion?.xAxisOrdinate ?? 0.0;
|
|
177
|
+
const bounds = coordinateInfo?.originalBounds;
|
|
178
|
+
// Viewer bounds are already in metres (geometry engine converts from IFC native unit)
|
|
179
|
+
const mvx = bounds ? (bounds.min.x + bounds.max.x) / 2 : 0;
|
|
180
|
+
const mvy = bounds ? (bounds.min.y + bounds.max.y) / 2 : 0;
|
|
181
|
+
const mvz = bounds ? (bounds.min.z + bounds.max.z) / 2 : 0;
|
|
182
|
+
|
|
183
|
+
let placementHeight = bridge.modelOrigin.height;
|
|
184
|
+
if (clamp && terrainH !== null) {
|
|
185
|
+
const minY = bounds?.min.y ?? 0;
|
|
186
|
+
const bottomOffset = mvy - minY; // already in metres
|
|
187
|
+
placementHeight = terrainH + bottomOffset;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const origin = Cesium.Cartesian3.fromDegrees(
|
|
191
|
+
bridge.modelOrigin.longitude, bridge.modelOrigin.latitude, placementHeight,
|
|
192
|
+
);
|
|
193
|
+
const enuToEcef = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
|
|
194
|
+
// No lengthUnitScale here — viewer-space GLB vertices are already in metres.
|
|
195
|
+
const sa = hScale * absc, so = hScale * ordi;
|
|
196
|
+
const tx = -(sa * mvx + so * mvz);
|
|
197
|
+
const ty = -(so * mvx - sa * mvz);
|
|
198
|
+
const tz = -mvy;
|
|
199
|
+
const ifcToEnu = new Cesium.Matrix4(
|
|
200
|
+
sa, 0, so, tx,
|
|
201
|
+
so, 0, -sa, ty,
|
|
202
|
+
0, 1, 0, tz,
|
|
203
|
+
0, 0, 0, 1,
|
|
204
|
+
);
|
|
205
|
+
return Cesium.Matrix4.multiply(enuToEcef, ifcToEnu, new Cesium.Matrix4());
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export interface CesiumOverlayProps {
|
|
209
|
+
mapConversion?: MapConversion;
|
|
210
|
+
projectedCRS?: ProjectedCRS;
|
|
211
|
+
coordinateInfo?: CoordinateInfo;
|
|
212
|
+
geometryResult?: GeometryResult | null;
|
|
213
|
+
/** IFC project length unit → metres (e.g. 0.001 for mm models). Default 1. */
|
|
214
|
+
lengthUnitScale?: number;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function CesiumOverlay({
|
|
218
|
+
mapConversion,
|
|
219
|
+
projectedCRS,
|
|
220
|
+
coordinateInfo,
|
|
221
|
+
geometryResult,
|
|
222
|
+
lengthUnitScale = 1,
|
|
223
|
+
}: CesiumOverlayProps) {
|
|
224
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
225
|
+
const viewerRef = useRef<InstanceType<typeof import('cesium').Viewer> | null>(null);
|
|
226
|
+
const bridgeRef = useRef<CesiumBridge | null>(null);
|
|
227
|
+
const rafRef = useRef<number | null>(null);
|
|
228
|
+
const [status, setStatus] = useState<'idle' | 'loading' | 'ready' | 'error'>('idle');
|
|
229
|
+
const [error, setError] = useState<string | null>(null);
|
|
230
|
+
// Tracks bridge readiness as state (not just a ref) so terrain query effect re-runs
|
|
231
|
+
const [bridgeVersion, setBridgeVersion] = useState(0);
|
|
232
|
+
|
|
233
|
+
const cesiumEnabled = useViewerStore((s) => s.cesiumEnabled);
|
|
234
|
+
const dataSource = useViewerStore((s) => s.cesiumDataSource);
|
|
235
|
+
const ionToken = useViewerStore((s) => s.cesiumIonToken);
|
|
236
|
+
const terrainEnabled = useViewerStore((s) => s.cesiumTerrainEnabled);
|
|
237
|
+
const terrainClamp = useViewerStore((s) => s.cesiumTerrainClamp);
|
|
238
|
+
const setCesiumTerrainClamp = useViewerStore((s) => s.setCesiumTerrainClamp);
|
|
239
|
+
const terrainHeight = useViewerStore((s) => s.cesiumTerrainHeight);
|
|
240
|
+
const setCesiumTerrainHeight = useViewerStore((s) => s.setCesiumTerrainHeight);
|
|
241
|
+
const setCesiumTerrainClipY = useViewerStore((s) => s.setCesiumTerrainClipY);
|
|
242
|
+
const setCesiumGlbLoaded = useViewerStore((s) => s.setCesiumGlbLoaded);
|
|
243
|
+
|
|
244
|
+
// Use refs so the camera sync loop always reads the latest values
|
|
245
|
+
const terrainClampRef = useRef(terrainClamp);
|
|
246
|
+
const terrainHeightRef = useRef(terrainHeight);
|
|
247
|
+
terrainClampRef.current = terrainClamp;
|
|
248
|
+
terrainHeightRef.current = terrainHeight;
|
|
249
|
+
|
|
250
|
+
// Track whether we've auto-clamped to terrain (only once, so user can still uncheck)
|
|
251
|
+
const autoClampedRef = useRef(false);
|
|
252
|
+
|
|
253
|
+
// Track the Cesium model (IFC geometry loaded as glTF for correct world positioning)
|
|
254
|
+
const cesiumModelRef = useRef<{ modelMatrix: any; destroy?: () => void } | null>(null);
|
|
255
|
+
const glbCacheRef = useRef<{ meshCount: number; glb: Uint8Array } | null>(null);
|
|
256
|
+
|
|
257
|
+
// ─── Effect 1: Create/destroy the Cesium viewer (heavy, rare) ───────────
|
|
258
|
+
// Only depends on cesiumEnabled, ionToken, terrainEnabled, dataSource.
|
|
259
|
+
// NOT on mapConversion/projectedCRS — those are handled by Effect 2.
|
|
260
|
+
useEffect(() => {
|
|
261
|
+
if (!cesiumEnabled || !containerRef.current) return;
|
|
262
|
+
|
|
263
|
+
let cancelled = false;
|
|
264
|
+
setStatus('loading');
|
|
265
|
+
setError(null);
|
|
266
|
+
|
|
267
|
+
(async () => {
|
|
268
|
+
try {
|
|
269
|
+
const Cesium = await loadCesium();
|
|
270
|
+
if (cancelled || !containerRef.current) return;
|
|
271
|
+
|
|
272
|
+
// Configure Cesium ion token if provided
|
|
273
|
+
if (ionToken) {
|
|
274
|
+
Cesium.Ion.defaultAccessToken = ionToken;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const viewer = new Cesium.Viewer(containerRef.current, {
|
|
278
|
+
animation: false,
|
|
279
|
+
baseLayerPicker: false,
|
|
280
|
+
fullscreenButton: false,
|
|
281
|
+
geocoder: false,
|
|
282
|
+
homeButton: false,
|
|
283
|
+
infoBox: false,
|
|
284
|
+
sceneModePicker: false,
|
|
285
|
+
selectionIndicator: false,
|
|
286
|
+
timeline: false,
|
|
287
|
+
navigationHelpButton: false,
|
|
288
|
+
navigationInstructionsInitiallyVisible: false,
|
|
289
|
+
// Cesium ion ToS requires visible attribution — use a small container
|
|
290
|
+
// at bottom of the overlay rather than hiding credits entirely.
|
|
291
|
+
msaaSamples: 1,
|
|
292
|
+
requestRenderMode: true,
|
|
293
|
+
maximumRenderTimeChange: Infinity,
|
|
294
|
+
baseLayer: false,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
if (cancelled) { viewer.destroy(); return; }
|
|
298
|
+
|
|
299
|
+
// Disable Cesium's user input — the IFC viewer controls the camera.
|
|
300
|
+
// Keep collision detection off since we set the camera programmatically.
|
|
301
|
+
const scene = viewer.scene;
|
|
302
|
+
const sscc = scene.screenSpaceCameraController;
|
|
303
|
+
sscc.enableRotate = false;
|
|
304
|
+
sscc.enableTranslate = false;
|
|
305
|
+
sscc.enableZoom = false;
|
|
306
|
+
sscc.enableTilt = false;
|
|
307
|
+
sscc.enableLook = false;
|
|
308
|
+
sscc.enableCollisionDetection = false;
|
|
309
|
+
sscc.minimumZoomDistance = 0;
|
|
310
|
+
sscc.maximumZoomDistance = Infinity;
|
|
311
|
+
// Enable depth testing so the model (and other objects) get clipped
|
|
312
|
+
// by terrain — prevents seeing underground portions.
|
|
313
|
+
scene.globe.depthTestAgainstTerrain = true;
|
|
314
|
+
|
|
315
|
+
// Move credit/logo from bottom-left to top-left to avoid overlap
|
|
316
|
+
// with other UI elements.
|
|
317
|
+
const bottomContainer = viewer.bottomContainer as HTMLElement;
|
|
318
|
+
if (bottomContainer) {
|
|
319
|
+
bottomContainer.style.top = '0';
|
|
320
|
+
bottomContainer.style.bottom = 'auto';
|
|
321
|
+
bottomContainer.style.left = '0';
|
|
322
|
+
bottomContainer.style.right = 'auto';
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Disable skybox/atmosphere/fog for transparent compositing
|
|
326
|
+
if (scene.skyBox) (scene.skyBox as any).show = false;
|
|
327
|
+
if (scene.sun) scene.sun.show = false;
|
|
328
|
+
if (scene.moon) scene.moon.show = false;
|
|
329
|
+
if (scene.skyAtmosphere) scene.skyAtmosphere.show = false;
|
|
330
|
+
scene.fog.enabled = false;
|
|
331
|
+
scene.globe.showGroundAtmosphere = false;
|
|
332
|
+
scene.backgroundColor = Cesium.Color.TRANSPARENT;
|
|
333
|
+
scene.globe.baseColor = Cesium.Color.TRANSPARENT;
|
|
334
|
+
|
|
335
|
+
// Add imagery
|
|
336
|
+
try {
|
|
337
|
+
const imageryProvider = await Cesium.IonImageryProvider.fromAssetId(2);
|
|
338
|
+
viewer.imageryLayers.addImageryProvider(imageryProvider);
|
|
339
|
+
} catch {
|
|
340
|
+
viewer.imageryLayers.addImageryProvider(
|
|
341
|
+
new Cesium.OpenStreetMapImageryProvider({
|
|
342
|
+
url: 'https://a.tile.openstreetmap.org/',
|
|
343
|
+
})
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Add terrain
|
|
348
|
+
if (terrainEnabled && ionToken) {
|
|
349
|
+
try {
|
|
350
|
+
const terrainProvider = await Cesium.CesiumTerrainProvider.fromIonAssetId(1);
|
|
351
|
+
viewer.terrainProvider = terrainProvider;
|
|
352
|
+
} catch { /* terrain unavailable */ }
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Add data source layer
|
|
356
|
+
await addDataSourceLayer(Cesium, viewer, dataSource, ionToken);
|
|
357
|
+
|
|
358
|
+
if (cancelled) { viewer.destroy(); return; }
|
|
359
|
+
|
|
360
|
+
viewerRef.current = viewer;
|
|
361
|
+
setStatus('ready');
|
|
362
|
+
} catch (err) {
|
|
363
|
+
if (!cancelled) {
|
|
364
|
+
console.error('[CesiumOverlay] Init failed:', err);
|
|
365
|
+
setError(err instanceof Error ? err.message : 'Cesium initialization failed');
|
|
366
|
+
setStatus('error');
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
})();
|
|
370
|
+
|
|
371
|
+
return () => {
|
|
372
|
+
cancelled = true;
|
|
373
|
+
if (rafRef.current !== null) {
|
|
374
|
+
cancelAnimationFrame(rafRef.current);
|
|
375
|
+
rafRef.current = null;
|
|
376
|
+
}
|
|
377
|
+
if (viewerRef.current) {
|
|
378
|
+
viewerRef.current.destroy();
|
|
379
|
+
viewerRef.current = null;
|
|
380
|
+
}
|
|
381
|
+
// Invalidate model ref — the destroyed viewer took the primitive with it,
|
|
382
|
+
// so Effect 2c must re-load the GLB into the next viewer instance.
|
|
383
|
+
cesiumModelRef.current = null;
|
|
384
|
+
bridgeRef.current = null;
|
|
385
|
+
setStatus('idle');
|
|
386
|
+
};
|
|
387
|
+
}, [cesiumEnabled, ionToken, terrainEnabled, dataSource]);
|
|
388
|
+
|
|
389
|
+
// ─── Effect 2: Rebuild coordinate bridge when georef changes (fast) ─────
|
|
390
|
+
// This is the live-edit handler. When the user changes EPSG, eastings,
|
|
391
|
+
// northings, rotation, etc., we rebuild the bridge and fly to the new spot.
|
|
392
|
+
useEffect(() => {
|
|
393
|
+
if (status !== 'ready' || !mapConversion || !projectedCRS) {
|
|
394
|
+
bridgeRef.current = null;
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
let cancelled = false;
|
|
399
|
+
|
|
400
|
+
(async () => {
|
|
401
|
+
const bridge = await createCesiumBridge(mapConversion, projectedCRS, coordinateInfo, lengthUnitScale);
|
|
402
|
+
if (cancelled) return;
|
|
403
|
+
|
|
404
|
+
if (!bridge) {
|
|
405
|
+
bridgeRef.current = null;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const prevBridge = bridgeRef.current;
|
|
410
|
+
bridgeRef.current = bridge;
|
|
411
|
+
autoClampedRef.current = false; // reset for new bridge
|
|
412
|
+
// Bump version so terrain query effect re-runs now that bridge is ready
|
|
413
|
+
setBridgeVersion((v) => v + 1);
|
|
414
|
+
|
|
415
|
+
// Fly to the new model location (smooth animation)
|
|
416
|
+
const viewer = viewerRef.current;
|
|
417
|
+
const Cesium = cesiumModule;
|
|
418
|
+
if (viewer && Cesium) {
|
|
419
|
+
const { modelOrigin } = bridge;
|
|
420
|
+
|
|
421
|
+
const isFirstPosition = !prevBridge;
|
|
422
|
+
const target = Cesium.Cartesian3.fromDegrees(
|
|
423
|
+
modelOrigin.longitude, modelOrigin.latitude, modelOrigin.height,
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
if (isFirstPosition) {
|
|
427
|
+
// First time: fly to the model location.
|
|
428
|
+
// On first load terrain tiles may not be ready, so globe.getHeight
|
|
429
|
+
// can return undefined. Use flyTo which handles terrain automatically,
|
|
430
|
+
// targeting a safe altitude above the model origin.
|
|
431
|
+
const safeHeight = Math.max(modelOrigin.height, 100);
|
|
432
|
+
viewer.camera.flyTo({
|
|
433
|
+
destination: Cesium.Cartesian3.fromDegrees(
|
|
434
|
+
modelOrigin.longitude, modelOrigin.latitude, safeHeight + 500,
|
|
435
|
+
),
|
|
436
|
+
orientation: {
|
|
437
|
+
heading: 0,
|
|
438
|
+
pitch: Cesium.Math.toRadians(-45),
|
|
439
|
+
roll: 0,
|
|
440
|
+
},
|
|
441
|
+
duration: 0, // instant
|
|
442
|
+
});
|
|
443
|
+
} else if (prevBridge) {
|
|
444
|
+
// Georef edit: just re-render, the camera sync loop will pick
|
|
445
|
+
// up the new bridge on the next frame. No dramatic fly animation.
|
|
446
|
+
viewer.scene.requestRender();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
})();
|
|
450
|
+
|
|
451
|
+
return () => { cancelled = true; };
|
|
452
|
+
}, [status, mapConversion, projectedCRS, coordinateInfo, lengthUnitScale]);
|
|
453
|
+
|
|
454
|
+
// ─── Effect 2b: Query terrain height when bridge is ready ───────────────
|
|
455
|
+
// Also re-queries when terrainClamp is toggled on (in case first query failed)
|
|
456
|
+
useEffect(() => {
|
|
457
|
+
if (status !== 'ready') return;
|
|
458
|
+
const bridge = bridgeRef.current;
|
|
459
|
+
const viewer = viewerRef.current;
|
|
460
|
+
const Cesium = cesiumModule;
|
|
461
|
+
if (!bridge || !viewer || !Cesium) return;
|
|
462
|
+
|
|
463
|
+
let cancelled = false;
|
|
464
|
+
|
|
465
|
+
// Query immediately, then retry after a delay if terrain tiles weren't loaded yet
|
|
466
|
+
// Compute model center Y in viewer space for terrain clip offset
|
|
467
|
+
const bounds = coordinateInfo?.originalBounds;
|
|
468
|
+
const modelVY = bounds ? (bounds.min.y + bounds.max.y) / 2 : 0;
|
|
469
|
+
const modelMinY = bounds ? bounds.min.y : 0;
|
|
470
|
+
|
|
471
|
+
const doQuery = () => {
|
|
472
|
+
bridge.queryTerrainHeight(Cesium, viewer).then((h) => {
|
|
473
|
+
if (!cancelled && h !== null) {
|
|
474
|
+
setCesiumTerrainHeight(h);
|
|
475
|
+
// Compute terrain clip Y in viewer space (both h and modelOrigin.height are metres,
|
|
476
|
+
// bounds are also in metres since the geometry engine converts during extraction)
|
|
477
|
+
const terrainClipY = modelMinY + (h - bridge.modelOrigin.height);
|
|
478
|
+
setCesiumTerrainClipY(terrainClipY);
|
|
479
|
+
|
|
480
|
+
// Auto-enable terrain clamping if the model is significantly below terrain
|
|
481
|
+
// (only once — don't override if the user has manually toggled)
|
|
482
|
+
if (!autoClampedRef.current && h > bridge.modelOrigin.height + 5) {
|
|
483
|
+
autoClampedRef.current = true;
|
|
484
|
+
setCesiumTerrainClamp(true);
|
|
485
|
+
// Fly camera to the clamped position so the model is visible
|
|
486
|
+
viewer.camera.flyTo({
|
|
487
|
+
destination: Cesium.Cartesian3.fromDegrees(
|
|
488
|
+
bridge.modelOrigin.longitude, bridge.modelOrigin.latitude, h + 500,
|
|
489
|
+
),
|
|
490
|
+
orientation: { heading: 0, pitch: Cesium.Math.toRadians(-45), roll: 0 },
|
|
491
|
+
duration: 0,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// First attempt
|
|
499
|
+
doQuery();
|
|
500
|
+
// Retry after 5s in case terrain tiles were still loading
|
|
501
|
+
const retryTimer = setTimeout(doQuery, 5000);
|
|
502
|
+
|
|
503
|
+
return () => { cancelled = true; clearTimeout(retryTimer); };
|
|
504
|
+
}, [status, terrainEnabled, terrainClamp, bridgeVersion]);
|
|
505
|
+
|
|
506
|
+
// ─── Effect 2c: Load GLB into Cesium (only when geometry changes) ───────
|
|
507
|
+
// This is the heavy operation — only re-runs when geometry actually changes.
|
|
508
|
+
useEffect(() => {
|
|
509
|
+
if (status !== 'ready' || !geometryResult?.meshes?.length) return;
|
|
510
|
+
const viewer = viewerRef.current;
|
|
511
|
+
const bridge = bridgeRef.current;
|
|
512
|
+
const Cesium = cesiumModule;
|
|
513
|
+
if (!viewer || !bridge || !Cesium) return;
|
|
514
|
+
|
|
515
|
+
let cancelled = false;
|
|
516
|
+
|
|
517
|
+
const startExport = async () => {
|
|
518
|
+
if (cancelled) return;
|
|
519
|
+
try {
|
|
520
|
+
// Export GLB (cached by mesh count — skip if already loaded)
|
|
521
|
+
const meshCount = geometryResult.meshes.length;
|
|
522
|
+
if (cesiumModelRef.current && glbCacheRef.current?.meshCount === meshCount) {
|
|
523
|
+
// Model already loaded with same geometry — just update matrix
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Remove previous model
|
|
528
|
+
if (cesiumModelRef.current) {
|
|
529
|
+
viewer.scene.primitives.remove(cesiumModelRef.current);
|
|
530
|
+
cesiumModelRef.current = null;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
let glbBytes: Uint8Array;
|
|
534
|
+
if (glbCacheRef.current?.meshCount === meshCount) {
|
|
535
|
+
glbBytes = glbCacheRef.current.glb;
|
|
536
|
+
} else {
|
|
537
|
+
await new Promise(r => setTimeout(r, 50));
|
|
538
|
+
if (cancelled) return;
|
|
539
|
+
glbBytes = buildMergedGLB(geometryResult.meshes);
|
|
540
|
+
glbCacheRef.current = { meshCount, glb: glbBytes };
|
|
541
|
+
}
|
|
542
|
+
if (cancelled) return;
|
|
543
|
+
|
|
544
|
+
await new Promise(r => setTimeout(r, 0));
|
|
545
|
+
if (cancelled) return;
|
|
546
|
+
|
|
547
|
+
// Build initial model matrix
|
|
548
|
+
const modelMatrix = buildModelMatrix(Cesium, bridge, mapConversion, coordinateInfo, terrainClampRef.current, terrainHeightRef.current);
|
|
549
|
+
|
|
550
|
+
const blob = new Blob([glbBytes as BlobPart], { type: 'model/gltf-binary' });
|
|
551
|
+
const glbUrl = URL.createObjectURL(blob);
|
|
552
|
+
let model: { modelMatrix: any; destroy?: () => void } | null = null;
|
|
553
|
+
try {
|
|
554
|
+
model = await Cesium.Model.fromGltfAsync({
|
|
555
|
+
url: glbUrl,
|
|
556
|
+
modelMatrix,
|
|
557
|
+
shadows: Cesium.ShadowMode.DISABLED,
|
|
558
|
+
// The generated GLB stores viewer-space vertices and buildModelMatrix
|
|
559
|
+
// already maps viewer axes into ENU. Avoid Cesium's default glTF
|
|
560
|
+
// Y-up/Z-forward correction or the model is rotated onto its side.
|
|
561
|
+
upAxis: Cesium.Axis.Z,
|
|
562
|
+
forwardAxis: Cesium.Axis.X,
|
|
563
|
+
});
|
|
564
|
+
} finally {
|
|
565
|
+
URL.revokeObjectURL(glbUrl);
|
|
566
|
+
}
|
|
567
|
+
if (cancelled) {
|
|
568
|
+
model?.destroy?.();
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
viewer.scene.primitives.add(model);
|
|
573
|
+
cesiumModelRef.current = model;
|
|
574
|
+
setCesiumGlbLoaded(true);
|
|
575
|
+
viewer.scene.requestRender();
|
|
576
|
+
} catch (err) {
|
|
577
|
+
console.warn('[CesiumOverlay] Failed to load IFC model into Cesium:', err);
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const deferTimer = setTimeout(startExport, 1000);
|
|
582
|
+
|
|
583
|
+
return () => {
|
|
584
|
+
cancelled = true;
|
|
585
|
+
clearTimeout(deferTimer);
|
|
586
|
+
if (cesiumModelRef.current && viewerRef.current) {
|
|
587
|
+
viewerRef.current.scene.primitives.remove(cesiumModelRef.current);
|
|
588
|
+
cesiumModelRef.current = null;
|
|
589
|
+
}
|
|
590
|
+
setCesiumGlbLoaded(false);
|
|
591
|
+
};
|
|
592
|
+
}, [status, bridgeVersion, geometryResult]);
|
|
593
|
+
|
|
594
|
+
// ─── Effect 2d: Update model matrix (instant, no reload) ────────────────
|
|
595
|
+
// When terrain clamp, terrain height, or georef changes, just update the
|
|
596
|
+
// existing model's matrix — no GLB re-export, no flicker.
|
|
597
|
+
useEffect(() => {
|
|
598
|
+
const model = cesiumModelRef.current;
|
|
599
|
+
const bridge = bridgeRef.current;
|
|
600
|
+
const viewer = viewerRef.current;
|
|
601
|
+
const Cesium = cesiumModule;
|
|
602
|
+
if (!model || !bridge || !viewer || !Cesium) return;
|
|
603
|
+
|
|
604
|
+
const newMatrix = buildModelMatrix(Cesium, bridge, mapConversion, coordinateInfo, terrainClamp, terrainHeight);
|
|
605
|
+
model.modelMatrix = newMatrix;
|
|
606
|
+
viewer.scene.requestRender();
|
|
607
|
+
}, [terrainClamp, terrainHeight, mapConversion, coordinateInfo, lengthUnitScale]);
|
|
608
|
+
|
|
609
|
+
// ─── Effect 3: Camera sync loop ─────────────────────────────────────────
|
|
610
|
+
useEffect(() => {
|
|
611
|
+
if (status !== 'ready') return;
|
|
612
|
+
|
|
613
|
+
const viewer = viewerRef.current;
|
|
614
|
+
if (!viewer) return;
|
|
615
|
+
|
|
616
|
+
let cancelled = false;
|
|
617
|
+
|
|
618
|
+
function syncCamera() {
|
|
619
|
+
if (cancelled) return;
|
|
620
|
+
|
|
621
|
+
const bridge = bridgeRef.current;
|
|
622
|
+
const renderer = getGlobalRenderer();
|
|
623
|
+
const Cesium = cesiumModule;
|
|
624
|
+
if (!viewer || !bridge || !renderer || !Cesium) {
|
|
625
|
+
rafRef.current = requestAnimationFrame(syncCamera);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const camera = renderer.getCamera();
|
|
630
|
+
const camPos = camera.getPosition();
|
|
631
|
+
const camTarget = camera.getTarget();
|
|
632
|
+
const camUp = camera.getUp();
|
|
633
|
+
const fov = camera.getFOV();
|
|
634
|
+
|
|
635
|
+
// Sync Cesium camera (no terrain offset — model matrix handles clamping)
|
|
636
|
+
bridge.syncCamera(Cesium, viewer, camPos, camTarget, camUp, fov);
|
|
637
|
+
|
|
638
|
+
rafRef.current = requestAnimationFrame(syncCamera);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
rafRef.current = requestAnimationFrame(syncCamera);
|
|
642
|
+
|
|
643
|
+
return () => {
|
|
644
|
+
cancelled = true;
|
|
645
|
+
if (rafRef.current !== null) {
|
|
646
|
+
cancelAnimationFrame(rafRef.current);
|
|
647
|
+
rafRef.current = null;
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
}, [status]);
|
|
651
|
+
|
|
652
|
+
if (!cesiumEnabled || !mapConversion || !projectedCRS) {
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return (
|
|
657
|
+
<>
|
|
658
|
+
<div
|
|
659
|
+
ref={containerRef}
|
|
660
|
+
className="absolute inset-0 z-0"
|
|
661
|
+
style={{ pointerEvents: 'none' }}
|
|
662
|
+
/>
|
|
663
|
+
{status === 'loading' && (
|
|
664
|
+
<div className="absolute top-2 left-1/2 -translate-x-1/2 z-10 flex items-center gap-2 px-3 py-1.5 bg-black/60 backdrop-blur-sm rounded text-xs text-white font-mono">
|
|
665
|
+
<div className="h-3 w-3 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
666
|
+
Loading 3D context...
|
|
667
|
+
</div>
|
|
668
|
+
)}
|
|
669
|
+
{status === 'error' && error && (
|
|
670
|
+
<div className="absolute top-2 left-1/2 -translate-x-1/2 z-10 px-3 py-1.5 bg-red-900/80 backdrop-blur-sm rounded text-xs text-red-200 font-mono">
|
|
671
|
+
{error}
|
|
672
|
+
</div>
|
|
673
|
+
)}
|
|
674
|
+
</>
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Add the selected 3D data source layer to the Cesium viewer.
|
|
680
|
+
*/
|
|
681
|
+
async function addDataSourceLayer(
|
|
682
|
+
Cesium: typeof import('cesium'),
|
|
683
|
+
viewer: InstanceType<typeof import('cesium').Viewer>,
|
|
684
|
+
dataSource: string,
|
|
685
|
+
ionToken: string,
|
|
686
|
+
) {
|
|
687
|
+
try {
|
|
688
|
+
switch (dataSource) {
|
|
689
|
+
case 'osm-buildings': {
|
|
690
|
+
if (!ionToken) return;
|
|
691
|
+
const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(96188);
|
|
692
|
+
viewer.scene.primitives.add(tileset);
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
case 'google-photorealistic': {
|
|
696
|
+
try {
|
|
697
|
+
const tileset = await Cesium.createGooglePhotorealistic3DTileset();
|
|
698
|
+
viewer.scene.primitives.add(tileset);
|
|
699
|
+
} catch {
|
|
700
|
+
if (ionToken) {
|
|
701
|
+
const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2275207);
|
|
702
|
+
viewer.scene.primitives.add(tileset);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
case 'bing-aerial':
|
|
708
|
+
default:
|
|
709
|
+
// No 3D tileset for Bing — imagery is added separately via imageryLayers
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
} catch (err) {
|
|
713
|
+
console.warn('[CesiumOverlay] Failed to add data source:', dataSource, err);
|
|
714
|
+
}
|
|
715
|
+
}
|