@ifc-lite/viewer 1.17.3 → 1.17.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +39 -30
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +132 -0
- package/DESKTOP_CONTRACT_VERSION +1 -0
- package/dist/assets/arrow-CZ5kQ26f.js +20 -0
- package/dist/assets/basketViewActivator-86rgogji.js +1 -0
- package/dist/assets/{bcf-D5-QWGO9.js → bcf-DOG9_WPX.js} +1 -1
- package/dist/assets/{browser-CKs-FY1P.js → browser-C5TFR7sH.js} +1 -1
- package/dist/assets/cesium-ADbP7waU.css +1 -0
- package/dist/assets/cesium-DUOzBlqv.js +17817 -0
- package/dist/assets/drawing-2d-DoxKMqbO.js +257 -0
- package/dist/assets/{exporters-C_6J153K.js → exporters-CcPS9MK5.js} +2898 -2380
- package/dist/assets/geometry.worker-BFUYA08u.js +1 -0
- package/dist/assets/ids-DQ5jY0E8.js +1 -0
- package/dist/assets/ifc-lite_bg-BINvzoCP.wasm +0 -0
- package/dist/assets/{index-jhBr1wbn.js → index-Bfms9I4A.js} +41036 -34613
- package/dist/assets/index-_bfZsDCC.css +1 -0
- package/dist/assets/{maplibre-gl-BpvwNKKy.js → maplibre-gl-CGLcoNXc.js} +1 -1
- package/dist/assets/native-bridge-DUyLCMZS.js +429 -0
- package/dist/assets/{sandbox-B79eavQ3.js → sandbox-C8575tul.js} +4342 -4324
- package/dist/assets/{server-client-D3bUPJJc.js → server-client-BuZK7OST.js} +4 -4
- package/dist/assets/tauri-core-stub-D8Fa-u43.js +1 -0
- package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +1 -0
- package/dist/assets/tauri-fs-stub-BdeRC7aK.js +1 -0
- package/dist/assets/wasm-bridge-JsqEGDV8.js +1 -0
- package/dist/assets/{zip-B-jFFAGa.js → zip-DBEtpeu6.js} +3 -3
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json +1 -0
- package/dist/cesium/Assets/Images/bing_maps_credit.png +0 -0
- package/dist/cesium/Assets/Images/cesium_credit.png +0 -0
- package/dist/cesium/Assets/Images/google_earth_credit.png +0 -0
- package/dist/cesium/Assets/Images/ion-credit.png +0 -0
- package/dist/cesium/Assets/Textures/LensFlare/DirtMask.jpg +0 -0
- package/dist/cesium/Assets/Textures/LensFlare/StarBurst.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/0/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/0/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/tilemapresource.xml +14 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mx.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_my.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mz.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_px.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_py.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_pz.jpg +0 -0
- package/dist/cesium/Assets/Textures/maki/airfield.png +0 -0
- package/dist/cesium/Assets/Textures/maki/airport.png +0 -0
- package/dist/cesium/Assets/Textures/maki/alcohol-shop.png +0 -0
- package/dist/cesium/Assets/Textures/maki/america-football.png +0 -0
- package/dist/cesium/Assets/Textures/maki/art-gallery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bakery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bank.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bar.png +0 -0
- package/dist/cesium/Assets/Textures/maki/baseball.png +0 -0
- package/dist/cesium/Assets/Textures/maki/basketball.png +0 -0
- package/dist/cesium/Assets/Textures/maki/beer.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bicycle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/building.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bus.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cafe.png +0 -0
- package/dist/cesium/Assets/Textures/maki/camera.png +0 -0
- package/dist/cesium/Assets/Textures/maki/campsite.png +0 -0
- package/dist/cesium/Assets/Textures/maki/car.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cemetery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cesium.png +0 -0
- package/dist/cesium/Assets/Textures/maki/chemist.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cinema.png +0 -0
- package/dist/cesium/Assets/Textures/maki/circle-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/circle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/city.png +0 -0
- package/dist/cesium/Assets/Textures/maki/clothing-store.png +0 -0
- package/dist/cesium/Assets/Textures/maki/college.png +0 -0
- package/dist/cesium/Assets/Textures/maki/commercial.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cricket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cross.png +0 -0
- package/dist/cesium/Assets/Textures/maki/dam.png +0 -0
- package/dist/cesium/Assets/Textures/maki/danger.png +0 -0
- package/dist/cesium/Assets/Textures/maki/disability.png +0 -0
- package/dist/cesium/Assets/Textures/maki/dog-park.png +0 -0
- package/dist/cesium/Assets/Textures/maki/embassy.png +0 -0
- package/dist/cesium/Assets/Textures/maki/emergency-telephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/entrance.png +0 -0
- package/dist/cesium/Assets/Textures/maki/farm.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fast-food.png +0 -0
- package/dist/cesium/Assets/Textures/maki/ferry.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fire-station.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fuel.png +0 -0
- package/dist/cesium/Assets/Textures/maki/garden.png +0 -0
- package/dist/cesium/Assets/Textures/maki/gift.png +0 -0
- package/dist/cesium/Assets/Textures/maki/golf.png +0 -0
- package/dist/cesium/Assets/Textures/maki/grocery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/hairdresser.png +0 -0
- package/dist/cesium/Assets/Textures/maki/harbor.png +0 -0
- package/dist/cesium/Assets/Textures/maki/heart.png +0 -0
- package/dist/cesium/Assets/Textures/maki/heliport.png +0 -0
- package/dist/cesium/Assets/Textures/maki/hospital.png +0 -0
- package/dist/cesium/Assets/Textures/maki/ice-cream.png +0 -0
- package/dist/cesium/Assets/Textures/maki/industrial.png +0 -0
- package/dist/cesium/Assets/Textures/maki/land-use.png +0 -0
- package/dist/cesium/Assets/Textures/maki/laundry.png +0 -0
- package/dist/cesium/Assets/Textures/maki/library.png +0 -0
- package/dist/cesium/Assets/Textures/maki/lighthouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/lodging.png +0 -0
- package/dist/cesium/Assets/Textures/maki/logging.png +0 -0
- package/dist/cesium/Assets/Textures/maki/london-underground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/marker-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/marker.png +0 -0
- package/dist/cesium/Assets/Textures/maki/minefield.png +0 -0
- package/dist/cesium/Assets/Textures/maki/mobilephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/monument.png +0 -0
- package/dist/cesium/Assets/Textures/maki/museum.png +0 -0
- package/dist/cesium/Assets/Textures/maki/music.png +0 -0
- package/dist/cesium/Assets/Textures/maki/oil-well.png +0 -0
- package/dist/cesium/Assets/Textures/maki/park.png +0 -0
- package/dist/cesium/Assets/Textures/maki/park2.png +0 -0
- package/dist/cesium/Assets/Textures/maki/parking-garage.png +0 -0
- package/dist/cesium/Assets/Textures/maki/parking.png +0 -0
- package/dist/cesium/Assets/Textures/maki/pharmacy.png +0 -0
- package/dist/cesium/Assets/Textures/maki/pitch.png +0 -0
- package/dist/cesium/Assets/Textures/maki/place-of-worship.png +0 -0
- package/dist/cesium/Assets/Textures/maki/playground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/police.png +0 -0
- package/dist/cesium/Assets/Textures/maki/polling-place.png +0 -0
- package/dist/cesium/Assets/Textures/maki/post.png +0 -0
- package/dist/cesium/Assets/Textures/maki/prison.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-above.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-light.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-metro.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-underground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-christian.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-jewish.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-muslim.png +0 -0
- package/dist/cesium/Assets/Textures/maki/restaurant.png +0 -0
- package/dist/cesium/Assets/Textures/maki/roadblock.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rocket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/school.png +0 -0
- package/dist/cesium/Assets/Textures/maki/scooter.png +0 -0
- package/dist/cesium/Assets/Textures/maki/shop.png +0 -0
- package/dist/cesium/Assets/Textures/maki/skiing.png +0 -0
- package/dist/cesium/Assets/Textures/maki/slaughterhouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/soccer.png +0 -0
- package/dist/cesium/Assets/Textures/maki/square-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/square.png +0 -0
- package/dist/cesium/Assets/Textures/maki/star-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/star.png +0 -0
- package/dist/cesium/Assets/Textures/maki/suitcase.png +0 -0
- package/dist/cesium/Assets/Textures/maki/swimming.png +0 -0
- package/dist/cesium/Assets/Textures/maki/telephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/tennis.png +0 -0
- package/dist/cesium/Assets/Textures/maki/theatre.png +0 -0
- package/dist/cesium/Assets/Textures/maki/toilets.png +0 -0
- package/dist/cesium/Assets/Textures/maki/town-hall.png +0 -0
- package/dist/cesium/Assets/Textures/maki/town.png +0 -0
- package/dist/cesium/Assets/Textures/maki/triangle-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/triangle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/village.png +0 -0
- package/dist/cesium/Assets/Textures/maki/warehouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/waste-basket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/water.png +0 -0
- package/dist/cesium/Assets/Textures/maki/wetland.png +0 -0
- package/dist/cesium/Assets/Textures/maki/zoo.png +0 -0
- package/dist/cesium/Assets/Textures/moonSmall.jpg +0 -0
- package/dist/cesium/Assets/Textures/pin.svg +1 -0
- package/dist/cesium/Assets/Textures/waterNormals.jpg +0 -0
- package/dist/cesium/Assets/Textures/waterNormalsSmall.jpg +0 -0
- package/dist/cesium/Assets/approximateTerrainHeights.json +1 -0
- package/dist/cesium/ThirdParty/Workers/package.json +1 -0
- package/dist/cesium/ThirdParty/Workers/zip-web-worker.js +1 -0
- package/dist/cesium/ThirdParty/basis_transcoder.wasm +0 -0
- package/dist/cesium/ThirdParty/draco_decoder.wasm +0 -0
- package/dist/cesium/ThirdParty/google-earth-dbroot-parser.js +1 -0
- package/dist/cesium/ThirdParty/wasm_splats_bg.wasm +0 -0
- package/dist/cesium/ThirdParty/zip-module.wasm +0 -0
- package/dist/cesium/Widgets/Animation/Animation.css +127 -0
- package/dist/cesium/Widgets/Animation/lighter.css +70 -0
- package/dist/cesium/Widgets/BaseLayerPicker/BaseLayerPicker.css +108 -0
- package/dist/cesium/Widgets/BaseLayerPicker/lighter.css +22 -0
- package/dist/cesium/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css +102 -0
- package/dist/cesium/Widgets/CesiumInspector/CesiumInspector.css +113 -0
- package/dist/cesium/Widgets/CesiumWidget/CesiumWidget.css +119 -0
- package/dist/cesium/Widgets/CesiumWidget/lighter.css +14 -0
- package/dist/cesium/Widgets/FullscreenButton/FullscreenButton.css +8 -0
- package/dist/cesium/Widgets/Geocoder/Geocoder.css +70 -0
- package/dist/cesium/Widgets/Geocoder/lighter.css +17 -0
- package/dist/cesium/Widgets/I3SBuildingSceneLayerExplorer/I3SBuildingSceneLayerExplorer.css +27 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldHillshade.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldImagery.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldOcean.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/azureAerial.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/azureRoads.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingAerial.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingAerialLabels.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingRoads.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/blueMarble.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/earthAtNight.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleContour.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleRoadmap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleSatellite.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleSatelliteLabels.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapQuestOpenStreetMap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxSatellite.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxStreets.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxTerrain.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/naturalEarthII.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/openStreetMap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/sentinel-2.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmooth.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmoothDark.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stamenToner.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stamenWatercolor.png +0 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/Mouse.svg +84 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseLeft.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseMiddle.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseRight.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/Touch.svg +120 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchDrag.svg +129 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchRotate.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchTilt.svg +135 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchZoom.svg +74 -0
- package/dist/cesium/Widgets/Images/TerrainProviders/CesiumWorldTerrain.png +0 -0
- package/dist/cesium/Widgets/Images/TerrainProviders/Ellipsoid.png +0 -0
- package/dist/cesium/Widgets/Images/TimelineIcons.png +0 -0
- package/dist/cesium/Widgets/Images/info-loading.gif +0 -0
- package/dist/cesium/Widgets/InfoBox/InfoBox.css +92 -0
- package/dist/cesium/Widgets/InfoBox/InfoBoxDescription.css +178 -0
- package/dist/cesium/Widgets/NavigationHelpButton/NavigationHelpButton.css +93 -0
- package/dist/cesium/Widgets/NavigationHelpButton/lighter.css +38 -0
- package/dist/cesium/Widgets/PerformanceWatchdog/PerformanceWatchdog.css +15 -0
- package/dist/cesium/Widgets/ProjectionPicker/ProjectionPicker.css +38 -0
- package/dist/cesium/Widgets/SceneModePicker/SceneModePicker.css +56 -0
- package/dist/cesium/Widgets/SelectionIndicator/SelectionIndicator.css +20 -0
- package/dist/cesium/Widgets/Timeline/Timeline.css +103 -0
- package/dist/cesium/Widgets/Timeline/lighter.css +23 -0
- package/dist/cesium/Widgets/VRButton/VRButton.css +8 -0
- package/dist/cesium/Widgets/Viewer/Viewer.css +107 -0
- package/dist/cesium/Widgets/VoxelInspector/VoxelInspector.css +16 -0
- package/dist/cesium/Widgets/lighter.css +237 -0
- package/dist/cesium/Widgets/lighterShared.css +46 -0
- package/dist/cesium/Widgets/shared.css +103 -0
- package/dist/cesium/Widgets/widgets.css +1342 -0
- package/dist/cesium/Workers/chunk-23ZQ2IVV.js +29 -0
- package/dist/cesium/Workers/chunk-2EQO3Q56.js +26 -0
- package/dist/cesium/Workers/chunk-2MJIIVP4.js +26 -0
- package/dist/cesium/Workers/chunk-2TE5NTVD.js +26 -0
- package/dist/cesium/Workers/chunk-2ZBHLJST.js +26 -0
- package/dist/cesium/Workers/chunk-5TJMAQVL.js +26 -0
- package/dist/cesium/Workers/chunk-6BD4U3VO.js +26 -0
- package/dist/cesium/Workers/chunk-7TVGLKQF.js +26 -0
- package/dist/cesium/Workers/chunk-BTSYJ5XU.js +26 -0
- package/dist/cesium/Workers/chunk-BXMEEOCS.js +63 -0
- package/dist/cesium/Workers/chunk-BYLCY7GP.js +29 -0
- package/dist/cesium/Workers/chunk-CTHM3W6I.js +26 -0
- package/dist/cesium/Workers/chunk-CUUSNIVQ.js +26 -0
- package/dist/cesium/Workers/chunk-E3JOOS3S.js +26 -0
- package/dist/cesium/Workers/chunk-E7KYDCM5.js +26 -0
- package/dist/cesium/Workers/chunk-EDVBB7SS.js +27 -0
- package/dist/cesium/Workers/chunk-EFBN7QNX.js +26 -0
- package/dist/cesium/Workers/chunk-EQ4YRVWL.js +26 -0
- package/dist/cesium/Workers/chunk-F6PRE7D6.js +26 -0
- package/dist/cesium/Workers/chunk-FC4ZZ65J.js +26 -0
- package/dist/cesium/Workers/chunk-FFBVWF2L.js +26 -0
- package/dist/cesium/Workers/chunk-GBAA6GVX.js +26 -0
- package/dist/cesium/Workers/chunk-ICALLYLG.js +26 -0
- package/dist/cesium/Workers/chunk-ILRYTWTP.js +26 -0
- package/dist/cesium/Workers/chunk-IRNLBSEJ.js +26 -0
- package/dist/cesium/Workers/chunk-IX4VMHEV.js +26 -0
- package/dist/cesium/Workers/chunk-L6QHHACZ.js +26 -0
- package/dist/cesium/Workers/chunk-LI2ZSORM.js +26 -0
- package/dist/cesium/Workers/chunk-LSLE2RL4.js +26 -0
- package/dist/cesium/Workers/chunk-M4HLDBCG.js +26 -0
- package/dist/cesium/Workers/chunk-MJHHSGEH.js +26 -0
- package/dist/cesium/Workers/chunk-NMVKML6W.js +26 -0
- package/dist/cesium/Workers/chunk-OCWJRAXS.js +26 -0
- package/dist/cesium/Workers/chunk-OIRKANTH.js +26 -0
- package/dist/cesium/Workers/chunk-OIT7J4IC.js +26 -0
- package/dist/cesium/Workers/chunk-OLZ3FYUM.js +26 -0
- package/dist/cesium/Workers/chunk-Q5BPHJQF.js +26 -0
- package/dist/cesium/Workers/chunk-QFM5DCMQ.js +26 -0
- package/dist/cesium/Workers/chunk-QKUIYMGC.js +28 -0
- package/dist/cesium/Workers/chunk-S44JILQT.js +26 -0
- package/dist/cesium/Workers/chunk-SLT4J352.js +26 -0
- package/dist/cesium/Workers/chunk-SQMIIXB7.js +26 -0
- package/dist/cesium/Workers/chunk-TJ4XLGBQ.js +26 -0
- package/dist/cesium/Workers/chunk-TNSUQXWK.js +27 -0
- package/dist/cesium/Workers/chunk-UBOGZS7F.js +26 -0
- package/dist/cesium/Workers/chunk-V3OSTMM6.js +26 -0
- package/dist/cesium/Workers/chunk-V7QEYVP3.js +26 -0
- package/dist/cesium/Workers/chunk-VUKYSU4H.js +26 -0
- package/dist/cesium/Workers/chunk-W37FE5GR.js +26 -0
- package/dist/cesium/Workers/chunk-WBOV35NL.js +26 -0
- package/dist/cesium/Workers/chunk-WPMZLB3Y.js +26 -0
- package/dist/cesium/Workers/chunk-WWWZVEEH.js +26 -0
- package/dist/cesium/Workers/chunk-XFIQ5DEQ.js +28 -0
- package/dist/cesium/Workers/chunk-XQHLGIO7.js +26 -0
- package/dist/cesium/Workers/chunk-XUSCFAVF.js +26 -0
- package/dist/cesium/Workers/chunk-YP7I5QBZ.js +26 -0
- package/dist/cesium/Workers/chunk-Z3QF2EHT.js +26 -0
- package/dist/cesium/Workers/combineGeometry.js +26 -0
- package/dist/cesium/Workers/createBoxGeometry.js +26 -0
- package/dist/cesium/Workers/createBoxOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCircleGeometry.js +26 -0
- package/dist/cesium/Workers/createCircleOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCoplanarPolygonGeometry.js +26 -0
- package/dist/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCorridorGeometry.js +26 -0
- package/dist/cesium/Workers/createCorridorOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCylinderGeometry.js +26 -0
- package/dist/cesium/Workers/createCylinderOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipseGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipseOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipsoidGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipsoidOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createFrustumGeometry.js +26 -0
- package/dist/cesium/Workers/createFrustumOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createGeometry.js +26 -0
- package/dist/cesium/Workers/createGroundPolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createPlaneGeometry.js +26 -0
- package/dist/cesium/Workers/createPlaneOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolygonGeometry.js +26 -0
- package/dist/cesium/Workers/createPolygonOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineVolumeGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineVolumeOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createRectangleGeometry.js +26 -0
- package/dist/cesium/Workers/createRectangleOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createSimplePolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createSphereGeometry.js +26 -0
- package/dist/cesium/Workers/createSphereOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createTaskProcessorWorker.js +26 -0
- package/dist/cesium/Workers/createVectorTileClampedPolylines.js +26 -0
- package/dist/cesium/Workers/createVectorTileGeometries.js +26 -0
- package/dist/cesium/Workers/createVectorTilePoints.js +26 -0
- package/dist/cesium/Workers/createVectorTilePolygons.js +26 -0
- package/dist/cesium/Workers/createVectorTilePolylines.js +26 -0
- package/dist/cesium/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -0
- package/dist/cesium/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +26 -0
- package/dist/cesium/Workers/createVerticesFromHeightmap.js +26 -0
- package/dist/cesium/Workers/createVerticesFromQuantizedTerrainMesh.js +26 -0
- package/dist/cesium/Workers/createWallGeometry.js +26 -0
- package/dist/cesium/Workers/createWallOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/decodeDraco.js +26 -0
- package/dist/cesium/Workers/decodeGoogleEarthEnterprisePacket.js +26 -0
- package/dist/cesium/Workers/decodeI3S.js +26 -0
- package/dist/cesium/Workers/gaussianSplatSorter.js +26 -0
- package/dist/cesium/Workers/gaussianSplatTextureGenerator.js +26 -0
- package/dist/cesium/Workers/incrementallyBuildTerrainPicker.js +26 -0
- package/dist/cesium/Workers/transcodeKTX2.js +56 -0
- package/dist/cesium/Workers/transferTypedArrayTest.js +26 -0
- package/dist/cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
- package/dist/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -0
- package/dist/index.html +13 -10
- package/index.html +1 -0
- package/package.json +18 -15
- package/src/App.tsx +7 -9
- package/src/components/viewer/BCFPanel.tsx +46 -4
- package/src/components/viewer/CesiumOverlay.tsx +715 -0
- package/src/components/viewer/CesiumSettingsDialog.tsx +100 -0
- package/src/components/viewer/ChatPanel.tsx +232 -90
- package/src/components/viewer/CommandPalette.tsx +6 -1
- package/src/components/viewer/DesktopEntitlementBanner.tsx +74 -0
- package/src/components/viewer/ExportChangesButton.tsx +6 -1
- package/src/components/viewer/ExportDialog.tsx +22 -6
- package/src/components/viewer/HierarchyPanel.tsx +196 -0
- package/src/components/viewer/IDSPanel.tsx +52 -3
- package/src/components/viewer/KeyboardShortcutsDialog.tsx +1 -1
- package/src/components/viewer/MainToolbar.tsx +355 -28
- package/src/components/viewer/PropertiesPanel.tsx +234 -81
- package/src/components/viewer/ScriptPanel.tsx +34 -8
- package/src/components/viewer/SettingsPage.tsx +581 -0
- package/src/components/viewer/StatusBar.tsx +17 -1
- package/src/components/viewer/ThemeSwitch.tsx +63 -7
- package/src/components/viewer/ViewerLayout.tsx +48 -6
- package/src/components/viewer/Viewport.tsx +61 -8
- package/src/components/viewer/ViewportContainer.tsx +265 -28
- package/src/components/viewer/ViewportOverlays.tsx +132 -27
- package/src/components/viewer/bcf/BCFTopicDetail.tsx +4 -4
- package/src/components/viewer/chat/ModelSelector.tsx +90 -54
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +229 -55
- package/src/components/viewer/properties/LocationMap.tsx +462 -19
- package/src/components/viewer/properties/ModelMetadataPanel.tsx +1 -1
- package/src/components/viewer/selectionHandlers.ts +4 -3
- package/src/components/viewer/tools/SectionCapControls.tsx +237 -0
- package/src/components/viewer/tools/SectionPanel.tsx +39 -18
- package/src/components/viewer/useAnimationLoop.ts +13 -1
- package/src/components/viewer/useGeometryStreaming.ts +127 -40
- package/src/components/viewer/useMouseControls.ts +4 -1
- package/src/components/viewer/useRenderUpdates.ts +1 -1
- package/src/hooks/ids/idsDataAccessor.ts +60 -24
- package/src/hooks/ingest/viewerModelIngest.ts +280 -0
- package/src/hooks/useIDS.ts +1 -1
- package/src/hooks/useIfc.ts +7 -1
- package/src/hooks/useIfcCache.ts +28 -15
- package/src/hooks/useIfcFederation.ts +378 -291
- package/src/hooks/useIfcLoader.ts +1657 -130
- package/src/hooks/useIfcServer.ts +0 -69
- package/src/hooks/useViewControls.ts +13 -5
- package/src/index.css +484 -10
- package/src/lib/desktop/desktopEntitlementEvents.ts +39 -0
- package/src/lib/desktop-entitlement.ts +43 -0
- package/src/lib/desktop-product.ts +124 -0
- package/src/lib/geo/cesium-bridge.ts +318 -0
- package/src/lib/geo/effective-georef.test.ts +73 -0
- package/src/lib/geo/effective-georef.ts +111 -0
- package/src/lib/geo/reproject.ts +249 -37
- package/src/lib/llm/byok-guard.test.ts +77 -0
- package/src/lib/llm/byok-guard.ts +39 -0
- package/src/lib/llm/free-models.test.ts +0 -6
- package/src/lib/llm/models.ts +104 -42
- package/src/lib/llm/stream-client.ts +74 -110
- package/src/lib/llm/stream-direct.test.ts +130 -0
- package/src/lib/llm/stream-direct.ts +316 -0
- package/src/lib/llm/types.ts +14 -2
- package/src/lib/recent-files.ts +2 -1
- package/src/main.tsx +1 -10
- package/src/services/analysis-extensions.ts +125 -0
- package/src/services/api-keys.ts +73 -0
- package/src/services/app-navigation.ts +13 -0
- package/src/services/bsdd.ts +53 -4
- package/src/services/cacheService.ts +1 -1
- package/src/services/desktop-cache.ts +43 -0
- package/src/services/desktop-export.ts +77 -0
- package/src/services/desktop-harness.ts +196 -0
- package/src/services/desktop-logger.ts +20 -0
- package/src/services/desktop-native-metadata.ts +207 -0
- package/src/services/desktop-panel-actions.ts +43 -0
- package/src/services/desktop-preferences.ts +44 -0
- package/src/services/file-dialog.ts +147 -0
- package/src/services/tauri-core-stub.ts +7 -0
- package/src/services/tauri-dialog-stub.ts +7 -0
- package/src/services/tauri-fs-stub.ts +7 -0
- package/src/store/constants.ts +20 -2
- package/src/store/index.ts +52 -7
- package/src/store/slices/cesiumSlice.ts +127 -0
- package/src/store/slices/chatSlice.test.ts +6 -76
- package/src/store/slices/chatSlice.ts +21 -58
- package/src/store/slices/dataSlice.ts +139 -28
- package/src/store/slices/desktopEntitlementSlice.ts +86 -0
- package/src/store/slices/loadingSlice.ts +14 -2
- package/src/store/slices/modelSlice.ts +58 -3
- package/src/store/slices/sectionSlice.test.ts +87 -7
- package/src/store/slices/sectionSlice.ts +151 -5
- package/src/store/slices/uiSlice.ts +28 -5
- package/src/store/types.ts +122 -2
- package/src/store.ts +1 -1
- package/src/utils/desktopModelSnapshot.ts +358 -0
- package/src/utils/ifcConfig.ts +6 -1
- package/src/utils/nativeSpatialDataStore.ts +253 -0
- package/src/utils/serverDataModel.ts +4 -0
- package/src/utils/spatialHierarchy.ts +10 -11
- package/src/utils/viewportUtils.ts +7 -2
- package/src/vite-env.d.ts +0 -4
- package/vite.config.ts +24 -0
- package/dist/assets/arrow-DJf2ErbF.js +0 -20
- package/dist/assets/basketViewActivator-aojwdomq.js +0 -1
- package/dist/assets/desktop-cache-oPzaWXYE.js +0 -1
- package/dist/assets/drawing-2d-gWfpdfYe.js +0 -257
- package/dist/assets/geometry.worker-Nz9_YIqh.js +0 -1
- package/dist/assets/ids-B4jTqB1O.js +0 -1
- package/dist/assets/ifc-lite_bg-eSkBTizQ.wasm +0 -0
- package/dist/assets/index-pbE7itQS.css +0 -1
- package/dist/assets/native-bridge-DSIyEYXG.js +0 -113
- package/dist/assets/wasm-bridge-B0J07fZZ.js +0 -1
- package/src/components/viewer/UpgradePage.tsx +0 -69
- package/src/lib/llm/ClerkChatSync.tsx +0 -74
- package/src/lib/llm/clerk-auth.ts +0 -62
|
@@ -13,19 +13,304 @@
|
|
|
13
13
|
import { useCallback } from 'react';
|
|
14
14
|
import { useShallow } from 'zustand/react/shallow';
|
|
15
15
|
import { useViewerStore, type FederatedModel, type SchemaVersion } from '../store.js';
|
|
16
|
-
import {
|
|
17
|
-
|
|
16
|
+
import {
|
|
17
|
+
detectFormat,
|
|
18
|
+
parseFederatedIfcx,
|
|
19
|
+
type IfcDataStore,
|
|
20
|
+
type FederatedIfcxParseResult,
|
|
21
|
+
type MapConversion,
|
|
22
|
+
type ProjectedCRS,
|
|
23
|
+
} from '@ifc-lite/parser';
|
|
24
|
+
import type { CoordinateInfo, MeshData } from '@ifc-lite/geometry';
|
|
18
25
|
import { IfcQuery } from '@ifc-lite/query';
|
|
19
26
|
import { buildSpatialIndexGuarded } from '../utils/loadingUtils.js';
|
|
20
|
-
import { loadGLBToMeshData } from '@ifc-lite/cache';
|
|
21
|
-
|
|
22
27
|
import { getDynamicBatchConfig } from '../utils/ifcConfig.js';
|
|
28
|
+
import { calculateMeshBounds, createCoordinateInfo } from '../utils/localParsingUtils.js';
|
|
23
29
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
convertIfcxMeshes,
|
|
31
|
+
getMaxExpressId,
|
|
32
|
+
parseGlbViewerModel,
|
|
33
|
+
parseIfcxViewerModel,
|
|
34
|
+
parseStepBufferViewerModel,
|
|
35
|
+
} from './ingest/viewerModelIngest.js';
|
|
36
|
+
import { readNativeFile, type NativeFileHandle } from '../services/file-dialog.js';
|
|
37
|
+
import { getEffectiveGeoreference, type GeorefMutationDataLike } from '../lib/geo/effective-georef.js';
|
|
38
|
+
|
|
39
|
+
function isNativeFileHandle(file: File | NativeFileHandle): file is NativeFileHandle {
|
|
40
|
+
return typeof (file as NativeFileHandle).path === 'string';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function toExactArrayBuffer(bytes: Uint8Array): ArrayBuffer {
|
|
44
|
+
if (bytes.buffer instanceof ArrayBuffer && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) {
|
|
45
|
+
return bytes.buffer;
|
|
46
|
+
}
|
|
47
|
+
return bytes.slice().buffer;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type FederatedGeometryResult = NonNullable<FederatedModel['geometryResult']>;
|
|
51
|
+
|
|
52
|
+
interface ModelGeoref {
|
|
53
|
+
mapConversion: MapConversion;
|
|
54
|
+
projectedCRS: ProjectedCRS;
|
|
55
|
+
lengthUnitScale: number;
|
|
56
|
+
coordinateInfo?: CoordinateInfo;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface AffineTransform3D {
|
|
60
|
+
m00: number;
|
|
61
|
+
m01: number;
|
|
62
|
+
m02: number;
|
|
63
|
+
tx: number;
|
|
64
|
+
m10: number;
|
|
65
|
+
m11: number;
|
|
66
|
+
m12: number;
|
|
67
|
+
ty: number;
|
|
68
|
+
m20: number;
|
|
69
|
+
m21: number;
|
|
70
|
+
m22: number;
|
|
71
|
+
tz: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getMapUnitScale(georef: ModelGeoref): number {
|
|
75
|
+
return georef.projectedCRS.mapUnitScale ?? georef.lengthUnitScale ?? 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getAxis(conversion: MapConversion): { a: number; o: number; scale: number; denom: number } {
|
|
79
|
+
const a = conversion.xAxisAbscissa ?? 1;
|
|
80
|
+
const o = conversion.xAxisOrdinate ?? 0;
|
|
81
|
+
const scale = conversion.scale ?? 1;
|
|
82
|
+
const denom = Math.max(a * a + o * o, 1e-12);
|
|
83
|
+
return { a, o, scale, denom };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function extractModelGeoref(
|
|
87
|
+
dataStore: IfcDataStore,
|
|
88
|
+
coordinateInfo?: CoordinateInfo,
|
|
89
|
+
mutations?: GeorefMutationDataLike,
|
|
90
|
+
): ModelGeoref | null {
|
|
91
|
+
const georef = getEffectiveGeoreference(dataStore, coordinateInfo, mutations);
|
|
92
|
+
if (!georef?.mapConversion || !georef.projectedCRS?.name) return null;
|
|
93
|
+
return {
|
|
94
|
+
mapConversion: georef.mapConversion,
|
|
95
|
+
projectedCRS: georef.projectedCRS,
|
|
96
|
+
lengthUnitScale: georef.lengthUnitScale,
|
|
97
|
+
coordinateInfo,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function crsKey(crs: ProjectedCRS): string {
|
|
102
|
+
return `${crs.name ?? ''}|${crs.geodeticDatum ?? ''}|${crs.mapProjection ?? ''}|${crs.mapZone ?? ''}`.toUpperCase();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function canAlignInSameProjectedCrs(a: ModelGeoref, b: ModelGeoref): boolean {
|
|
106
|
+
return crsKey(a.projectedCRS) === crsKey(b.projectedCRS);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function totalYupOffset(coordinateInfo?: CoordinateInfo): { x: number; y: number; z: number } {
|
|
110
|
+
const shift = coordinateInfo?.originShift ?? { x: 0, y: 0, z: 0 };
|
|
111
|
+
const rtc = coordinateInfo?.wasmRtcOffset;
|
|
112
|
+
const rtcYup = rtc ? { x: rtc.x, y: rtc.z, z: -rtc.y } : { x: 0, y: 0, z: 0 };
|
|
113
|
+
return {
|
|
114
|
+
x: shift.x + rtcYup.x,
|
|
115
|
+
y: shift.y + rtcYup.y,
|
|
116
|
+
z: shift.z + rtcYup.z,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function emptyBounds() {
|
|
121
|
+
return {
|
|
122
|
+
min: { x: Infinity, y: Infinity, z: Infinity },
|
|
123
|
+
max: { x: -Infinity, y: -Infinity, z: -Infinity },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function zeroBounds() {
|
|
128
|
+
return {
|
|
129
|
+
min: { x: 0, y: 0, z: 0 },
|
|
130
|
+
max: { x: 0, y: 0, z: 0 },
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function updateBounds(bounds: ReturnType<typeof emptyBounds>, x: number, y: number, z: number): boolean {
|
|
135
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) return false;
|
|
136
|
+
bounds.min.x = Math.min(bounds.min.x, x);
|
|
137
|
+
bounds.min.y = Math.min(bounds.min.y, y);
|
|
138
|
+
bounds.min.z = Math.min(bounds.min.z, z);
|
|
139
|
+
bounds.max.x = Math.max(bounds.max.x, x);
|
|
140
|
+
bounds.max.y = Math.max(bounds.max.y, y);
|
|
141
|
+
bounds.max.z = Math.max(bounds.max.z, z);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function buildGeorefAlignmentTransform(source: ModelGeoref, reference: ModelGeoref): AffineTransform3D | null {
|
|
146
|
+
const sourceConv = source.mapConversion;
|
|
147
|
+
const refConv = reference.mapConversion;
|
|
148
|
+
const sourceAxis = getAxis(sourceConv);
|
|
149
|
+
const refAxis = getAxis(refConv);
|
|
150
|
+
const refDenom = refAxis.scale * refAxis.denom;
|
|
151
|
+
if (Math.abs(refDenom) < 1e-12) return null;
|
|
152
|
+
|
|
153
|
+
const sourceMapUnitScale = getMapUnitScale(source);
|
|
154
|
+
const refMapUnitScale = getMapUnitScale(reference);
|
|
155
|
+
const sourceOffset = totalYupOffset(source.coordinateInfo);
|
|
156
|
+
const refOffset = totalYupOffset(reference.coordinateInfo);
|
|
157
|
+
|
|
158
|
+
const eVx = sourceAxis.scale * sourceAxis.a;
|
|
159
|
+
const eVz = sourceAxis.scale * sourceAxis.o;
|
|
160
|
+
const eC = sourceConv.eastings * sourceMapUnitScale
|
|
161
|
+
+ sourceAxis.scale * (sourceAxis.a * sourceOffset.x + sourceAxis.o * sourceOffset.z)
|
|
162
|
+
- refConv.eastings * refMapUnitScale;
|
|
163
|
+
|
|
164
|
+
const nVx = sourceAxis.scale * sourceAxis.o;
|
|
165
|
+
const nVz = -sourceAxis.scale * sourceAxis.a;
|
|
166
|
+
const nC = sourceConv.northings * sourceMapUnitScale
|
|
167
|
+
+ sourceAxis.scale * (sourceAxis.o * sourceOffset.x - sourceAxis.a * sourceOffset.z)
|
|
168
|
+
- refConv.northings * refMapUnitScale;
|
|
169
|
+
|
|
170
|
+
const hC = sourceConv.orthogonalHeight * sourceMapUnitScale
|
|
171
|
+
+ sourceOffset.y
|
|
172
|
+
- refConv.orthogonalHeight * refMapUnitScale;
|
|
173
|
+
|
|
174
|
+
const invRefDenom = 1 / refDenom;
|
|
175
|
+
const xVx = (refAxis.a * eVx + refAxis.o * nVx) * invRefDenom;
|
|
176
|
+
const xVz = (refAxis.a * eVz + refAxis.o * nVz) * invRefDenom;
|
|
177
|
+
const xC = (refAxis.a * eC + refAxis.o * nC) * invRefDenom - refOffset.x;
|
|
178
|
+
|
|
179
|
+
const yVx = (-refAxis.o * eVx + refAxis.a * nVx) * invRefDenom;
|
|
180
|
+
const yVz = (-refAxis.o * eVz + refAxis.a * nVz) * invRefDenom;
|
|
181
|
+
const yC = (-refAxis.o * eC + refAxis.a * nC) * invRefDenom;
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
m00: xVx,
|
|
185
|
+
m01: 0,
|
|
186
|
+
m02: xVz,
|
|
187
|
+
tx: xC,
|
|
188
|
+
m10: 0,
|
|
189
|
+
m11: 1,
|
|
190
|
+
m12: 0,
|
|
191
|
+
ty: hC - refOffset.y,
|
|
192
|
+
m20: -yVx,
|
|
193
|
+
m21: 0,
|
|
194
|
+
m22: -yVz,
|
|
195
|
+
tz: -yC - refOffset.z,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function isIdentityTransform(transform: AffineTransform3D): boolean {
|
|
200
|
+
const eps = 1e-7;
|
|
201
|
+
return Math.abs(transform.m00 - 1) < eps
|
|
202
|
+
&& Math.abs(transform.m01) < eps
|
|
203
|
+
&& Math.abs(transform.m02) < eps
|
|
204
|
+
&& Math.abs(transform.tx) < eps
|
|
205
|
+
&& Math.abs(transform.m10) < eps
|
|
206
|
+
&& Math.abs(transform.m11 - 1) < eps
|
|
207
|
+
&& Math.abs(transform.m12) < eps
|
|
208
|
+
&& Math.abs(transform.ty) < eps
|
|
209
|
+
&& Math.abs(transform.m20) < eps
|
|
210
|
+
&& Math.abs(transform.m21) < eps
|
|
211
|
+
&& Math.abs(transform.m22 - 1) < eps
|
|
212
|
+
&& Math.abs(transform.tz) < eps;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function applyAlignmentTransformAndUpdateBounds(
|
|
216
|
+
geometry: FederatedGeometryResult,
|
|
217
|
+
transform: AffineTransform3D,
|
|
218
|
+
referenceInfo?: CoordinateInfo,
|
|
219
|
+
): void {
|
|
220
|
+
const bounds = emptyBounds();
|
|
221
|
+
let found = false;
|
|
222
|
+
|
|
223
|
+
for (const mesh of geometry.meshes) {
|
|
224
|
+
const positions = mesh.positions;
|
|
225
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
226
|
+
const x = positions[i];
|
|
227
|
+
const y = positions[i + 1];
|
|
228
|
+
const z = positions[i + 2];
|
|
229
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const alignedX = transform.m00 * x + transform.m01 * y + transform.m02 * z + transform.tx;
|
|
234
|
+
const alignedY = transform.m10 * x + transform.m11 * y + transform.m12 * z + transform.ty;
|
|
235
|
+
const alignedZ = transform.m20 * x + transform.m21 * y + transform.m22 * z + transform.tz;
|
|
236
|
+
positions[i] = alignedX;
|
|
237
|
+
positions[i + 1] = alignedY;
|
|
238
|
+
positions[i + 2] = alignedZ;
|
|
239
|
+
found = updateBounds(bounds, alignedX, alignedY, alignedZ) || found;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Rotate normals by the transform's 3×3 linear part (translation omitted)
|
|
243
|
+
// and renormalize. CRS alignment is a rigid rotation, so the linear part
|
|
244
|
+
// itself is the correct transform for normals; degenerate results from
|
|
245
|
+
// zero-length or non-finite inputs are left in place.
|
|
246
|
+
const normals = mesh.normals;
|
|
247
|
+
if (normals && normals.length >= 3) {
|
|
248
|
+
for (let i = 0; i < normals.length; i += 3) {
|
|
249
|
+
const nx = normals[i];
|
|
250
|
+
const ny = normals[i + 1];
|
|
251
|
+
const nz = normals[i + 2];
|
|
252
|
+
if (!Number.isFinite(nx) || !Number.isFinite(ny) || !Number.isFinite(nz)) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const rx = transform.m00 * nx + transform.m01 * ny + transform.m02 * nz;
|
|
256
|
+
const ry = transform.m10 * nx + transform.m11 * ny + transform.m12 * nz;
|
|
257
|
+
const rz = transform.m20 * nx + transform.m21 * ny + transform.m22 * nz;
|
|
258
|
+
const len = Math.sqrt(rx * rx + ry * ry + rz * rz);
|
|
259
|
+
if (!Number.isFinite(len) || len < 1e-12) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
normals[i] = rx / len;
|
|
263
|
+
normals[i + 1] = ry / len;
|
|
264
|
+
normals[i + 2] = rz / len;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
geometry.coordinateInfo = {
|
|
270
|
+
originShift: referenceInfo?.originShift ?? { x: 0, y: 0, z: 0 },
|
|
271
|
+
originalBounds: found ? bounds : zeroBounds(),
|
|
272
|
+
shiftedBounds: found ? bounds : zeroBounds(),
|
|
273
|
+
hasLargeCoordinates: referenceInfo?.hasLargeCoordinates ?? false,
|
|
274
|
+
wasmRtcOffset: referenceInfo?.wasmRtcOffset,
|
|
275
|
+
buildingRotation: referenceInfo?.buildingRotation,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function alignGeometryToReferenceGeoref(
|
|
280
|
+
geometry: FederatedGeometryResult,
|
|
281
|
+
source: ModelGeoref,
|
|
282
|
+
reference: ModelGeoref,
|
|
283
|
+
): boolean {
|
|
284
|
+
if (!canAlignInSameProjectedCrs(source, reference)) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const transform = buildGeorefAlignmentTransform(source, reference);
|
|
289
|
+
if (!transform) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!isIdentityTransform(transform)) {
|
|
294
|
+
applyAlignmentTransformAndUpdateBounds(geometry, transform, reference.coordinateInfo);
|
|
295
|
+
}
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function findReferenceGeorefModel(): ModelGeoref | null {
|
|
300
|
+
const state = useViewerStore.getState();
|
|
301
|
+
const modelEntries = Array.from(state.models.entries()) as Array<[string, FederatedModel]>;
|
|
302
|
+
const sorted = [...modelEntries].sort(([, a], [, b]) => (a.loadedAt ?? 0) - (b.loadedAt ?? 0));
|
|
303
|
+
for (const [modelId, model] of sorted) {
|
|
304
|
+
if (!model.ifcDataStore || !model.geometryResult) continue;
|
|
305
|
+
const georef = extractModelGeoref(
|
|
306
|
+
model.ifcDataStore,
|
|
307
|
+
model.geometryResult.coordinateInfo,
|
|
308
|
+
state.georefMutations.get(modelId),
|
|
309
|
+
);
|
|
310
|
+
if (georef) return georef;
|
|
311
|
+
}
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
29
314
|
|
|
30
315
|
/**
|
|
31
316
|
* Extended data store type for IFCX (IFC5) files.
|
|
@@ -43,41 +328,6 @@ export interface IfcxDataStore extends IfcDataStore {
|
|
|
43
328
|
_layerInfo?: Array<{ id: string; name: string; meshCount: number }>;
|
|
44
329
|
}
|
|
45
330
|
|
|
46
|
-
/** Shape of raw meshes returned from IFCX parsers before conversion to MeshData */
|
|
47
|
-
interface RawIfcxMesh {
|
|
48
|
-
expressId?: number;
|
|
49
|
-
express_id?: number;
|
|
50
|
-
id?: number;
|
|
51
|
-
positions: Float32Array | number[];
|
|
52
|
-
indices: Uint32Array | number[];
|
|
53
|
-
normals: Float32Array | number[];
|
|
54
|
-
color?: [number, number, number, number] | [number, number, number];
|
|
55
|
-
ifcType?: string;
|
|
56
|
-
ifc_type?: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Convert raw IFCX parser meshes to viewer-compatible MeshData[].
|
|
61
|
-
* Ensures typed arrays, normalizes colors, and filters out empty meshes.
|
|
62
|
-
*/
|
|
63
|
-
function convertIfcxMeshes(rawMeshes: RawIfcxMesh[]): MeshData[] {
|
|
64
|
-
return rawMeshes.map((m) => {
|
|
65
|
-
const positions = m.positions instanceof Float32Array ? m.positions : new Float32Array(m.positions || []);
|
|
66
|
-
const indices = m.indices instanceof Uint32Array ? m.indices : new Uint32Array(m.indices || []);
|
|
67
|
-
const normals = m.normals instanceof Float32Array ? m.normals : new Float32Array(m.normals || []);
|
|
68
|
-
const color = normalizeColor(m.color);
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
expressId: m.expressId ?? m.express_id ?? m.id ?? 0,
|
|
72
|
-
positions,
|
|
73
|
-
indices,
|
|
74
|
-
normals,
|
|
75
|
-
color,
|
|
76
|
-
ifcType: m.ifcType ?? m.ifc_type ?? 'IfcProduct',
|
|
77
|
-
};
|
|
78
|
-
}).filter((m) => m.positions.length > 0 && m.indices.length > 0);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
331
|
/**
|
|
82
332
|
* Hook providing multi-model federation operations
|
|
83
333
|
* Includes addModel, removeModel, federated IFCX loading, overlay management,
|
|
@@ -122,12 +372,17 @@ export function useIfcFederation() {
|
|
|
122
372
|
* Returns the model ID on success, null on failure
|
|
123
373
|
*/
|
|
124
374
|
const addModel = useCallback(async (
|
|
125
|
-
file: File,
|
|
126
|
-
options?: {
|
|
375
|
+
file: File | NativeFileHandle,
|
|
376
|
+
options?: {
|
|
377
|
+
name?: string;
|
|
378
|
+
modelId?: string;
|
|
379
|
+
loadedAt?: number;
|
|
380
|
+
visible?: boolean;
|
|
381
|
+
collapsed?: boolean;
|
|
382
|
+
}
|
|
127
383
|
): Promise<string | null> => {
|
|
128
|
-
const modelId = crypto.randomUUID();
|
|
129
|
-
const
|
|
130
|
-
|
|
384
|
+
const modelId = options?.modelId ?? crypto.randomUUID();
|
|
385
|
+
const addStart = performance.now();
|
|
131
386
|
try {
|
|
132
387
|
// IMPORTANT: Before adding a new model, check if there's a legacy model
|
|
133
388
|
// (loaded via loadFile) that's not in the Map yet. If so, migrate it first.
|
|
@@ -167,11 +422,11 @@ export function useIfcFederation() {
|
|
|
167
422
|
schemaVersion: 'IFC4',
|
|
168
423
|
loadedAt: Date.now() - 1000,
|
|
169
424
|
fileSize: 0,
|
|
425
|
+
sourceFile: undefined,
|
|
170
426
|
idOffset: legacyOffset,
|
|
171
427
|
maxExpressId: legacyMaxExpressId,
|
|
172
428
|
};
|
|
173
429
|
storeAddModel(legacyModel);
|
|
174
|
-
console.log(`[useIfc] Migrated legacy model "${legacyModel.name}" to federation (offset: ${legacyOffset}, maxId: ${legacyMaxExpressId})`);
|
|
175
430
|
}
|
|
176
431
|
|
|
177
432
|
setLoading(true);
|
|
@@ -179,180 +434,93 @@ export function useIfcFederation() {
|
|
|
179
434
|
setProgress({ phase: 'Loading file', percent: 0 });
|
|
180
435
|
|
|
181
436
|
// Read file from disk
|
|
182
|
-
const buffer =
|
|
437
|
+
const buffer = isNativeFileHandle(file)
|
|
438
|
+
? toExactArrayBuffer(await readNativeFile(file.path))
|
|
439
|
+
: await file.arrayBuffer();
|
|
183
440
|
const fileSizeMB = buffer.byteLength / (1024 * 1024);
|
|
184
441
|
|
|
185
442
|
// Detect file format
|
|
186
443
|
const format = detectFormat(buffer);
|
|
187
444
|
|
|
188
445
|
let parsedDataStore: IfcDataStore | null = null;
|
|
189
|
-
let parsedGeometry:
|
|
446
|
+
let parsedGeometry: FederatedModel['geometryResult'] = null;
|
|
190
447
|
let schemaVersion: SchemaVersion = 'IFC4';
|
|
191
448
|
|
|
192
|
-
// IFCX files must be parsed client-side
|
|
193
449
|
if (format === 'ifcx') {
|
|
194
450
|
setProgress({ phase: 'Parsing IFCX (client-side)', percent: 10 });
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
setError(`"${file.name}" is an overlay file with no geometry. Please load it together with a base IFCX file (select all files at once for federated loading).`);
|
|
209
|
-
setLoading(false);
|
|
210
|
-
return null;
|
|
451
|
+
try {
|
|
452
|
+
const result = await parseIfcxViewerModel(buffer, setProgress);
|
|
453
|
+
parsedDataStore = result.dataStore;
|
|
454
|
+
parsedGeometry = result.geometryResult;
|
|
455
|
+
schemaVersion = result.schemaVersion;
|
|
456
|
+
} catch (error) {
|
|
457
|
+
if (error instanceof Error && error.message === 'overlay-only-ifcx') {
|
|
458
|
+
console.warn(`[useIfc] IFCX file "${file.name}" has no geometry - this is an overlay file.`);
|
|
459
|
+
setError(`"${file.name}" is an overlay file with no geometry. Please load it together with a base IFCX file (select all files at once for federated loading).`);
|
|
460
|
+
setLoading(false);
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
throw error;
|
|
211
464
|
}
|
|
212
|
-
|
|
213
|
-
const { bounds, stats } = calculateMeshBounds(meshes);
|
|
214
|
-
const coordinateInfo = createCoordinateInfo(bounds);
|
|
215
|
-
|
|
216
|
-
parsedGeometry = {
|
|
217
|
-
meshes,
|
|
218
|
-
totalVertices: stats.totalVertices,
|
|
219
|
-
totalTriangles: stats.totalTriangles,
|
|
220
|
-
coordinateInfo,
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
parsedDataStore = {
|
|
224
|
-
fileSize: ifcxResult.fileSize,
|
|
225
|
-
schemaVersion: 'IFC5' as const,
|
|
226
|
-
entityCount: ifcxResult.entityCount,
|
|
227
|
-
parseTime: ifcxResult.parseTime,
|
|
228
|
-
source: new Uint8Array(buffer),
|
|
229
|
-
entityIndex: { byId: new Map(), byType: new Map() },
|
|
230
|
-
strings: ifcxResult.strings,
|
|
231
|
-
entities: ifcxResult.entities,
|
|
232
|
-
properties: ifcxResult.properties,
|
|
233
|
-
quantities: ifcxResult.quantities,
|
|
234
|
-
relationships: ifcxResult.relationships,
|
|
235
|
-
spatialHierarchy: ifcxResult.spatialHierarchy,
|
|
236
|
-
} as IfcxDataStore;
|
|
237
|
-
|
|
238
|
-
schemaVersion = 'IFC5';
|
|
239
|
-
|
|
240
465
|
} else if (format === 'glb') {
|
|
241
|
-
// GLB files: parse directly to MeshData (geometry only, no IFC data model)
|
|
242
466
|
setProgress({ phase: 'Parsing GLB', percent: 10 });
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
setError('GLB file contains no geometry');
|
|
248
|
-
setLoading(false);
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const { bounds, stats } = calculateMeshBounds(meshes);
|
|
253
|
-
const coordinateInfo = createCoordinateInfo(bounds);
|
|
254
|
-
|
|
255
|
-
parsedGeometry = {
|
|
256
|
-
meshes,
|
|
257
|
-
totalVertices: stats.totalVertices,
|
|
258
|
-
totalTriangles: stats.totalTriangles,
|
|
259
|
-
coordinateInfo,
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
// Create a minimal data store for GLB (no IFC properties)
|
|
263
|
-
parsedDataStore = {
|
|
264
|
-
fileSize: buffer.byteLength,
|
|
265
|
-
schemaVersion: 'IFC4' as const,
|
|
266
|
-
entityCount: meshes.length,
|
|
267
|
-
parseTime: 0,
|
|
268
|
-
source: new Uint8Array(0),
|
|
269
|
-
entityIndex: { byId: new Map(), byType: new Map() },
|
|
270
|
-
strings: { getString: () => undefined, getStringId: () => undefined, count: 0 } as unknown as IfcDataStore['strings'],
|
|
271
|
-
entities: { count: 0, getId: () => 0, getType: () => 0, getName: () => undefined, getGlobalId: () => undefined } as unknown as IfcDataStore['entities'],
|
|
272
|
-
properties: { count: 0, getPropertiesForEntity: () => [], getPropertySetForEntity: () => [] } as unknown as IfcDataStore['properties'],
|
|
273
|
-
quantities: { count: 0, getQuantitiesForEntity: () => [] } as unknown as IfcDataStore['quantities'],
|
|
274
|
-
relationships: { count: 0, getRelationships: () => [], getRelated: () => [] } as unknown as IfcDataStore['relationships'],
|
|
275
|
-
spatialHierarchy: null as unknown as IfcDataStore['spatialHierarchy'],
|
|
276
|
-
} as unknown as IfcDataStore;
|
|
277
|
-
|
|
278
|
-
schemaVersion = 'IFC4'; // GLB doesn't have a schema version, use IFC4 as default
|
|
279
|
-
|
|
467
|
+
const result = await parseGlbViewerModel(buffer);
|
|
468
|
+
parsedDataStore = result.dataStore;
|
|
469
|
+
parsedGeometry = result.geometryResult;
|
|
470
|
+
schemaVersion = result.schemaVersion;
|
|
280
471
|
} else {
|
|
281
|
-
// IFC4/IFC2X3 STEP format - use WASM parsing
|
|
282
472
|
setProgress({ phase: 'Starting geometry streaming', percent: 10 });
|
|
283
473
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const allMeshes: MeshData[] = [];
|
|
295
|
-
let finalCoordinateInfo: CoordinateInfo | null = null;
|
|
296
|
-
// Capture RTC offset from WASM for proper multi-model alignment
|
|
297
|
-
let capturedRtcOffset: { x: number; y: number; z: number } | null = null;
|
|
298
|
-
|
|
299
|
-
const dynamicBatchConfig = getDynamicBatchConfig(fileSizeMB);
|
|
300
|
-
|
|
301
|
-
for await (const event of geometryProcessor.processAdaptive(new Uint8Array(buffer), {
|
|
302
|
-
sizeThreshold: 2 * 1024 * 1024,
|
|
303
|
-
batchSize: dynamicBatchConfig,
|
|
304
|
-
})) {
|
|
305
|
-
switch (event.type) {
|
|
306
|
-
case 'batch': {
|
|
307
|
-
for (let i = 0; i < event.meshes.length; i++) allMeshes.push(event.meshes[i]);
|
|
308
|
-
finalCoordinateInfo = event.coordinateInfo ?? null;
|
|
309
|
-
const progressPercent = 10 + Math.min(80, (allMeshes.length / 1000) * 0.8);
|
|
310
|
-
setProgress({ phase: `Processing geometry (${allMeshes.length} meshes)`, percent: progressPercent });
|
|
311
|
-
break;
|
|
312
|
-
}
|
|
313
|
-
case 'rtcOffset': {
|
|
314
|
-
// Capture RTC offset from WASM for multi-model alignment
|
|
315
|
-
if (event.hasRtc) {
|
|
316
|
-
capturedRtcOffset = event.rtcOffset;
|
|
317
|
-
}
|
|
318
|
-
break;
|
|
319
|
-
}
|
|
320
|
-
case 'complete':
|
|
321
|
-
finalCoordinateInfo = event.coordinateInfo ?? null;
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
parsedDataStore = await dataStorePromise;
|
|
327
|
-
|
|
328
|
-
// Calculate storey heights
|
|
329
|
-
if (parsedDataStore.spatialHierarchy && parsedDataStore.spatialHierarchy.storeyHeights.size === 0 && parsedDataStore.spatialHierarchy.storeyElevations.size > 1) {
|
|
330
|
-
const calculatedHeights = calculateStoreyHeights(parsedDataStore.spatialHierarchy.storeyElevations);
|
|
331
|
-
for (const [storeyId, height] of calculatedHeights) {
|
|
332
|
-
parsedDataStore.spatialHierarchy.storeyHeights.set(storeyId, height);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
parsedGeometry = {
|
|
337
|
-
meshes: allMeshes,
|
|
338
|
-
totalVertices: allMeshes.reduce((sum, m) => sum + m.positions.length / 3, 0),
|
|
339
|
-
totalTriangles: allMeshes.reduce((sum, m) => sum + m.indices.length / 3, 0),
|
|
340
|
-
coordinateInfo: finalCoordinateInfo || createCoordinateInfo(calculateMeshBounds(allMeshes).bounds),
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
// Store captured RTC offset in coordinate info for multi-model alignment
|
|
344
|
-
if (parsedGeometry.coordinateInfo && capturedRtcOffset) {
|
|
345
|
-
parsedGeometry.coordinateInfo.wasmRtcOffset = capturedRtcOffset;
|
|
474
|
+
// For federated models: use the first model's RTC offset so all models
|
|
475
|
+
// share the same coordinate origin. This ensures pixel-perfect alignment
|
|
476
|
+
// without error-prone delta adjustments.
|
|
477
|
+
let sharedRtcOffset: { x: number; y: number; z: number } | undefined;
|
|
478
|
+
const existingModelsForRtc = Array.from(useViewerStore.getState().models.values()) as FederatedModel[];
|
|
479
|
+
if (existingModelsForRtc.length > 0) {
|
|
480
|
+
const sorted = [...existingModelsForRtc].sort((a, b) => (a.loadedAt ?? 0) - (b.loadedAt ?? 0));
|
|
481
|
+
sharedRtcOffset = sorted.find(
|
|
482
|
+
(model) => model.geometryResult?.coordinateInfo?.wasmRtcOffset != null,
|
|
483
|
+
)?.geometryResult?.coordinateInfo?.wasmRtcOffset;
|
|
346
484
|
}
|
|
347
485
|
|
|
348
|
-
|
|
349
|
-
|
|
486
|
+
const result = await parseStepBufferViewerModel({
|
|
487
|
+
fileName: file.name,
|
|
488
|
+
buffer,
|
|
489
|
+
fileSizeMB,
|
|
490
|
+
getDynamicBatchSize: getDynamicBatchConfig,
|
|
491
|
+
onProgress: setProgress,
|
|
492
|
+
sharedRtcOffset,
|
|
493
|
+
});
|
|
494
|
+
parsedDataStore = result.dataStore;
|
|
495
|
+
parsedGeometry = result.geometryResult;
|
|
496
|
+
schemaVersion = result.schemaVersion;
|
|
350
497
|
}
|
|
351
498
|
|
|
352
499
|
if (!parsedDataStore || !parsedGeometry) {
|
|
353
500
|
throw new Error('Failed to parse file');
|
|
354
501
|
}
|
|
355
502
|
|
|
503
|
+
const referenceGeoref = findReferenceGeorefModel();
|
|
504
|
+
// Include any georef edits the user has already saved for this model so
|
|
505
|
+
// that a reload after editing reflects the new placement. Without this,
|
|
506
|
+
// extractModelGeoref reads only the raw parsed metadata and mutations
|
|
507
|
+
// are silently ignored.
|
|
508
|
+
const parsedGeorefMutations = useViewerStore.getState().georefMutations.get(modelId);
|
|
509
|
+
const parsedGeoref = extractModelGeoref(
|
|
510
|
+
parsedDataStore,
|
|
511
|
+
parsedGeometry.coordinateInfo,
|
|
512
|
+
parsedGeorefMutations,
|
|
513
|
+
);
|
|
514
|
+
if (referenceGeoref && parsedGeoref) {
|
|
515
|
+
setProgress({ phase: 'Aligning georeferenced model', percent: 90 });
|
|
516
|
+
const aligned = alignGeometryToReferenceGeoref(parsedGeometry, parsedGeoref, referenceGeoref);
|
|
517
|
+
if (!aligned) {
|
|
518
|
+
console.warn(
|
|
519
|
+
`[ifc-lite] Skipped georeferenced federation alignment for "${file.name}" because CRS differs from the reference model.`,
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
356
524
|
// =========================================================================
|
|
357
525
|
// FEDERATION REGISTRY: Transform expressIds to globally unique IDs
|
|
358
526
|
// This is the BULLETPROOF fix for multi-model ID collisions
|
|
@@ -361,15 +529,7 @@ export function useIfcFederation() {
|
|
|
361
529
|
// Step 1: Find max expressId in this model
|
|
362
530
|
// IMPORTANT: Use ALL entities from data store, not just meshes
|
|
363
531
|
// Spatial containers (IfcProject, IfcSite, etc.) don't have geometry but need valid globalId resolution
|
|
364
|
-
const
|
|
365
|
-
// FIXED: Use iteration instead of spread to avoid stack overflow with large Maps
|
|
366
|
-
let maxExpressIdFromEntities = 0;
|
|
367
|
-
if (parsedDataStore.entityIndex?.byId) {
|
|
368
|
-
for (const key of parsedDataStore.entityIndex.byId.keys()) {
|
|
369
|
-
if (key > maxExpressIdFromEntities) maxExpressIdFromEntities = key;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
const maxExpressId = Math.max(maxExpressIdFromMeshes, maxExpressIdFromEntities);
|
|
532
|
+
const maxExpressId = getMaxExpressId(parsedDataStore, parsedGeometry.meshes);
|
|
373
533
|
|
|
374
534
|
// Step 2: Register with federation registry to get unique offset
|
|
375
535
|
const idOffset = registerModelOffset(modelId, maxExpressId);
|
|
@@ -384,74 +544,10 @@ export function useIfcFederation() {
|
|
|
384
544
|
}
|
|
385
545
|
|
|
386
546
|
// =========================================================================
|
|
387
|
-
// COORDINATE ALIGNMENT:
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
// RTC offset is in IFC coordinates (Z-up). After Z-up to Y-up conversion:
|
|
392
|
-
// - IFC X → WebGL X
|
|
393
|
-
// - IFC Y → WebGL -Z
|
|
394
|
-
// - IFC Z → WebGL Y (vertical)
|
|
547
|
+
// COORDINATE ALIGNMENT: All federated models use the same shared RTC offset
|
|
548
|
+
// (passed to WASM during parsing above), so no post-processing vertex
|
|
549
|
+
// adjustment is needed. All models are already in the same coordinate space.
|
|
395
550
|
// =========================================================================
|
|
396
|
-
const existingModels = Array.from(useViewerStore.getState().models.values()) as FederatedModel[];
|
|
397
|
-
if (existingModels.length > 0) {
|
|
398
|
-
const firstModel = existingModels[0];
|
|
399
|
-
const firstRtc = firstModel.geometryResult?.coordinateInfo?.wasmRtcOffset;
|
|
400
|
-
const newRtc = parsedGeometry.coordinateInfo?.wasmRtcOffset;
|
|
401
|
-
|
|
402
|
-
// If both models have RTC offsets, use RTC delta for precise alignment
|
|
403
|
-
if (firstRtc && newRtc) {
|
|
404
|
-
// Calculate what adjustment is needed to align new model with first model
|
|
405
|
-
// First model: pos = original - firstRtc
|
|
406
|
-
// New model: pos = original - newRtc
|
|
407
|
-
// To align: newPos + adjustment = firstPos (assuming same original)
|
|
408
|
-
// adjustment = firstRtc - newRtc (add back new's RTC, subtract first's RTC)
|
|
409
|
-
const adjustX = firstRtc.x - newRtc.x; // IFC X adjustment
|
|
410
|
-
const adjustY = firstRtc.y - newRtc.y; // IFC Y adjustment
|
|
411
|
-
const adjustZ = firstRtc.z - newRtc.z; // IFC Z adjustment (vertical)
|
|
412
|
-
|
|
413
|
-
// Convert to WebGL coordinates:
|
|
414
|
-
// IFC X → WebGL X (no change)
|
|
415
|
-
// IFC Y → WebGL -Z (swap and negate)
|
|
416
|
-
// IFC Z → WebGL Y (vertical)
|
|
417
|
-
const webglAdjustX = adjustX;
|
|
418
|
-
const webglAdjustY = adjustZ; // IFC Z is WebGL Y (vertical)
|
|
419
|
-
const webglAdjustZ = -adjustY; // IFC Y is WebGL -Z
|
|
420
|
-
|
|
421
|
-
const hasSignificantAdjust = Math.abs(webglAdjustX) > 0.01 ||
|
|
422
|
-
Math.abs(webglAdjustY) > 0.01 ||
|
|
423
|
-
Math.abs(webglAdjustZ) > 0.01;
|
|
424
|
-
|
|
425
|
-
if (hasSignificantAdjust) {
|
|
426
|
-
console.log(`[useIfc] Aligning model "${file.name}" using RTC adjustment: X=${webglAdjustX.toFixed(2)}m, Y=${webglAdjustY.toFixed(2)}m, Z=${webglAdjustZ.toFixed(2)}m`);
|
|
427
|
-
|
|
428
|
-
// Apply adjustment to all mesh vertices
|
|
429
|
-
// SUBTRACT adjustment: if firstRtc > newRtc, first was shifted MORE,
|
|
430
|
-
// so new model needs to be shifted in same direction (subtract more)
|
|
431
|
-
for (const mesh of parsedGeometry.meshes) {
|
|
432
|
-
const positions = mesh.positions;
|
|
433
|
-
for (let i = 0; i < positions.length; i += 3) {
|
|
434
|
-
positions[i] -= webglAdjustX;
|
|
435
|
-
positions[i + 1] -= webglAdjustY;
|
|
436
|
-
positions[i + 2] -= webglAdjustZ;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Update coordinate info bounds
|
|
441
|
-
if (parsedGeometry.coordinateInfo) {
|
|
442
|
-
parsedGeometry.coordinateInfo.shiftedBounds.min.x -= webglAdjustX;
|
|
443
|
-
parsedGeometry.coordinateInfo.shiftedBounds.max.x -= webglAdjustX;
|
|
444
|
-
parsedGeometry.coordinateInfo.shiftedBounds.min.y -= webglAdjustY;
|
|
445
|
-
parsedGeometry.coordinateInfo.shiftedBounds.max.y -= webglAdjustY;
|
|
446
|
-
parsedGeometry.coordinateInfo.shiftedBounds.min.z -= webglAdjustZ;
|
|
447
|
-
parsedGeometry.coordinateInfo.shiftedBounds.max.z -= webglAdjustZ;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
} else {
|
|
451
|
-
// No RTC info - can't align reliably. This happens with old cache entries.
|
|
452
|
-
console.warn(`[useIfc] Cannot align "${file.name}" - missing RTC offset. Clear cache and reload.`);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
551
|
|
|
456
552
|
// Build spatial index AFTER ID offset + RTC alignment so it stores
|
|
457
553
|
// correct globalIds and final world-space positions.
|
|
@@ -463,11 +559,12 @@ export function useIfcFederation() {
|
|
|
463
559
|
name: options?.name ?? file.name,
|
|
464
560
|
ifcDataStore: parsedDataStore,
|
|
465
561
|
geometryResult: parsedGeometry,
|
|
466
|
-
visible: true,
|
|
467
|
-
collapsed: hasModels(), // Collapse if not first model
|
|
562
|
+
visible: options?.visible ?? true,
|
|
563
|
+
collapsed: options?.collapsed ?? hasModels(), // Collapse if not first model
|
|
468
564
|
schemaVersion,
|
|
469
|
-
loadedAt: Date.now(),
|
|
565
|
+
loadedAt: options?.loadedAt ?? Date.now(),
|
|
470
566
|
fileSize: buffer.byteLength,
|
|
567
|
+
sourceFile: file,
|
|
471
568
|
idOffset,
|
|
472
569
|
maxExpressId,
|
|
473
570
|
};
|
|
@@ -481,9 +578,7 @@ export function useIfcFederation() {
|
|
|
481
578
|
|
|
482
579
|
setProgress({ phase: 'Complete', percent: 100 });
|
|
483
580
|
setLoading(false);
|
|
484
|
-
|
|
485
|
-
const totalElapsedMs = performance.now() - totalStartTime;
|
|
486
|
-
console.log(`[useIfc] ✓ Added model ${file.name} (${fileSizeMB.toFixed(1)}MB) | ${totalElapsedMs.toFixed(0)}ms`);
|
|
581
|
+
console.log(`[ifc-lite] Added model ${file.name} (${fileSizeMB.toFixed(1)}MB) in ${(performance.now() - addStart).toFixed(0)}ms`);
|
|
487
582
|
|
|
488
583
|
return modelId;
|
|
489
584
|
|
|
@@ -519,7 +614,7 @@ export function useIfcFederation() {
|
|
|
519
614
|
*/
|
|
520
615
|
const getQueryForModel = useCallback((modelId: string): IfcQuery | null => {
|
|
521
616
|
const model = getModel(modelId);
|
|
522
|
-
if (!model) return null;
|
|
617
|
+
if (!model || !model.ifcDataStore) return null;
|
|
523
618
|
return new IfcQuery(model.ifcDataStore);
|
|
524
619
|
}, [getModel]);
|
|
525
620
|
|
|
@@ -684,10 +779,6 @@ export function useIfcFederation() {
|
|
|
684
779
|
storeAddModel(layerModel);
|
|
685
780
|
}
|
|
686
781
|
|
|
687
|
-
console.log(`[useIfc] Federated IFCX loaded: ${layers.length} layers, ${result.entityCount} entities, ${meshes.length} meshes`);
|
|
688
|
-
console.log(`[useIfc] Composition stats: ${result.compositionStats.inheritanceResolutions} inheritance resolutions, ${result.compositionStats.crossLayerReferences} cross-layer refs`);
|
|
689
|
-
console.log(`[useIfc] Layers in Models panel: ${layers.map((l: { name: string }) => l.name).join(', ')}`);
|
|
690
|
-
|
|
691
782
|
setProgress({ phase: 'Complete', percent: 100 });
|
|
692
783
|
setLoading(false);
|
|
693
784
|
} catch (err: unknown) {
|
|
@@ -753,7 +844,6 @@ export function useIfcFederation() {
|
|
|
753
844
|
) as ArrayBuffer;
|
|
754
845
|
|
|
755
846
|
existingBuffers = [{ buffer: sourceBuffer, name: modelName }];
|
|
756
|
-
console.log(`[useIfc] Converting single IFCX file "${modelName}" to federated mode`);
|
|
757
847
|
} else {
|
|
758
848
|
setError('Cannot add overlays: no IFCX model loaded');
|
|
759
849
|
return;
|
|
@@ -774,9 +864,6 @@ export function useIfcFederation() {
|
|
|
774
864
|
// Combine: existing layers + new overlays (new overlays are strongest = first in array)
|
|
775
865
|
const allBuffers = [...newBuffers, ...existingBuffers];
|
|
776
866
|
|
|
777
|
-
console.log(`[useIfc] Re-composing federated IFCX with ${newBuffers.length} new overlay(s)`);
|
|
778
|
-
console.log(`[useIfc] Total layers: ${allBuffers.length} (${existingBuffers.length} existing + ${newBuffers.length} new)`);
|
|
779
|
-
|
|
780
867
|
await loadFederatedIfcxFromBuffers(allBuffers, { resetState: false });
|
|
781
868
|
}, [setError, loadFederatedIfcxFromBuffers]);
|
|
782
869
|
|