@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,310 @@
|
|
|
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
|
+
* Cesium coordinate bridge — lookAtTransform approach.
|
|
7
|
+
*
|
|
8
|
+
* KEY INSIGHT (from Cesium GitHub #6032): Camera.setView() with direction/up
|
|
9
|
+
* vectors causes drift because it doesn't properly orthonormalize. The fix:
|
|
10
|
+
* use lookAtTransform() which sets a reference frame and keeps the camera
|
|
11
|
+
* matrix clean.
|
|
12
|
+
*
|
|
13
|
+
* APPROACH: Build a single 4x4 matrix that transforms from IFC viewer space
|
|
14
|
+
* to ECEF, pass it to Cesium via lookAtTransform(). Then set camera position,
|
|
15
|
+
* direction, and up in IFC viewer coordinates — Cesium applies the transform
|
|
16
|
+
* internally with full precision.
|
|
17
|
+
*
|
|
18
|
+
* The viewer→ECEF transform is composed of:
|
|
19
|
+
* 1. Translate by (-modelCenter) to center on model origin
|
|
20
|
+
* 2. Rotate via viewerYup→ifcZup axis swap
|
|
21
|
+
* 3. Rotate via Helmert (IFC→projected CRS alignment)
|
|
22
|
+
* 4. Transform ENU→ECEF via Cesium.Transforms.eastNorthUpToFixedFrame()
|
|
23
|
+
*
|
|
24
|
+
* Since this is a SINGLE matrix, it's applied atomically by Cesium — no
|
|
25
|
+
* intermediate rounding or re-orthonormalization. The model stays pinned.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import proj4 from 'proj4';
|
|
29
|
+
import type { MapConversion, ProjectedCRS } from '@ifc-lite/parser';
|
|
30
|
+
import type { CoordinateInfo } from '@ifc-lite/geometry';
|
|
31
|
+
import { resolveProjection } from './reproject';
|
|
32
|
+
|
|
33
|
+
export interface GeodesicPosition {
|
|
34
|
+
longitude: number;
|
|
35
|
+
latitude: number;
|
|
36
|
+
height: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface CesiumBridge {
|
|
40
|
+
modelOrigin: GeodesicPosition;
|
|
41
|
+
rotationAngle: number;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Sync the Cesium camera using lookAtTransform with a viewer→ECEF matrix.
|
|
45
|
+
* The IFC camera position/direction/up are passed in viewer coordinates —
|
|
46
|
+
* Cesium transforms them to ECEF internally using one consistent matrix.
|
|
47
|
+
*/
|
|
48
|
+
syncCamera(
|
|
49
|
+
Cesium: typeof import('cesium'),
|
|
50
|
+
viewer: InstanceType<typeof import('cesium').Viewer>,
|
|
51
|
+
camPos: { x: number; y: number; z: number },
|
|
52
|
+
camTarget: { x: number; y: number; z: number },
|
|
53
|
+
camUp: { x: number; y: number; z: number },
|
|
54
|
+
fov: number,
|
|
55
|
+
terrainClampOffset?: number,
|
|
56
|
+
): void;
|
|
57
|
+
|
|
58
|
+
/** Query terrain height at model origin. */
|
|
59
|
+
queryTerrainHeight(
|
|
60
|
+
Cesium: typeof import('cesium'),
|
|
61
|
+
viewer: InstanceType<typeof import('cesium').Viewer>,
|
|
62
|
+
): Promise<number | null>;
|
|
63
|
+
|
|
64
|
+
viewerToGeodetic(vx: number, vy: number, vz: number): GeodesicPosition | null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const TERRAIN_QUERY_RETRY_DELAY_MS = 3000;
|
|
68
|
+
|
|
69
|
+
export async function createCesiumBridge(
|
|
70
|
+
mapConversion: MapConversion,
|
|
71
|
+
projectedCRS: ProjectedCRS,
|
|
72
|
+
coordinateInfo?: CoordinateInfo,
|
|
73
|
+
): Promise<CesiumBridge | null> {
|
|
74
|
+
const projDef = await resolveProjection(projectedCRS);
|
|
75
|
+
if (!projDef) return null;
|
|
76
|
+
|
|
77
|
+
const hScale = mapConversion.scale ?? 1.0;
|
|
78
|
+
const absc = mapConversion.xAxisAbscissa ?? 1.0;
|
|
79
|
+
const ordi = mapConversion.xAxisOrdinate ?? 0.0;
|
|
80
|
+
const rotAngle = Math.atan2(ordi, absc);
|
|
81
|
+
|
|
82
|
+
const shift = coordinateInfo?.originShift ?? { x: 0, y: 0, z: 0 };
|
|
83
|
+
const rtc = coordinateInfo?.wasmRtcOffset;
|
|
84
|
+
const rtcYup = rtc
|
|
85
|
+
? { x: rtc.x, y: rtc.z, z: -rtc.y }
|
|
86
|
+
: { x: 0, y: 0, z: 0 };
|
|
87
|
+
|
|
88
|
+
const bounds = coordinateInfo?.originalBounds;
|
|
89
|
+
const modelVX = bounds ? (bounds.min.x + bounds.max.x) / 2 : 0;
|
|
90
|
+
const modelVY = bounds ? (bounds.min.y + bounds.max.y) / 2 : 0;
|
|
91
|
+
const modelVZ = bounds ? (bounds.min.z + bounds.max.z) / 2 : 0;
|
|
92
|
+
|
|
93
|
+
// ── Compute model origin in WGS84 ──
|
|
94
|
+
const owx = modelVX + shift.x + rtcYup.x;
|
|
95
|
+
const owy = modelVY + shift.y + rtcYup.y;
|
|
96
|
+
const owz = modelVZ + shift.z + rtcYup.z;
|
|
97
|
+
// Viewer Y-up → IFC Z-up
|
|
98
|
+
const oIfcX = owx;
|
|
99
|
+
const oIfcY = -owz;
|
|
100
|
+
const oIfcZ = owy;
|
|
101
|
+
const oEasting = mapConversion.eastings + hScale * (absc * oIfcX - ordi * oIfcY);
|
|
102
|
+
const oNorthing = mapConversion.northings + hScale * (ordi * oIfcX + absc * oIfcY);
|
|
103
|
+
const oHeight = mapConversion.orthogonalHeight + oIfcZ;
|
|
104
|
+
|
|
105
|
+
let originLon: number, originLat: number;
|
|
106
|
+
try {
|
|
107
|
+
const [lon, lat] = proj4(projDef, 'WGS84', [oEasting, oNorthing]);
|
|
108
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
|
|
109
|
+
originLon = lon;
|
|
110
|
+
originLat = lat;
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const modelOrigin: GeodesicPosition = {
|
|
116
|
+
longitude: originLon,
|
|
117
|
+
latitude: originLat,
|
|
118
|
+
height: oHeight,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// ── Build the viewer→ENU 3x3 rotation matrix ──
|
|
122
|
+
// This converts a DELTA vector from viewer space to ENU.
|
|
123
|
+
// Step 1: viewer Y-up → IFC Z-up: (vx, vy, vz) → (vx, -vz, vy)
|
|
124
|
+
// Step 2: Helmert rotation: (ifcX, ifcY) → (east, north) with scale
|
|
125
|
+
//
|
|
126
|
+
// Combined as a 3x3 matrix M where [east, north, up] = M * [vx, vy, vz]:
|
|
127
|
+
// east = hScale * (absc * vx - ordi * (-vz)) = hScale * (absc*vx + ordi*vz)
|
|
128
|
+
// north = hScale * (ordi * vx + absc * (-vz)) = hScale * (ordi*vx - absc*vz)
|
|
129
|
+
// up = vy (ifcZ = vy, vertical is viewer Y)
|
|
130
|
+
//
|
|
131
|
+
// So M = [hScale*absc, 0, hScale*ordi ]
|
|
132
|
+
// [hScale*ordi, 0, -hScale*absc ]
|
|
133
|
+
// [0, 1, 0 ]
|
|
134
|
+
const m00 = hScale * absc; // east from vx
|
|
135
|
+
const m01 = 0; // east from vy
|
|
136
|
+
const m02 = hScale * ordi; // east from vz
|
|
137
|
+
const m10 = hScale * ordi; // north from vx
|
|
138
|
+
const m11 = 0; // north from vy
|
|
139
|
+
const m12 = -hScale * absc; // north from vz
|
|
140
|
+
const m20 = 0; // up from vx
|
|
141
|
+
const m21 = 1; // up from vy
|
|
142
|
+
const m22 = 0; // up from vz
|
|
143
|
+
|
|
144
|
+
// ── Cache for ECEF objects ──
|
|
145
|
+
let viewerToEcefMatrix: InstanceType<typeof import('cesium').Matrix4> | null = null;
|
|
146
|
+
let modelOriginCartesian: InstanceType<typeof import('cesium').Cartesian3> | null = null;
|
|
147
|
+
let cachedClampUp: number | null = null;
|
|
148
|
+
|
|
149
|
+
function ensureEcefCache(Cesium: typeof import('cesium'), clampUp: number) {
|
|
150
|
+
if (cachedClampUp === clampUp && viewerToEcefMatrix !== null) return;
|
|
151
|
+
cachedClampUp = clampUp;
|
|
152
|
+
|
|
153
|
+
const originWithClamp = Cesium.Cartesian3.fromDegrees(
|
|
154
|
+
originLon, originLat, oHeight + clampUp,
|
|
155
|
+
);
|
|
156
|
+
modelOriginCartesian = originWithClamp;
|
|
157
|
+
|
|
158
|
+
// Get ENU→ECEF 4x4 matrix at model origin
|
|
159
|
+
const enuToEcef = Cesium.Transforms.eastNorthUpToFixedFrame(originWithClamp);
|
|
160
|
+
|
|
161
|
+
// Build viewer→ECEF = enuToEcef * viewerToENU
|
|
162
|
+
// viewerToENU is: translate(-modelCenter) then rotate by M
|
|
163
|
+
// As a 4x4: columns are the ENU directions of viewer axes, translation is -modelCenter in ENU
|
|
164
|
+
//
|
|
165
|
+
// viewerToENU_4x4 = [ m00 m01 m02 tx ]
|
|
166
|
+
// [ m10 m11 m12 ty ]
|
|
167
|
+
// [ m20 m21 m22 tz ]
|
|
168
|
+
// [ 0 0 0 1 ]
|
|
169
|
+
// where (tx, ty, tz) = M * (-modelVX, -modelVY, -modelVZ)
|
|
170
|
+
const tx = m00 * (-modelVX) + m01 * (-modelVY) + m02 * (-modelVZ);
|
|
171
|
+
const ty = m10 * (-modelVX) + m11 * (-modelVY) + m12 * (-modelVZ);
|
|
172
|
+
const tz = m20 * (-modelVX) + m21 * (-modelVY) + m22 * (-modelVZ);
|
|
173
|
+
|
|
174
|
+
// Cesium Matrix4 is column-major
|
|
175
|
+
const viewerToEnu = new Cesium.Matrix4(
|
|
176
|
+
m00, m01, m02, tx,
|
|
177
|
+
m10, m11, m12, ty,
|
|
178
|
+
m20, m21, m22, tz,
|
|
179
|
+
0, 0, 0, 1,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Compose: viewerToEcef = enuToEcef * viewerToEnu
|
|
183
|
+
viewerToEcefMatrix = Cesium.Matrix4.multiply(
|
|
184
|
+
enuToEcef, viewerToEnu, new Cesium.Matrix4(),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function syncCamera(
|
|
189
|
+
Cesium: typeof import('cesium'),
|
|
190
|
+
viewer: InstanceType<typeof import('cesium').Viewer>,
|
|
191
|
+
camPos: { x: number; y: number; z: number },
|
|
192
|
+
camTarget: { x: number; y: number; z: number },
|
|
193
|
+
camUp: { x: number; y: number; z: number },
|
|
194
|
+
fov: number,
|
|
195
|
+
terrainClampOffset?: number,
|
|
196
|
+
): void {
|
|
197
|
+
const clampUp = terrainClampOffset ?? 0;
|
|
198
|
+
ensureEcefCache(Cesium, clampUp);
|
|
199
|
+
if (!viewerToEcefMatrix) return;
|
|
200
|
+
|
|
201
|
+
// Set the camera's reference frame to our viewer→ECEF transform.
|
|
202
|
+
// After this call, all camera properties (position, direction, up)
|
|
203
|
+
// are interpreted in IFC VIEWER coordinates, not ECEF.
|
|
204
|
+
viewer.camera.lookAtTransform(viewerToEcefMatrix);
|
|
205
|
+
|
|
206
|
+
// Now set camera in VIEWER coordinates — Cesium applies the transform
|
|
207
|
+
viewer.camera.position = new Cesium.Cartesian3(camPos.x, camPos.y, camPos.z);
|
|
208
|
+
|
|
209
|
+
const dirX = camTarget.x - camPos.x;
|
|
210
|
+
const dirY = camTarget.y - camPos.y;
|
|
211
|
+
const dirZ = camTarget.z - camPos.z;
|
|
212
|
+
const dirLen = Math.sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
|
|
213
|
+
if (dirLen > 1e-8) {
|
|
214
|
+
viewer.camera.direction = new Cesium.Cartesian3(
|
|
215
|
+
dirX / dirLen, dirY / dirLen, dirZ / dirLen,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
viewer.camera.up = new Cesium.Cartesian3(camUp.x, camUp.y, camUp.z);
|
|
220
|
+
|
|
221
|
+
// Recompute right = direction × up (maintain orthonormality)
|
|
222
|
+
const right = Cesium.Cartesian3.cross(
|
|
223
|
+
viewer.camera.direction, viewer.camera.up, new Cesium.Cartesian3(),
|
|
224
|
+
);
|
|
225
|
+
Cesium.Cartesian3.normalize(right, right);
|
|
226
|
+
viewer.camera.right = right;
|
|
227
|
+
|
|
228
|
+
// Sync FOV — CRITICAL for preventing model drift.
|
|
229
|
+
// IFC renderer uses `fov` as VERTICAL FOV always.
|
|
230
|
+
// Cesium's PerspectiveFrustum.fov is HORIZONTAL when aspect > 1 (landscape).
|
|
231
|
+
// If we set Cesium's fov = IFC's vertical fov, Cesium treats it as horizontal,
|
|
232
|
+
// producing a completely different projection — the model slides during orbit.
|
|
233
|
+
// Fix: convert vertical FOV → horizontal FOV.
|
|
234
|
+
const frustum = viewer.camera.frustum;
|
|
235
|
+
if (frustum instanceof Cesium.PerspectiveFrustum) {
|
|
236
|
+
const aspect = frustum.aspectRatio || (viewer.canvas.width / viewer.canvas.height);
|
|
237
|
+
if (aspect > 1) {
|
|
238
|
+
// Landscape: Cesium expects horizontal FOV
|
|
239
|
+
// horizontal_fov = 2 * atan(aspect * tan(vertical_fov / 2))
|
|
240
|
+
frustum.fov = 2 * Math.atan(aspect * Math.tan(fov / 2));
|
|
241
|
+
} else {
|
|
242
|
+
// Portrait: Cesium uses fov as vertical — pass through
|
|
243
|
+
frustum.fov = fov;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
viewer.scene.requestRender();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function queryTerrainHeight(
|
|
251
|
+
Cesium: typeof import('cesium'),
|
|
252
|
+
viewer: InstanceType<typeof import('cesium').Viewer>,
|
|
253
|
+
): Promise<number | null> {
|
|
254
|
+
const position = Cesium.Cartographic.fromDegrees(originLon, originLat);
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const globeHeight = viewer.scene.globe.getHeight(position);
|
|
258
|
+
if (globeHeight !== undefined && Number.isFinite(globeHeight)) {
|
|
259
|
+
return globeHeight;
|
|
260
|
+
}
|
|
261
|
+
} catch { /* not available yet */ }
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const terrainProvider = viewer.terrainProvider;
|
|
265
|
+
if (terrainProvider) {
|
|
266
|
+
const results = await Cesium.sampleTerrainMostDetailed(terrainProvider, [position]);
|
|
267
|
+
if (results && results.length > 0 && Number.isFinite(results[0].height)) {
|
|
268
|
+
return results[0].height;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} catch { /* terrain sampling failed */ }
|
|
272
|
+
|
|
273
|
+
await new Promise(r => setTimeout(r, TERRAIN_QUERY_RETRY_DELAY_MS));
|
|
274
|
+
try {
|
|
275
|
+
const globeHeight = viewer.scene.globe.getHeight(position);
|
|
276
|
+
if (globeHeight !== undefined && Number.isFinite(globeHeight)) {
|
|
277
|
+
return globeHeight;
|
|
278
|
+
}
|
|
279
|
+
} catch { /* still not available */ }
|
|
280
|
+
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function viewerToGeodetic(vx: number, vy: number, vz: number): GeodesicPosition | null {
|
|
285
|
+
const wx = vx + shift.x + rtcYup.x;
|
|
286
|
+
const wy = vy + shift.y + rtcYup.y;
|
|
287
|
+
const wz = vz + shift.z + rtcYup.z;
|
|
288
|
+
const ifcX = wx;
|
|
289
|
+
const ifcY = -wz;
|
|
290
|
+
const ifcZ = wy;
|
|
291
|
+
const easting = mapConversion.eastings + hScale * (absc * ifcX - ordi * ifcY);
|
|
292
|
+
const northing = mapConversion.northings + hScale * (ordi * ifcX + absc * ifcY);
|
|
293
|
+
const height = mapConversion.orthogonalHeight + ifcZ;
|
|
294
|
+
try {
|
|
295
|
+
const [lon, lat] = proj4(projDef!, 'WGS84', [easting, northing]);
|
|
296
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
|
|
297
|
+
return { longitude: lon, latitude: lat, height };
|
|
298
|
+
} catch {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
modelOrigin,
|
|
305
|
+
rotationAngle: rotAngle,
|
|
306
|
+
syncCamera,
|
|
307
|
+
queryTerrainHeight,
|
|
308
|
+
viewerToGeodetic,
|
|
309
|
+
};
|
|
310
|
+
}
|
package/src/lib/geo/reproject.ts
CHANGED
|
@@ -176,31 +176,7 @@ function computeProjectedCenter(
|
|
|
176
176
|
conversion: MapConversion,
|
|
177
177
|
coordinateInfo?: CoordinateInfo,
|
|
178
178
|
): { easting: number; northing: number } {
|
|
179
|
-
|
|
180
|
-
let ifcY = 0;
|
|
181
|
-
|
|
182
|
-
if (coordinateInfo) {
|
|
183
|
-
const bounds = coordinateInfo.originalBounds;
|
|
184
|
-
const shift = coordinateInfo.originShift;
|
|
185
|
-
const rtc = coordinateInfo.wasmRtcOffset;
|
|
186
|
-
|
|
187
|
-
// Convert WASM RTC offset from IFC Z-up to viewer Y-up
|
|
188
|
-
const rtcYup = rtc
|
|
189
|
-
? { x: rtc.x, y: rtc.z, z: -rtc.y }
|
|
190
|
-
: { x: 0, y: 0, z: 0 };
|
|
191
|
-
|
|
192
|
-
// Bounds center in viewer Y-up (scene-local)
|
|
193
|
-
const cx = (bounds.min.x + bounds.max.x) / 2;
|
|
194
|
-
const cz = (bounds.min.z + bounds.max.z) / 2;
|
|
195
|
-
|
|
196
|
-
// World Y-up = scene_local + originShift + wasmRtcOffset_yup
|
|
197
|
-
const worldYupX = cx + shift.x + rtcYup.x;
|
|
198
|
-
const worldYupZ = cz + shift.z + rtcYup.z;
|
|
199
|
-
|
|
200
|
-
// Convert Y-up to IFC Z-up: ifc_x = viewer_x, ifc_y = -viewer_z
|
|
201
|
-
ifcX = worldYupX;
|
|
202
|
-
ifcY = -worldYupZ;
|
|
203
|
-
}
|
|
179
|
+
const { ifcX, ifcY } = computeLocalIfcCenter(coordinateInfo);
|
|
204
180
|
|
|
205
181
|
// Apply MapConversion rotation + scale + offset
|
|
206
182
|
const scale = conversion.scale ?? 1.0;
|
|
@@ -242,3 +218,153 @@ export async function reprojectToLatLon(
|
|
|
242
218
|
return null;
|
|
243
219
|
}
|
|
244
220
|
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Compute the model's local IFC center offset (ifcX, ifcY) from coordinate info.
|
|
224
|
+
* This is the geometry center in IFC Z-up coordinates, before MapConversion is applied.
|
|
225
|
+
*/
|
|
226
|
+
function computeLocalIfcCenter(coordinateInfo?: CoordinateInfo): { ifcX: number; ifcY: number } {
|
|
227
|
+
if (!coordinateInfo) return { ifcX: 0, ifcY: 0 };
|
|
228
|
+
|
|
229
|
+
const bounds = coordinateInfo.originalBounds;
|
|
230
|
+
const shift = coordinateInfo.originShift;
|
|
231
|
+
const rtc = coordinateInfo.wasmRtcOffset;
|
|
232
|
+
|
|
233
|
+
const rtcYup = rtc
|
|
234
|
+
? { x: rtc.x, y: rtc.z, z: -rtc.y }
|
|
235
|
+
: { x: 0, y: 0, z: 0 };
|
|
236
|
+
|
|
237
|
+
const cx = (bounds.min.x + bounds.max.x) / 2;
|
|
238
|
+
const cz = (bounds.min.z + bounds.max.z) / 2;
|
|
239
|
+
|
|
240
|
+
const worldYupX = cx + shift.x + rtcYup.x;
|
|
241
|
+
const worldYupZ = cz + shift.z + rtcYup.z;
|
|
242
|
+
|
|
243
|
+
return { ifcX: worldYupX, ifcY: -worldYupZ };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Reverse-project a WGS84 lat/lon into the IfcMapConversion eastings/northings
|
|
248
|
+
* values that would place the model center at the given location.
|
|
249
|
+
*
|
|
250
|
+
* This accounts for the model's local geometry offset, rotation, and scale:
|
|
251
|
+
* projected = eastings + scale * (cos*ifcX - sin*ifcY)
|
|
252
|
+
* ⟹ eastings = projected - scale * (cos*ifcX - sin*ifcY)
|
|
253
|
+
*/
|
|
254
|
+
export async function reprojectFromLatLon(
|
|
255
|
+
latLon: LatLon,
|
|
256
|
+
crs: ProjectedCRS,
|
|
257
|
+
conversion?: MapConversion,
|
|
258
|
+
coordinateInfo?: CoordinateInfo,
|
|
259
|
+
): Promise<{ easting: number; northing: number } | null> {
|
|
260
|
+
const projDef = await resolveProjection(crs);
|
|
261
|
+
if (!projDef) return null;
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const [projE, projN] = proj4('WGS84', projDef, [latLon.lon, latLon.lat]);
|
|
265
|
+
if (!Number.isFinite(projE) || !Number.isFinite(projN)) return null;
|
|
266
|
+
|
|
267
|
+
// Subtract the rotated/scaled local geometry offset so that
|
|
268
|
+
// the resulting eastings/northings place the model center at this position
|
|
269
|
+
const { ifcX, ifcY } = computeLocalIfcCenter(coordinateInfo);
|
|
270
|
+
const scale = conversion?.scale ?? 1.0;
|
|
271
|
+
const abscissa = conversion?.xAxisAbscissa ?? 1.0;
|
|
272
|
+
const ordinate = conversion?.xAxisOrdinate ?? 0.0;
|
|
273
|
+
|
|
274
|
+
const easting = projE - scale * (abscissa * ifcX - ordinate * ifcY);
|
|
275
|
+
const northing = projN - scale * (ordinate * ifcX + abscissa * ifcY);
|
|
276
|
+
|
|
277
|
+
return { easting, northing };
|
|
278
|
+
} catch {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Compute a building footprint rectangle from the model's bounding box and
|
|
285
|
+
* reproject each corner to WGS84 for display as a GeoJSON polygon on a web map.
|
|
286
|
+
*
|
|
287
|
+
* Uses the shiftedBounds (scene-local after RTC) from CoordinateInfo, transforms
|
|
288
|
+
* each corner through the MapConversion pipeline (rotation + scale + offset),
|
|
289
|
+
* then reprojects to lat/lon. The result is a rotated rectangle matching the
|
|
290
|
+
* model's XZ extent on the map.
|
|
291
|
+
*
|
|
292
|
+
* @returns A single GeoJSON-compatible polygon: closed ring of [lon, lat] pairs
|
|
293
|
+
*/
|
|
294
|
+
export async function computeFootprintGeoJSON(
|
|
295
|
+
conversion: MapConversion,
|
|
296
|
+
crs: ProjectedCRS,
|
|
297
|
+
coordinateInfo: CoordinateInfo,
|
|
298
|
+
): Promise<[number, number][] | null> {
|
|
299
|
+
const projDef = await resolveProjection(crs);
|
|
300
|
+
if (!projDef) {
|
|
301
|
+
console.warn('[footprint] failed to resolve projection for CRS:', crs.name);
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const scale = conversion.scale ?? 1.0;
|
|
306
|
+
const abscissa = conversion.xAxisAbscissa ?? 1.0;
|
|
307
|
+
const ordinate = conversion.xAxisOrdinate ?? 0.0;
|
|
308
|
+
|
|
309
|
+
const shift = coordinateInfo.originShift;
|
|
310
|
+
const rtc = coordinateInfo.wasmRtcOffset;
|
|
311
|
+
const rtcYup = rtc
|
|
312
|
+
? { x: rtc.x, z: -rtc.y }
|
|
313
|
+
: { x: 0, z: 0 };
|
|
314
|
+
|
|
315
|
+
const bounds = coordinateInfo.shiftedBounds;
|
|
316
|
+
|
|
317
|
+
// Four corners of the bounding box on the XZ plane (viewer Y-up)
|
|
318
|
+
const corners = [
|
|
319
|
+
{ x: bounds.min.x, z: bounds.min.z },
|
|
320
|
+
{ x: bounds.max.x, z: bounds.min.z },
|
|
321
|
+
{ x: bounds.max.x, z: bounds.max.z },
|
|
322
|
+
{ x: bounds.min.x, z: bounds.max.z },
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
const ring: [number, number][] = [];
|
|
326
|
+
|
|
327
|
+
for (const c of corners) {
|
|
328
|
+
// Scene-local → world Y-up
|
|
329
|
+
const worldX = c.x + shift.x + rtcYup.x;
|
|
330
|
+
const worldZ = c.z + shift.z + rtcYup.z;
|
|
331
|
+
|
|
332
|
+
// Y-up → IFC Z-up: ifcX = worldX, ifcY = -worldZ
|
|
333
|
+
const ifcX = worldX;
|
|
334
|
+
const ifcY = -worldZ;
|
|
335
|
+
|
|
336
|
+
// MapConversion: local IFC → projected CRS
|
|
337
|
+
const easting = conversion.eastings + scale * (abscissa * ifcX - ordinate * ifcY);
|
|
338
|
+
const northing = conversion.northings + scale * (ordinate * ifcX + abscissa * ifcY);
|
|
339
|
+
|
|
340
|
+
// Projected CRS → WGS84
|
|
341
|
+
try {
|
|
342
|
+
const [lon, lat] = proj4(projDef, 'WGS84', [easting, northing]);
|
|
343
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null;
|
|
344
|
+
ring.push([lon, lat]);
|
|
345
|
+
} catch {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Close the ring (GeoJSON requirement)
|
|
351
|
+
ring.push(ring[0]);
|
|
352
|
+
return ring;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Query terrain elevation at a given lat/lon using the Open-Meteo elevation API.
|
|
357
|
+
* Returns height in metres above sea level, or null on failure.
|
|
358
|
+
*/
|
|
359
|
+
export async function queryTerrainElevation(latLon: LatLon): Promise<number | null> {
|
|
360
|
+
try {
|
|
361
|
+
const url = `https://api.open-meteo.com/v1/elevation?latitude=${latLon.lat}&longitude=${latLon.lon}`;
|
|
362
|
+
const resp = await fetch(url);
|
|
363
|
+
if (!resp.ok) return null;
|
|
364
|
+
const data = await resp.json();
|
|
365
|
+
const elev = data?.elevation?.[0];
|
|
366
|
+
return typeof elev === 'number' && Number.isFinite(elev) ? elev : null;
|
|
367
|
+
} catch {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
}
|
package/src/lib/recent-files.ts
CHANGED
|
@@ -103,7 +103,8 @@ export async function cacheFileBlobs(files: File[]): Promise<void> {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
/** Retrieve a cached file blob and reconstruct a File object. */
|
|
106
|
-
export async function getCachedFile(
|
|
106
|
+
export async function getCachedFile(target: string | RecentFileEntry): Promise<File | null> {
|
|
107
|
+
const name = typeof target === 'string' ? target : target.name;
|
|
107
108
|
try {
|
|
108
109
|
const db = await openDB();
|
|
109
110
|
const tx = db.transaction(STORE_NAME, 'readonly');
|
|
@@ -0,0 +1,125 @@
|
|
|
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
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
export type AnalysisExtensionPlacement = 'right' | 'bottom';
|
|
8
|
+
|
|
9
|
+
export interface AnalysisExtensionRenderProps {
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AnalysisExtensionDefinition {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
placement?: AnalysisExtensionPlacement;
|
|
18
|
+
icon: ComponentType<{ className?: string }>;
|
|
19
|
+
renderPanel: (props: AnalysisExtensionRenderProps) => ReactNode;
|
|
20
|
+
onBeforeOpen?: () => boolean | void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AnalysisExtensionsSnapshot {
|
|
24
|
+
activeId: string | null;
|
|
25
|
+
extensions: AnalysisExtensionDefinition[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const listeners = new Set<() => void>();
|
|
29
|
+
const extensions = new Map<string, AnalysisExtensionDefinition>();
|
|
30
|
+
|
|
31
|
+
let activeId: string | null = null;
|
|
32
|
+
let snapshot: AnalysisExtensionsSnapshot = {
|
|
33
|
+
activeId,
|
|
34
|
+
extensions: [],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function rebuildSnapshot(): void {
|
|
38
|
+
snapshot = {
|
|
39
|
+
activeId,
|
|
40
|
+
extensions: Array.from(extensions.values()),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function emit(): void {
|
|
45
|
+
rebuildSnapshot();
|
|
46
|
+
listeners.forEach((listener) => listener());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function setActiveId(nextActiveId: string | null): void {
|
|
50
|
+
if (activeId === nextActiveId) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
activeId = nextActiveId;
|
|
54
|
+
emit();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function canOpen(definition: AnalysisExtensionDefinition): boolean {
|
|
58
|
+
return definition.onBeforeOpen?.() !== false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function subscribeAnalysisExtensions(listener: () => void): () => void {
|
|
62
|
+
listeners.add(listener);
|
|
63
|
+
return () => {
|
|
64
|
+
listeners.delete(listener);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getAnalysisExtensionsSnapshot(): AnalysisExtensionsSnapshot {
|
|
69
|
+
return snapshot;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getAnalysisExtensionById(id: string | null | undefined): AnalysisExtensionDefinition | null {
|
|
73
|
+
if (!id) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return extensions.get(id) ?? null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function closeActiveAnalysisExtension(): void {
|
|
80
|
+
setActiveId(null);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function openAnalysisExtension(id: string): boolean {
|
|
84
|
+
const definition = extensions.get(id);
|
|
85
|
+
if (!definition) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (!canOpen(definition)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
setActiveId(id);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function toggleAnalysisExtension(id: string): boolean {
|
|
96
|
+
if (activeId === id) {
|
|
97
|
+
closeActiveAnalysisExtension();
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return openAnalysisExtension(id);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function registerAnalysisExtensions(definitions: AnalysisExtensionDefinition[]): () => void {
|
|
104
|
+
const nextEntries = definitions.map((definition) => [definition.id, definition] as const);
|
|
105
|
+
nextEntries.forEach(([id, definition]) => {
|
|
106
|
+
extensions.set(id, definition);
|
|
107
|
+
});
|
|
108
|
+
emit();
|
|
109
|
+
|
|
110
|
+
return () => {
|
|
111
|
+
let shouldEmit = false;
|
|
112
|
+
for (const [id] of nextEntries) {
|
|
113
|
+
if (extensions.delete(id)) {
|
|
114
|
+
shouldEmit = true;
|
|
115
|
+
}
|
|
116
|
+
if (activeId === id) {
|
|
117
|
+
activeId = null;
|
|
118
|
+
shouldEmit = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (shouldEmit) {
|
|
122
|
+
emit();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
export function navigateToPath(path: string, options: { replace?: boolean } = {}): void {
|
|
6
|
+
const { replace = false } = options;
|
|
7
|
+
if (replace) {
|
|
8
|
+
window.history.replaceState({}, '', path);
|
|
9
|
+
} else {
|
|
10
|
+
window.history.pushState({}, '', path);
|
|
11
|
+
}
|
|
12
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
13
|
+
}
|