@ifc-lite/viewer 1.17.3 → 1.17.4
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 +15 -0
- package/DESKTOP_CONTRACT_VERSION +1 -0
- package/dist/assets/arrow-CZ5kQ26f.js +20 -0
- package/dist/assets/basketViewActivator-BmnNtVfZ.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/{exporters-C_6J153K.js → exporters-ChAtBmlj.js} +2225 -1754
- package/dist/assets/geometry.worker-BQ0rzNo-.js +1 -0
- package/dist/assets/ifc-lite_bg-BX4E7TX8.wasm +0 -0
- package/dist/assets/{index-jhBr1wbn.js → index-Co8E2-FE.js} +28959 -24612
- package/dist/assets/index-DckuDqlv.css +1 -0
- package/dist/assets/{maplibre-gl-BpvwNKKy.js → maplibre-gl-CGLcoNXc.js} +1 -1
- package/dist/assets/native-bridge-BRvbckFQ.js +429 -0
- package/dist/assets/{sandbox-B79eavQ3.js → sandbox-DZiNLNMk.js} +5 -5
- package/dist/assets/{server-client-D3bUPJJc.js → server-client-BV8zHZ7Y.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-g01g7T9b.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 +10 -8
- package/package.json +15 -12
- package/src/App.tsx +1 -17
- package/src/components/viewer/BCFPanel.tsx +46 -4
- package/src/components/viewer/CesiumOverlay.tsx +672 -0
- package/src/components/viewer/CesiumSettingsDialog.tsx +100 -0
- package/src/components/viewer/ChatPanel.tsx +54 -16
- 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 +353 -27
- package/src/components/viewer/PropertiesPanel.tsx +218 -79
- package/src/components/viewer/ScriptPanel.tsx +34 -8
- package/src/components/viewer/SettingsPage.tsx +430 -0
- package/src/components/viewer/StatusBar.tsx +17 -1
- package/src/components/viewer/UpgradePage.tsx +6 -4
- package/src/components/viewer/ViewerLayout.tsx +47 -6
- package/src/components/viewer/Viewport.tsx +49 -8
- package/src/components/viewer/ViewportContainer.tsx +280 -28
- package/src/components/viewer/ViewportOverlays.tsx +129 -27
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +117 -5
- package/src/components/viewer/properties/LocationMap.tsx +458 -17
- package/src/components/viewer/selectionHandlers.ts +4 -3
- package/src/components/viewer/useAnimationLoop.ts +4 -0
- package/src/components/viewer/useGeometryStreaming.ts +127 -40
- package/src/components/viewer/useMouseControls.ts +4 -1
- package/src/hooks/ingest/viewerModelIngest.ts +275 -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 +57 -225
- package/src/hooks/useIfcLoader.ts +1656 -130
- package/src/hooks/useIfcServer.ts +0 -69
- package/src/lib/desktop/ClerkDesktopEntitlementSync.tsx +175 -0
- package/src/lib/desktop/desktopEntitlementEvents.ts +39 -0
- package/src/lib/desktop-entitlement.ts +45 -0
- package/src/lib/desktop-product.ts +124 -0
- package/src/lib/geo/cesium-bridge.ts +310 -0
- package/src/lib/geo/reproject.ts +151 -25
- package/src/lib/recent-files.ts +2 -1
- package/src/services/analysis-extensions.ts +125 -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/index.ts +40 -2
- package/src/store/slices/cesiumSlice.ts +122 -0
- package/src/store/slices/chatSlice.ts +5 -1
- 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/types.ts +96 -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 +250 -0
- package/src/utils/serverDataModel.ts +4 -0
- package/src/utils/spatialHierarchy.ts +10 -11
- 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/geometry.worker-Nz9_YIqh.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
|
@@ -0,0 +1,672 @@
|
|
|
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
|
+
const mvx = bounds ? (bounds.min.x + bounds.max.x) / 2 : 0;
|
|
179
|
+
const mvy = bounds ? (bounds.min.y + bounds.max.y) / 2 : 0;
|
|
180
|
+
const mvz = bounds ? (bounds.min.z + bounds.max.z) / 2 : 0;
|
|
181
|
+
|
|
182
|
+
let placementHeight = bridge.modelOrigin.height;
|
|
183
|
+
if (clamp && terrainH !== null) {
|
|
184
|
+
const minY = bounds?.min.y ?? 0;
|
|
185
|
+
const bottomOffset = mvy - minY;
|
|
186
|
+
placementHeight = terrainH + bottomOffset;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const origin = Cesium.Cartesian3.fromDegrees(
|
|
190
|
+
bridge.modelOrigin.longitude, bridge.modelOrigin.latitude, placementHeight,
|
|
191
|
+
);
|
|
192
|
+
const enuToEcef = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
|
|
193
|
+
const sa = hScale * absc, so = hScale * ordi;
|
|
194
|
+
const ifcToEnu = new Cesium.Matrix4(
|
|
195
|
+
sa, -so, 0, -(sa * mvx + so * mvz),
|
|
196
|
+
so, sa, 0, -(so * mvx - sa * mvz),
|
|
197
|
+
0, 0, 1, -mvy,
|
|
198
|
+
0, 0, 0, 1,
|
|
199
|
+
);
|
|
200
|
+
return Cesium.Matrix4.multiply(enuToEcef, ifcToEnu, new Cesium.Matrix4());
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface CesiumOverlayProps {
|
|
204
|
+
mapConversion?: MapConversion;
|
|
205
|
+
projectedCRS?: ProjectedCRS;
|
|
206
|
+
coordinateInfo?: CoordinateInfo;
|
|
207
|
+
geometryResult?: GeometryResult | null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function CesiumOverlay({
|
|
211
|
+
mapConversion,
|
|
212
|
+
projectedCRS,
|
|
213
|
+
coordinateInfo,
|
|
214
|
+
geometryResult,
|
|
215
|
+
}: CesiumOverlayProps) {
|
|
216
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
217
|
+
const viewerRef = useRef<InstanceType<typeof import('cesium').Viewer> | null>(null);
|
|
218
|
+
const bridgeRef = useRef<CesiumBridge | null>(null);
|
|
219
|
+
const rafRef = useRef<number | null>(null);
|
|
220
|
+
const [status, setStatus] = useState<'idle' | 'loading' | 'ready' | 'error'>('idle');
|
|
221
|
+
const [error, setError] = useState<string | null>(null);
|
|
222
|
+
// Tracks bridge readiness as state (not just a ref) so terrain query effect re-runs
|
|
223
|
+
const [bridgeVersion, setBridgeVersion] = useState(0);
|
|
224
|
+
|
|
225
|
+
const cesiumEnabled = useViewerStore((s) => s.cesiumEnabled);
|
|
226
|
+
const dataSource = useViewerStore((s) => s.cesiumDataSource);
|
|
227
|
+
const ionToken = useViewerStore((s) => s.cesiumIonToken);
|
|
228
|
+
const terrainEnabled = useViewerStore((s) => s.cesiumTerrainEnabled);
|
|
229
|
+
const terrainClamp = useViewerStore((s) => s.cesiumTerrainClamp);
|
|
230
|
+
const terrainHeight = useViewerStore((s) => s.cesiumTerrainHeight);
|
|
231
|
+
const setCesiumTerrainHeight = useViewerStore((s) => s.setCesiumTerrainHeight);
|
|
232
|
+
const setCesiumTerrainClipY = useViewerStore((s) => s.setCesiumTerrainClipY);
|
|
233
|
+
const setCesiumGlbLoaded = useViewerStore((s) => s.setCesiumGlbLoaded);
|
|
234
|
+
|
|
235
|
+
// Use refs so the camera sync loop always reads the latest values
|
|
236
|
+
const terrainClampRef = useRef(terrainClamp);
|
|
237
|
+
const terrainHeightRef = useRef(terrainHeight);
|
|
238
|
+
terrainClampRef.current = terrainClamp;
|
|
239
|
+
terrainHeightRef.current = terrainHeight;
|
|
240
|
+
|
|
241
|
+
// Track the Cesium model (IFC geometry loaded as glTF for correct world positioning)
|
|
242
|
+
const cesiumModelRef = useRef<{ modelMatrix: any; destroy?: () => void } | null>(null);
|
|
243
|
+
const glbCacheRef = useRef<{ meshCount: number; glb: Uint8Array } | null>(null);
|
|
244
|
+
|
|
245
|
+
// ─── Effect 1: Create/destroy the Cesium viewer (heavy, rare) ───────────
|
|
246
|
+
// Only depends on cesiumEnabled, ionToken, terrainEnabled, dataSource.
|
|
247
|
+
// NOT on mapConversion/projectedCRS — those are handled by Effect 2.
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
if (!cesiumEnabled || !containerRef.current) return;
|
|
250
|
+
|
|
251
|
+
let cancelled = false;
|
|
252
|
+
setStatus('loading');
|
|
253
|
+
setError(null);
|
|
254
|
+
|
|
255
|
+
(async () => {
|
|
256
|
+
try {
|
|
257
|
+
const Cesium = await loadCesium();
|
|
258
|
+
if (cancelled || !containerRef.current) return;
|
|
259
|
+
|
|
260
|
+
// Configure Cesium ion token if provided
|
|
261
|
+
if (ionToken) {
|
|
262
|
+
Cesium.Ion.defaultAccessToken = ionToken;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const viewer = new Cesium.Viewer(containerRef.current, {
|
|
266
|
+
animation: false,
|
|
267
|
+
baseLayerPicker: false,
|
|
268
|
+
fullscreenButton: false,
|
|
269
|
+
geocoder: false,
|
|
270
|
+
homeButton: false,
|
|
271
|
+
infoBox: false,
|
|
272
|
+
sceneModePicker: false,
|
|
273
|
+
selectionIndicator: false,
|
|
274
|
+
timeline: false,
|
|
275
|
+
navigationHelpButton: false,
|
|
276
|
+
navigationInstructionsInitiallyVisible: false,
|
|
277
|
+
// Cesium ion ToS requires visible attribution — use a small container
|
|
278
|
+
// at bottom of the overlay rather than hiding credits entirely.
|
|
279
|
+
msaaSamples: 1,
|
|
280
|
+
requestRenderMode: true,
|
|
281
|
+
maximumRenderTimeChange: Infinity,
|
|
282
|
+
baseLayer: false,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (cancelled) { viewer.destroy(); return; }
|
|
286
|
+
|
|
287
|
+
// Disable Cesium's user input — the IFC viewer controls the camera.
|
|
288
|
+
// Keep collision detection off since we set the camera programmatically.
|
|
289
|
+
const scene = viewer.scene;
|
|
290
|
+
const sscc = scene.screenSpaceCameraController;
|
|
291
|
+
sscc.enableRotate = false;
|
|
292
|
+
sscc.enableTranslate = false;
|
|
293
|
+
sscc.enableZoom = false;
|
|
294
|
+
sscc.enableTilt = false;
|
|
295
|
+
sscc.enableLook = false;
|
|
296
|
+
sscc.enableCollisionDetection = false;
|
|
297
|
+
sscc.minimumZoomDistance = 0;
|
|
298
|
+
sscc.maximumZoomDistance = Infinity;
|
|
299
|
+
// Enable depth testing so the model (and other objects) get clipped
|
|
300
|
+
// by terrain — prevents seeing underground portions.
|
|
301
|
+
scene.globe.depthTestAgainstTerrain = true;
|
|
302
|
+
|
|
303
|
+
// Move credit/logo from bottom-left to top-left to avoid overlap
|
|
304
|
+
// with other UI elements.
|
|
305
|
+
const bottomContainer = viewer.bottomContainer as HTMLElement;
|
|
306
|
+
if (bottomContainer) {
|
|
307
|
+
bottomContainer.style.top = '0';
|
|
308
|
+
bottomContainer.style.bottom = 'auto';
|
|
309
|
+
bottomContainer.style.left = '0';
|
|
310
|
+
bottomContainer.style.right = 'auto';
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Disable skybox/atmosphere/fog for transparent compositing
|
|
314
|
+
if (scene.skyBox) (scene.skyBox as any).show = false;
|
|
315
|
+
if (scene.sun) scene.sun.show = false;
|
|
316
|
+
if (scene.moon) scene.moon.show = false;
|
|
317
|
+
if (scene.skyAtmosphere) scene.skyAtmosphere.show = false;
|
|
318
|
+
scene.fog.enabled = false;
|
|
319
|
+
scene.globe.showGroundAtmosphere = false;
|
|
320
|
+
scene.backgroundColor = Cesium.Color.TRANSPARENT;
|
|
321
|
+
scene.globe.baseColor = Cesium.Color.TRANSPARENT;
|
|
322
|
+
|
|
323
|
+
// Add imagery
|
|
324
|
+
try {
|
|
325
|
+
const imageryProvider = await Cesium.IonImageryProvider.fromAssetId(2);
|
|
326
|
+
viewer.imageryLayers.addImageryProvider(imageryProvider);
|
|
327
|
+
} catch {
|
|
328
|
+
viewer.imageryLayers.addImageryProvider(
|
|
329
|
+
new Cesium.OpenStreetMapImageryProvider({
|
|
330
|
+
url: 'https://a.tile.openstreetmap.org/',
|
|
331
|
+
})
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Add terrain
|
|
336
|
+
if (terrainEnabled && ionToken) {
|
|
337
|
+
try {
|
|
338
|
+
const terrainProvider = await Cesium.CesiumTerrainProvider.fromIonAssetId(1);
|
|
339
|
+
viewer.terrainProvider = terrainProvider;
|
|
340
|
+
} catch { /* terrain unavailable */ }
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Add data source layer
|
|
344
|
+
await addDataSourceLayer(Cesium, viewer, dataSource, ionToken);
|
|
345
|
+
|
|
346
|
+
if (cancelled) { viewer.destroy(); return; }
|
|
347
|
+
|
|
348
|
+
viewerRef.current = viewer;
|
|
349
|
+
setStatus('ready');
|
|
350
|
+
} catch (err) {
|
|
351
|
+
if (!cancelled) {
|
|
352
|
+
console.error('[CesiumOverlay] Init failed:', err);
|
|
353
|
+
setError(err instanceof Error ? err.message : 'Cesium initialization failed');
|
|
354
|
+
setStatus('error');
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
})();
|
|
358
|
+
|
|
359
|
+
return () => {
|
|
360
|
+
cancelled = true;
|
|
361
|
+
if (rafRef.current !== null) {
|
|
362
|
+
cancelAnimationFrame(rafRef.current);
|
|
363
|
+
rafRef.current = null;
|
|
364
|
+
}
|
|
365
|
+
if (viewerRef.current) {
|
|
366
|
+
viewerRef.current.destroy();
|
|
367
|
+
viewerRef.current = null;
|
|
368
|
+
}
|
|
369
|
+
// Invalidate model ref — the destroyed viewer took the primitive with it,
|
|
370
|
+
// so Effect 2c must re-load the GLB into the next viewer instance.
|
|
371
|
+
cesiumModelRef.current = null;
|
|
372
|
+
bridgeRef.current = null;
|
|
373
|
+
setStatus('idle');
|
|
374
|
+
};
|
|
375
|
+
}, [cesiumEnabled, ionToken, terrainEnabled, dataSource]);
|
|
376
|
+
|
|
377
|
+
// ─── Effect 2: Rebuild coordinate bridge when georef changes (fast) ─────
|
|
378
|
+
// This is the live-edit handler. When the user changes EPSG, eastings,
|
|
379
|
+
// northings, rotation, etc., we rebuild the bridge and fly to the new spot.
|
|
380
|
+
useEffect(() => {
|
|
381
|
+
if (status !== 'ready' || !mapConversion || !projectedCRS) {
|
|
382
|
+
bridgeRef.current = null;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
let cancelled = false;
|
|
387
|
+
|
|
388
|
+
(async () => {
|
|
389
|
+
const bridge = await createCesiumBridge(mapConversion, projectedCRS, coordinateInfo);
|
|
390
|
+
if (cancelled) return;
|
|
391
|
+
|
|
392
|
+
if (!bridge) {
|
|
393
|
+
bridgeRef.current = null;
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const prevBridge = bridgeRef.current;
|
|
398
|
+
bridgeRef.current = bridge;
|
|
399
|
+
// Bump version so terrain query effect re-runs now that bridge is ready
|
|
400
|
+
setBridgeVersion((v) => v + 1);
|
|
401
|
+
|
|
402
|
+
// Fly to the new model location (smooth animation)
|
|
403
|
+
const viewer = viewerRef.current;
|
|
404
|
+
const Cesium = cesiumModule;
|
|
405
|
+
if (viewer && Cesium) {
|
|
406
|
+
const { modelOrigin } = bridge;
|
|
407
|
+
|
|
408
|
+
const isFirstPosition = !prevBridge;
|
|
409
|
+
const target = Cesium.Cartesian3.fromDegrees(
|
|
410
|
+
modelOrigin.longitude, modelOrigin.latitude, modelOrigin.height,
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
if (isFirstPosition) {
|
|
414
|
+
// First time: instant jump to isometric view
|
|
415
|
+
const hpr = new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-45), 300);
|
|
416
|
+
viewer.camera.lookAt(target, hpr);
|
|
417
|
+
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
|
|
418
|
+
viewer.scene.requestRender();
|
|
419
|
+
} else if (prevBridge) {
|
|
420
|
+
// Georef edit: just re-render, the camera sync loop will pick
|
|
421
|
+
// up the new bridge on the next frame. No dramatic fly animation.
|
|
422
|
+
viewer.scene.requestRender();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
})();
|
|
426
|
+
|
|
427
|
+
return () => { cancelled = true; };
|
|
428
|
+
}, [status, mapConversion, projectedCRS, coordinateInfo]);
|
|
429
|
+
|
|
430
|
+
// ─── Effect 2b: Query terrain height when bridge is ready ───────────────
|
|
431
|
+
// Also re-queries when terrainClamp is toggled on (in case first query failed)
|
|
432
|
+
useEffect(() => {
|
|
433
|
+
if (status !== 'ready') return;
|
|
434
|
+
const bridge = bridgeRef.current;
|
|
435
|
+
const viewer = viewerRef.current;
|
|
436
|
+
const Cesium = cesiumModule;
|
|
437
|
+
if (!bridge || !viewer || !Cesium) return;
|
|
438
|
+
|
|
439
|
+
let cancelled = false;
|
|
440
|
+
|
|
441
|
+
// Query immediately, then retry after a delay if terrain tiles weren't loaded yet
|
|
442
|
+
// Compute model center Y in viewer space for terrain clip offset
|
|
443
|
+
const bounds = coordinateInfo?.originalBounds;
|
|
444
|
+
const modelVY = bounds ? (bounds.min.y + bounds.max.y) / 2 : 0;
|
|
445
|
+
const modelMinY = bounds ? bounds.min.y : 0;
|
|
446
|
+
|
|
447
|
+
const doQuery = () => {
|
|
448
|
+
bridge.queryTerrainHeight(Cesium, viewer).then((h) => {
|
|
449
|
+
if (!cancelled && h !== null) {
|
|
450
|
+
setCesiumTerrainHeight(h);
|
|
451
|
+
// Compute terrain clip Y in viewer space:
|
|
452
|
+
// terrain is at height h (meters). Model origin is at bridge.modelOrigin.height.
|
|
453
|
+
// In viewer Y-up, the bottom of the model is at modelMinY.
|
|
454
|
+
// Terrain clip Y = modelMinY + (terrainHeight - modelOriginHeight)
|
|
455
|
+
// This places the clip plane at the terrain surface relative to model geometry.
|
|
456
|
+
const terrainClipY = modelMinY + (h - bridge.modelOrigin.height);
|
|
457
|
+
setCesiumTerrainClipY(terrainClipY);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// First attempt
|
|
463
|
+
doQuery();
|
|
464
|
+
// Retry after 5s in case terrain tiles were still loading
|
|
465
|
+
const retryTimer = setTimeout(doQuery, 5000);
|
|
466
|
+
|
|
467
|
+
return () => { cancelled = true; clearTimeout(retryTimer); };
|
|
468
|
+
}, [status, terrainEnabled, terrainClamp, bridgeVersion]);
|
|
469
|
+
|
|
470
|
+
// ─── Effect 2c: Load GLB into Cesium (only when geometry changes) ───────
|
|
471
|
+
// This is the heavy operation — only re-runs when geometry actually changes.
|
|
472
|
+
useEffect(() => {
|
|
473
|
+
if (status !== 'ready' || !geometryResult?.meshes?.length) return;
|
|
474
|
+
const viewer = viewerRef.current;
|
|
475
|
+
const bridge = bridgeRef.current;
|
|
476
|
+
const Cesium = cesiumModule;
|
|
477
|
+
if (!viewer || !bridge || !Cesium) return;
|
|
478
|
+
|
|
479
|
+
let cancelled = false;
|
|
480
|
+
|
|
481
|
+
const startExport = async () => {
|
|
482
|
+
if (cancelled) return;
|
|
483
|
+
try {
|
|
484
|
+
// Export GLB (cached by mesh count — skip if already loaded)
|
|
485
|
+
const meshCount = geometryResult.meshes.length;
|
|
486
|
+
if (cesiumModelRef.current && glbCacheRef.current?.meshCount === meshCount) {
|
|
487
|
+
// Model already loaded with same geometry — just update matrix
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Remove previous model
|
|
492
|
+
if (cesiumModelRef.current) {
|
|
493
|
+
viewer.scene.primitives.remove(cesiumModelRef.current);
|
|
494
|
+
cesiumModelRef.current = null;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
let glbBytes: Uint8Array;
|
|
498
|
+
if (glbCacheRef.current?.meshCount === meshCount) {
|
|
499
|
+
glbBytes = glbCacheRef.current.glb;
|
|
500
|
+
} else {
|
|
501
|
+
await new Promise(r => setTimeout(r, 50));
|
|
502
|
+
if (cancelled) return;
|
|
503
|
+
glbBytes = buildMergedGLB(geometryResult.meshes);
|
|
504
|
+
glbCacheRef.current = { meshCount, glb: glbBytes };
|
|
505
|
+
}
|
|
506
|
+
if (cancelled) return;
|
|
507
|
+
|
|
508
|
+
await new Promise(r => setTimeout(r, 0));
|
|
509
|
+
if (cancelled) return;
|
|
510
|
+
|
|
511
|
+
// Build initial model matrix
|
|
512
|
+
const modelMatrix = buildModelMatrix(Cesium, bridge, mapConversion, coordinateInfo, terrainClampRef.current, terrainHeightRef.current);
|
|
513
|
+
|
|
514
|
+
const blob = new Blob([glbBytes as BlobPart], { type: 'model/gltf-binary' });
|
|
515
|
+
const glbUrl = URL.createObjectURL(blob);
|
|
516
|
+
let model: { modelMatrix: any; destroy?: () => void } | null = null;
|
|
517
|
+
try {
|
|
518
|
+
model = await Cesium.Model.fromGltfAsync({
|
|
519
|
+
url: glbUrl, modelMatrix, shadows: Cesium.ShadowMode.DISABLED,
|
|
520
|
+
});
|
|
521
|
+
} finally {
|
|
522
|
+
URL.revokeObjectURL(glbUrl);
|
|
523
|
+
}
|
|
524
|
+
if (cancelled) {
|
|
525
|
+
model?.destroy?.();
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
viewer.scene.primitives.add(model);
|
|
530
|
+
cesiumModelRef.current = model;
|
|
531
|
+
setCesiumGlbLoaded(true);
|
|
532
|
+
viewer.scene.requestRender();
|
|
533
|
+
} catch (err) {
|
|
534
|
+
console.warn('[CesiumOverlay] Failed to load IFC model into Cesium:', err);
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const deferTimer = setTimeout(startExport, 1000);
|
|
539
|
+
|
|
540
|
+
return () => {
|
|
541
|
+
cancelled = true;
|
|
542
|
+
clearTimeout(deferTimer);
|
|
543
|
+
if (cesiumModelRef.current && viewerRef.current) {
|
|
544
|
+
viewerRef.current.scene.primitives.remove(cesiumModelRef.current);
|
|
545
|
+
cesiumModelRef.current = null;
|
|
546
|
+
}
|
|
547
|
+
setCesiumGlbLoaded(false);
|
|
548
|
+
};
|
|
549
|
+
}, [status, bridgeVersion, geometryResult]);
|
|
550
|
+
|
|
551
|
+
// ─── Effect 2d: Update model matrix (instant, no reload) ────────────────
|
|
552
|
+
// When terrain clamp, terrain height, or georef changes, just update the
|
|
553
|
+
// existing model's matrix — no GLB re-export, no flicker.
|
|
554
|
+
useEffect(() => {
|
|
555
|
+
const model = cesiumModelRef.current;
|
|
556
|
+
const bridge = bridgeRef.current;
|
|
557
|
+
const viewer = viewerRef.current;
|
|
558
|
+
const Cesium = cesiumModule;
|
|
559
|
+
if (!model || !bridge || !viewer || !Cesium) return;
|
|
560
|
+
|
|
561
|
+
const newMatrix = buildModelMatrix(Cesium, bridge, mapConversion, coordinateInfo, terrainClamp, terrainHeight);
|
|
562
|
+
model.modelMatrix = newMatrix;
|
|
563
|
+
viewer.scene.requestRender();
|
|
564
|
+
}, [terrainClamp, terrainHeight, mapConversion, coordinateInfo]);
|
|
565
|
+
|
|
566
|
+
// ─── Effect 3: Camera sync loop ─────────────────────────────────────────
|
|
567
|
+
useEffect(() => {
|
|
568
|
+
if (status !== 'ready') return;
|
|
569
|
+
|
|
570
|
+
const viewer = viewerRef.current;
|
|
571
|
+
if (!viewer) return;
|
|
572
|
+
|
|
573
|
+
let cancelled = false;
|
|
574
|
+
|
|
575
|
+
function syncCamera() {
|
|
576
|
+
if (cancelled) return;
|
|
577
|
+
|
|
578
|
+
const bridge = bridgeRef.current;
|
|
579
|
+
const renderer = getGlobalRenderer();
|
|
580
|
+
const Cesium = cesiumModule;
|
|
581
|
+
if (!viewer || !bridge || !renderer || !Cesium) {
|
|
582
|
+
rafRef.current = requestAnimationFrame(syncCamera);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const camera = renderer.getCamera();
|
|
587
|
+
const camPos = camera.getPosition();
|
|
588
|
+
const camTarget = camera.getTarget();
|
|
589
|
+
const camUp = camera.getUp();
|
|
590
|
+
const fov = camera.getFOV();
|
|
591
|
+
|
|
592
|
+
// Sync Cesium camera (no terrain offset — model matrix handles clamping)
|
|
593
|
+
bridge.syncCamera(Cesium, viewer, camPos, camTarget, camUp, fov);
|
|
594
|
+
|
|
595
|
+
rafRef.current = requestAnimationFrame(syncCamera);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
rafRef.current = requestAnimationFrame(syncCamera);
|
|
599
|
+
|
|
600
|
+
return () => {
|
|
601
|
+
cancelled = true;
|
|
602
|
+
if (rafRef.current !== null) {
|
|
603
|
+
cancelAnimationFrame(rafRef.current);
|
|
604
|
+
rafRef.current = null;
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
}, [status]);
|
|
608
|
+
|
|
609
|
+
if (!cesiumEnabled || !mapConversion || !projectedCRS) {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return (
|
|
614
|
+
<>
|
|
615
|
+
<div
|
|
616
|
+
ref={containerRef}
|
|
617
|
+
className="absolute inset-0 z-0"
|
|
618
|
+
style={{ pointerEvents: 'none' }}
|
|
619
|
+
/>
|
|
620
|
+
{status === 'loading' && (
|
|
621
|
+
<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">
|
|
622
|
+
<div className="h-3 w-3 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
623
|
+
Loading 3D context...
|
|
624
|
+
</div>
|
|
625
|
+
)}
|
|
626
|
+
{status === 'error' && error && (
|
|
627
|
+
<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">
|
|
628
|
+
{error}
|
|
629
|
+
</div>
|
|
630
|
+
)}
|
|
631
|
+
</>
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Add the selected 3D data source layer to the Cesium viewer.
|
|
637
|
+
*/
|
|
638
|
+
async function addDataSourceLayer(
|
|
639
|
+
Cesium: typeof import('cesium'),
|
|
640
|
+
viewer: InstanceType<typeof import('cesium').Viewer>,
|
|
641
|
+
dataSource: string,
|
|
642
|
+
ionToken: string,
|
|
643
|
+
) {
|
|
644
|
+
try {
|
|
645
|
+
switch (dataSource) {
|
|
646
|
+
case 'osm-buildings': {
|
|
647
|
+
if (!ionToken) return;
|
|
648
|
+
const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(96188);
|
|
649
|
+
viewer.scene.primitives.add(tileset);
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
case 'google-photorealistic': {
|
|
653
|
+
try {
|
|
654
|
+
const tileset = await Cesium.createGooglePhotorealistic3DTileset();
|
|
655
|
+
viewer.scene.primitives.add(tileset);
|
|
656
|
+
} catch {
|
|
657
|
+
if (ionToken) {
|
|
658
|
+
const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2275207);
|
|
659
|
+
viewer.scene.primitives.add(tileset);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
case 'bing-aerial':
|
|
665
|
+
default:
|
|
666
|
+
// No 3D tileset for Bing — imagery is added separately via imageryLayers
|
|
667
|
+
break;
|
|
668
|
+
}
|
|
669
|
+
} catch (err) {
|
|
670
|
+
console.warn('[CesiumOverlay] Failed to add data source:', dataSource, err);
|
|
671
|
+
}
|
|
672
|
+
}
|