@collabdt/core 0.0.42 → 0.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/components/Toolbar.d.ts.map +1 -1
- package/dist/core/components/Toolbar.js.map +1 -1
- package/dist/core/components/ToolbarBody.js.map +1 -1
- package/dist/core/components/TopNavigationBar.js.map +1 -1
- package/dist/core/components/authentication/ForgotPassword.js.map +1 -1
- package/dist/core/components/authentication/Signin.js.map +1 -1
- package/dist/core/components/authentication/Signup.js.map +1 -1
- package/dist/core/components/settings/src/OrganizationSkeleton.js.map +1 -1
- package/dist/core/components/settings/src/SettingsSkeleton.js.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.js.map +1 -1
- package/dist/core/components/ui/DataTable.d.ts.map +1 -1
- package/dist/core/components/ui/DataTable.js +7 -0
- package/dist/core/components/ui/DataTable.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileItemComponent.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileItemComponent.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileMarker.d.ts +5 -2
- package/dist/core/components/ui/FilesManager/src/FileMarker.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileMarker.js +84 -21
- package/dist/core/components/ui/FilesManager/src/FileMarker.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/convertIfcToFragmentsFile.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/convertIfcToFragmentsFile.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadHandler.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadHandler.js.map +1 -1
- package/dist/core/components/ui/Icons/IfcIcon.d.ts.map +1 -1
- package/dist/core/components/ui/Icons/IfcIcon.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/index.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/index.js.map +1 -1
- package/dist/core/components/ui/Navbar.d.ts.map +1 -1
- package/dist/core/components/ui/Navbar.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/ShareFeature/ShareToolSubmenu.d.ts.map +1 -1
- package/dist/core/components/ui/ShareFeature/ShareToolSubmenu.js.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js +2 -2
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js.map +1 -1
- package/dist/core/components/viewers/Data/infrastructureDetails/FieldRenderer.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/infrastructureDetails/FieldRenderer.js.map +1 -1
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js +2 -2
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js.map +1 -1
- package/dist/core/components/viewers/Viewer.d.ts.map +1 -1
- package/dist/core/components/viewers/Viewer.js +5 -1
- package/dist/core/components/viewers/Viewer.js.map +1 -1
- package/dist/core/components/viewers/bim/BimToolbar.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/BimToolbar.js.map +1 -1
- package/dist/core/components/viewers/bim/BimViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/BimViewer.js +14 -5
- package/dist/core/components/viewers/bim/BimViewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimLoadingState/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimLoadingState/index.js +6 -0
- package/dist/core/components/viewers/bim/src/BimLoadingState/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +168 -39
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/LayersTab/src/FloorplanSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/LayersTab/src/FloorplanSection.js +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/LayersTab/src/FloorplanSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.js.map +1 -1
- package/dist/core/components/viewers/bim/src/DXFLoader/index.d.ts +14 -37
- package/dist/core/components/viewers/bim/src/DXFLoader/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/DXFLoader/index.js +31 -318
- package/dist/core/components/viewers/bim/src/DXFLoader/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.d.ts +66 -7
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.js +253 -21
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/ModelManager/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/ModelManager/index.js +9 -0
- package/dist/core/components/viewers/bim/src/ModelManager/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/SimpleBimViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/SimpleBimViewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/TrueNorthPopover.d.ts +3 -2
- package/dist/core/components/viewers/bim/src/lib/TrueNorthPopover.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/TrueNorthPopover.js.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.js +3 -4
- package/dist/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +40 -18
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.d.ts +2 -5
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.js +45 -59
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddSensor.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddSensor.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.d.ts +10 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.js +25 -73
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.d.ts +7 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.js +42 -78
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/Position3DCard.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/Position3DCard.js +52 -75
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/Position3DCard.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.d.ts +15 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.js +165 -105
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.js.map +1 -1
- package/dist/core/components/viewers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/index.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js +32 -8
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/DatasetManager/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/DatasetManager/index.js +4 -3
- package/dist/core/components/viewers/map/datasets/DatasetManager/index.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.js +148 -0
- package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.js +32 -15
- package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.d.ts +4 -0
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.d.ts.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.js +43 -0
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.js.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/localDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js +39 -3
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.js +2 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.d.ts +47 -0
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.d.ts.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.js +114 -0
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.js.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.d.ts +49 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.d.ts.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.js +100 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.js.map +1 -0
- package/dist/core/components/viewers/map/src/Geocoder.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/Geocoder.js +9 -9
- package/dist/core/components/viewers/map/src/Geocoder.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js +25 -43
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/BimLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/BimLayer/index.js +19 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/BimLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.d.ts +9 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.js +28 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.js.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.js +1 -16
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.js +31 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.d.ts +21 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.js +111 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.js.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.js +77 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/MapProjection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/MapProjection.js +19 -9
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/MapProjection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.js +10 -3
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoder.d.ts +6 -2
- package/dist/core/components/viewers/map/utils/geocoder.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoder.js +10 -62
- package/dist/core/components/viewers/map/utils/geocoder.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/adapters.d.ts +5 -0
- package/dist/core/components/viewers/map/utils/geocoding/adapters.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/adapters.js +192 -0
- package/dist/core/components/viewers/map/utils/geocoding/adapters.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.d.ts +6 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.js +14 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.d.ts +8 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.js +44 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.d.ts +6 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.js +39 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.d.ts +8 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.js +35 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.js.map +1 -0
- package/dist/core/components/viewers/pointcloud/PointCloudToolbar.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudToolbar.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js +1 -3
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js +2 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/FilesSection.js +8 -2
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/tools/PerformanceSettingsTools/NodeSizeSelectionTool.js +9 -9
- package/dist/core/components/viewers/pointcloud/src/tools/PerformanceSettingsTools/NodeSizeSelectionTool.js.map +1 -1
- package/dist/core/hooks/provider.js.map +1 -1
- package/dist/core/plugins/host/provider.d.ts.map +1 -1
- package/dist/core/plugins/host/provider.js.map +1 -1
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/dist/core/store/BIM/context.d.ts.map +1 -1
- package/dist/core/store/BIM/context.js.map +1 -1
- package/dist/core/store/BIM/reducer.d.ts +0 -1
- package/dist/core/store/BIM/reducer.d.ts.map +1 -1
- package/dist/core/store/BIM/reducer.js +0 -4
- package/dist/core/store/BIM/reducer.js.map +1 -1
- package/dist/core/store/Buildings/context.d.ts.map +1 -1
- package/dist/core/store/Buildings/context.js.map +1 -1
- package/dist/core/store/Content/context.d.ts.map +1 -1
- package/dist/core/store/Content/context.js.map +1 -1
- package/dist/core/store/Datasets/context.d.ts.map +1 -1
- package/dist/core/store/Datasets/context.js +2 -1
- package/dist/core/store/Datasets/context.js.map +1 -1
- package/dist/core/store/Datasets/reducer.d.ts +2 -0
- package/dist/core/store/Datasets/reducer.d.ts.map +1 -1
- package/dist/core/store/Datasets/reducer.js +9 -4
- package/dist/core/store/Datasets/reducer.js.map +1 -1
- package/dist/core/store/Files/context.d.ts.map +1 -1
- package/dist/core/store/Files/context.js.map +1 -1
- package/dist/core/store/Map/context.d.ts.map +1 -1
- package/dist/core/store/Map/context.js.map +1 -1
- package/dist/core/store/Menus/context.d.ts.map +1 -1
- package/dist/core/store/Menus/context.js.map +1 -1
- package/dist/core/store/Permissions/context.d.ts.map +1 -1
- package/dist/core/store/Permissions/context.js.map +1 -1
- package/dist/core/store/PointCloud/context.d.ts.map +1 -1
- package/dist/core/store/PointCloud/context.js.map +1 -1
- package/dist/core/store/Tools/context.d.ts.map +1 -1
- package/dist/core/store/Tools/context.js.map +1 -1
- package/dist/core/types/datasetTypes.d.ts +8 -1
- package/dist/core/types/datasetTypes.d.ts.map +1 -1
- package/dist/core/types/dbTypes.d.ts +13 -13
- package/dist/core/types/dbTypes.d.ts.map +1 -1
- package/dist/core/types/dbTypes.js +13 -13
- package/dist/core/types/dbTypes.js.map +1 -1
- package/dist/core/types/index.d.ts +1 -1
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/types/index.js.map +1 -1
- package/dist/core/utils/imageUtils.js +1 -1
- package/dist/core/utils/imageUtils.js.map +1 -1
- package/package.json +4 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.d.ts +0 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.d.ts.map +0 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.js +0 -173
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/bim/src/FloorplanTool/index.ts"],"sourcesContent":["import * as OBC from '@thatopen/components'\r\nimport * as OBF from '@thatopen/components-front'\r\nimport * as THREE from 'three'\r\nimport { CurrentWorld } from '../CurrentWorld'\r\nimport { CameraController } from '../lib/CameraController'\r\nimport { ChromeController } from '../lib/ChromeController'\r\nimport { ClipController } from '../lib/ClipController'\r\nimport { CUT_CLASSES, FILL_CLASSES } from '../lib/drawingLayers'\r\nimport { disposeDrawing } from '../lib/drawingProjection'\r\nimport { GridController } from '../lib/GridController'\r\nimport { safeRun, safeRunAsync } from '../lib/safeRun'\r\nimport { stagePercent, ViewLoadingState } from '../lib/viewSection'\r\nimport { ViewModeCoordinator } from '../lib/ViewModeCoordinator'\r\nimport { FloorplanRenderer, RenderStage } from './src/FloorplanRenderer'\r\nimport { StoreyProjector } from './src/StoreyProjector'\r\nimport { CUT_COLOR, FILL_COLOR, FloorplanEntry, FLOORPLAN_TOOL_UUID } from './src/types'\r\nimport { normalizeElevation } from './src/utils'\r\n\r\nconst SLOT_SECTION = 'floorplan:section'\r\nconst SLOT_LOWER = 'floorplan:lower'\r\n\r\nexport type { FloorplanEntry } from './src/types'\r\n\r\nexport type FloorplanLoadingStage =\r\n | 'init'\r\n | 'readingStoreys'\r\n | RenderStage\r\n | 'project'\r\n | 'done'\r\n\r\nexport type FloorplanLoadingState = ViewLoadingState<FloorplanLoadingStage>\r\n\r\nconst STAGE_PERCENT: Record<FloorplanLoadingStage, number> = {\r\n init: 5,\r\n readingStoreys: 10,\r\n resolve: 20,\r\n cull: 35,\r\n switching: 35,\r\n cut: 55,\r\n fill: 75,\r\n project: 90,\r\n done: 100,\r\n}\r\n\r\nexport function getFloorplanStagePercent(\r\n stage: FloorplanLoadingStage | undefined,\r\n): number {\r\n return stagePercent(stage, STAGE_PERCENT)\r\n}\r\n\r\nexport class FloorplanTool extends OBC.Component {\r\n static uuid = FLOORPLAN_TOOL_UUID\r\n\r\n enabled = false\r\n\r\n readonly onDrawingsChanged = new OBC.Event<FloorplanEntry[]>()\r\n readonly onActiveDrawingChanged = new OBC.Event<FloorplanEntry | null>()\r\n readonly onGenerationStateChanged = new OBC.Event<FloorplanLoadingState>()\r\n /** Fires whenever an entry's layer metadata changes (toggle / color). */\r\n readonly onLayersChanged = new OBC.Event<FloorplanEntry>()\r\n /** Fires when the building's true-north angle (in degrees) is updated. */\r\n readonly onNorthAngleChanged = new OBC.Event<number>()\r\n /** Fires when line-pick mode toggles on/off. */\r\n readonly onPickingNorthChanged = new OBC.Event<boolean>()\r\n\r\n private _entries = new Map<string, FloorplanEntry>()\r\n private _activeId: string | null = null\r\n /** Sequence guard: when activate() is called multiple times in quick\r\n * succession (e.g. user clicks two storeys), only the last call applies\r\n * its final state. Prior calls bail out at await boundaries. */\r\n private _activateSeq = 0\r\n /** True-north rotation in degrees (clockwise from default screen-up).\r\n * Persists across storey switches but resets to 0 on tool dispose. */\r\n private _northAngle = 0\r\n private _pickingNorth = false\r\n private _pickHandler: ((e: MouseEvent) => void) | null = null\r\n\r\n private projector: StoreyProjector\r\n private renderer: FloorplanRenderer\r\n private camera: CameraController\r\n private chrome: ChromeController\r\n private clip: ClipController\r\n private grid: GridController\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(FloorplanTool.uuid, this)\r\n\r\n this.projector = new StoreyProjector(components)\r\n this.renderer = new FloorplanRenderer(components, this.projector)\r\n this.camera = new CameraController(components)\r\n this.chrome = new ChromeController(components)\r\n this.clip = new ClipController(components)\r\n this.grid = new GridController(components)\r\n\r\n const fragments = components.get(OBC.FragmentsManager)\r\n fragments.core.onModelLoaded.add((model) => {\r\n void this.generate(model.modelId)\r\n })\r\n }\r\n\r\n get drawings(): FloorplanEntry[] {\r\n return Array.from(this._entries.values()).sort(\r\n (a, b) => a.elevation - b.elevation,\r\n )\r\n }\r\n\r\n get activeDrawingId() {\r\n return this._activeId\r\n }\r\n\r\n get activeDrawing(): FloorplanEntry | null {\r\n return this._activeId ? this._entries.get(this._activeId) ?? null : null\r\n }\r\n\r\n /** Inject the world grid (typically `bimState.bim.grid`). Without this\r\n * the tool falls back to looking it up via `OBC.Grids`. */\r\n setGrid(grid: any | null) {\r\n this.grid.setGrid(grid)\r\n }\r\n\r\n /** Toggle visibility of a single per-class layer on an entry's drawing. */\r\n setLayerVisible(entryId: string, className: string, visible: boolean) {\r\n const entry = this._entries.get(entryId)\r\n if (!entry?.drawing || !entry.drawing.layers.has(className)) return\r\n entry.drawing.layers.setVisibility(className, visible)\r\n const meta = entry.layers.find((l) => l.className === className)\r\n if (meta) meta.visible = visible\r\n this._requestUpdate()\r\n this.onLayersChanged.trigger(entry)\r\n }\r\n\r\n /** Update the color (hex int, e.g. 0xff0000) of a per-class layer. Updates\r\n * both the projected-line color in the drawing AND the 3D highlight\r\n * color for FILL (slabs / roofs) and CUT (walls / columns / curtain-\r\n * walls) classes — so changing the IFCSLAB color repaints both its\r\n * outline and its solid fill underneath. */\r\n async setLayerColor(entryId: string, className: string, color: number) {\r\n const entry = this._entries.get(entryId)\r\n if (!entry?.drawing || !entry.drawing.layers.has(className)) return\r\n entry.drawing.layers.setColor(className, color)\r\n const meta = entry.layers.find((l) => l.className === className)\r\n if (meta) meta.color = color\r\n\r\n if (FILL_CLASSES.has(className) || CUT_CLASSES.has(className)) {\r\n await this._updateClassHighlight(entry, className, color)\r\n }\r\n\r\n this._requestUpdate()\r\n this.onLayersChanged.trigger(entry)\r\n }\r\n\r\n /** Prepend Fill / Cut group-color rows to entry.layers so the sidebar\r\n * shows group-level color pickers at the top of the layer list. Idempotent\r\n * — removes any existing group entries before re-inserting. */\r\n private _injectGroupColorLayers(entry: FloorplanEntry) {\r\n // Read current group colors from the renderer config so subsequent\r\n // setFillGroupColor / setCutGroupColor calls are reflected immediately.\r\n const groups = (this.renderer as any)._highlighter?.config?.groups ?? []\r\n const cutColor: number = (groups[0]?.color as number | undefined) ?? CUT_COLOR\r\n const fillColor: number = (groups[1]?.color as number | undefined) ?? FILL_COLOR\r\n const filtered = entry.layers.filter(\r\n (l) => l.className !== 'DrawingLayers.fill' && l.className !== 'DrawingLayers.cut',\r\n )\r\n entry.layers = [\r\n {\r\n className: 'DrawingLayers.cut',\r\n layerName: 'DrawingLayers.cut',\r\n visible: true,\r\n color: cutColor,\r\n itemCount: 0,\r\n displayKey: 'DrawingLayers.cut',\r\n },\r\n {\r\n className: 'DrawingLayers.fill',\r\n layerName: 'DrawingLayers.fill',\r\n visible: true,\r\n color: fillColor,\r\n itemCount: 0,\r\n displayKey: 'DrawingLayers.fill',\r\n },\r\n ...filtered,\r\n ]\r\n }\r\n\r\n /** Re-apply the 3D highlight (mesh fill color) for a single IFC class on\r\n * the active model, scoped to the active storey. Called after the user\r\n * edits a layer color in the sidebar so the 3D underlay stays in sync\r\n * with the line color. */\r\n private async _updateClassHighlight(\r\n entry: FloorplanEntry,\r\n className: string,\r\n color: number,\r\n ) {\r\n const fragments = this.components.get(OBC.FragmentsManager)\r\n const model = fragments.list.get(entry.modelId) as any\r\n if (!model) return\r\n\r\n // Match this specific class only (no partial-match regex).\r\n const map = await model.getItemsOfCategories([\r\n new RegExp(`^${className}$`),\r\n ])\r\n let ids = Object.values(map).flat() as number[]\r\n\r\n // Restrict to the active storey so changes don't leak into other floors.\r\n try {\r\n const storeyIds = await this.projector.getCachedStoreyIds(\r\n entry.modelId,\r\n entry.storeyLocalId,\r\n model,\r\n )\r\n if (storeyIds.length > 0) {\r\n const set = new Set(storeyIds)\r\n ids = ids.filter((id) => set.has(id))\r\n }\r\n } catch {\r\n // best-effort — fall through with unfiltered ids\r\n }\r\n\r\n if (ids.length === 0) return\r\n try {\r\n await model.highlight(ids, {\r\n color: new THREE.Color(color),\r\n opacity: 1,\r\n transparent: false,\r\n renderedFaces: 1,\r\n preserveOriginalMaterial: false,\r\n })\r\n fragments.core.update(true)\r\n } catch (error) {\r\n console.warn(\r\n `[FloorplanTool] 3D highlight update failed for ${className}:`,\r\n error,\r\n )\r\n }\r\n }\r\n\r\n /** Change the 3D fill color for ALL fill-group items (slabs / roofs) on\r\n * the active entry. Updates the layer metadata so the sidebar swatch\r\n * reflects the change immediately. */\r\n async setFillGroupColor(entryId: string, color: number) {\r\n const entry = this._entries.get(entryId)\r\n if (!entry) return\r\n await this.renderer.setFillColor(entryId, color)\r\n const meta = entry.layers.find((l) => l.className === 'DrawingLayers.fill')\r\n if (meta) meta.color = color\r\n this.onLayersChanged.trigger(entry)\r\n }\r\n\r\n /** Change the 3D cut color for ALL cut-group items (walls / columns /\r\n * curtain walls) on the active entry. */\r\n async setCutGroupColor(entryId: string, color: number) {\r\n const entry = this._entries.get(entryId)\r\n if (!entry) return\r\n await this.renderer.setCutColor(entryId, color)\r\n const meta = entry.layers.find((l) => l.className === 'DrawingLayers.cut')\r\n if (meta) meta.color = color\r\n this.onLayersChanged.trigger(entry)\r\n }\r\n\r\n /** Current true-north rotation (degrees, clockwise from default up). */\r\n get northAngle(): number {\r\n return this._northAngle\r\n }\r\n\r\n /** True while the line-pick mode is awaiting a click on the drawing. */\r\n get isPickingNorth(): boolean {\r\n return this._pickingNorth\r\n }\r\n\r\n /** Set the true-north rotation. Normalised to [0, 360). Applied to the\r\n * camera azimuth immediately if a floorplan is currently active so the\r\n * view re-orients. */\r\n setNorthAngle(degrees: number) {\r\n const normalized = ((degrees % 360) + 360) % 360\r\n if (this._northAngle === normalized) return\r\n this._northAngle = normalized\r\n this.onNorthAngleChanged.trigger(normalized)\r\n if (this._activeId) this._applyNorthToCamera()\r\n }\r\n\r\n /** Enter line-pick mode. The next click on the canvas raycasts against\r\n * the active drawing's lines; the hit line's bearing becomes the new\r\n * true-north angle. No-op if no floorplan is active. */\r\n startPickNorth() {\r\n if (this._pickingNorth || !this._activeId) return\r\n const world = this.components.get(CurrentWorld).world\r\n if (!world?.renderer) return\r\n const canvas = world.renderer.three.domElement\r\n this._pickHandler = (e: MouseEvent) => this._onPickClick(e)\r\n canvas.addEventListener('click', this._pickHandler)\r\n canvas.style.cursor = 'crosshair'\r\n this._pickingNorth = true\r\n this.onPickingNorthChanged.trigger(true)\r\n }\r\n\r\n /** Exit line-pick mode without applying any rotation. */\r\n cancelPickNorth() {\r\n if (!this._pickingNorth) return\r\n const world = this.components.get(CurrentWorld).world\r\n const canvas = world?.renderer?.three.domElement ?? null\r\n if (canvas && this._pickHandler) {\r\n canvas.removeEventListener('click', this._pickHandler)\r\n canvas.style.cursor = ''\r\n }\r\n this._pickHandler = null\r\n this._pickingNorth = false\r\n this.onPickingNorthChanged.trigger(false)\r\n }\r\n\r\n /** Click handler installed during line-pick mode. Raycasts against the\r\n * active drawing's `LineSegments` and applies the hit line's bearing\r\n * (clockwise from local north) as the new true-north angle. */\r\n private _onPickClick(event: MouseEvent) {\r\n const entry = this.activeDrawing\r\n if (!entry?.drawing) return\r\n const world = this.components.get(CurrentWorld).world\r\n if (!world?.renderer || !world?.camera) return\r\n const canvas = world.renderer.three.domElement\r\n const rect = canvas.getBoundingClientRect()\r\n const x = ((event.clientX - rect.left) / rect.width) * 2 - 1\r\n const y = -((event.clientY - rect.top) / rect.height) * 2 + 1\r\n const raycaster = new THREE.Raycaster()\r\n raycaster.params.Line.threshold = 0.5\r\n raycaster.setFromCamera(\r\n new THREE.Vector2(x, y),\r\n world.camera.three as THREE.Camera,\r\n )\r\n const hit = entry.drawing.raycast(raycaster.ray, null)\r\n if (!hit?.line) {\r\n // No line hit — keep picking active so the user can try again.\r\n return\r\n }\r\n // Drawing local space: X=right, Z=down-screen, Y=0. Compass bearing\r\n // (clockwise from local north which is -Z): atan2(dx, -dz).\r\n const dx = hit.line.end.x - hit.line.start.x\r\n const dz = hit.line.end.z - hit.line.start.z\r\n const bearingDeg = (Math.atan2(dx, -dz) * 180) / Math.PI\r\n this.setNorthAngle(bearingDeg)\r\n this.cancelPickNorth()\r\n }\r\n\r\n /** Push `_northAngle` into the camera-controls azimuth so the view\r\n * rotates around the active floor's centre. Called on every activate\r\n * and whenever the angle changes while a floorplan is active. */\r\n private _applyNorthToCamera() {\r\n const world = this.components.get(CurrentWorld).world\r\n if (!world?.camera?.controls) return\r\n const controls = world.camera.controls as any\r\n const radians = (this._northAngle * Math.PI) / 180\r\n if (typeof controls.rotateAzimuthTo === 'function') {\r\n void controls.rotateAzimuthTo(radians, true)\r\n } else {\r\n controls.azimuthAngle = radians\r\n }\r\n }\r\n\r\n /** Force a render update so layer visibility/color changes paint\r\n * immediately even when the camera is at rest. */\r\n private _requestUpdate() {\r\n try {\r\n const fragments = this.components.get(OBC.FragmentsManager)\r\n fragments.core.update(true)\r\n } catch {\r\n // FragmentsManager not initialized yet — no-op.\r\n }\r\n }\r\n\r\n /** Build one entry per IFC storey. Drawings are projected lazily on\r\n * first activation. */\r\n async generate(modelId: string): Promise<FloorplanEntry[]> {\r\n const fragments = this.components.get(OBC.FragmentsManager)\r\n const model = fragments.list.get(modelId)\r\n if (!model) return []\r\n\r\n const sourceWorld = this.components.get(CurrentWorld).world\r\n if (!sourceWorld) return []\r\n\r\n this._emit({ isLoading: true, stage: 'readingStoreys' })\r\n\r\n try {\r\n this.disposeEntriesForModel(modelId)\r\n await this.projector.ensureEditorReady()\r\n\r\n const storeyIds = Object.values(\r\n await model.getItemsOfCategories([/BUILDINGSTOREY/]),\r\n ).flat()\r\n if (storeyIds.length === 0) return []\r\n\r\n const storeysData = await model.getItemsData(storeyIds)\r\n const [, coordHeight] = await model.getCoordinates()\r\n const box = model.box\r\n const minY = box.min.y\r\n const maxY = box.max.y\r\n\r\n const created: FloorplanEntry[] = []\r\n\r\n // storeysData is parallel to storeyIds — preserve the mapping so each\r\n // entry remembers its storey's localId for later per-storey queries.\r\n for (let i = 0; i < storeysData.length; i++) {\r\n const storey = storeysData[i]\r\n const storeyLocalId = storeyIds[i]\r\n if (!('value' in storey.Name && 'value' in storey.Elevation)) continue\r\n\r\n const name = String(storey.Name.value)\r\n const rawElev = Number(storey.Elevation.value)\r\n const elevation = normalizeElevation(rawElev, coordHeight, minY, maxY)\r\n\r\n const entry: FloorplanEntry = {\r\n id: `${modelId}::${name}`,\r\n name,\r\n elevation,\r\n storeyLocalId,\r\n modelId,\r\n drawing: null,\r\n projected: false,\r\n layers: [],\r\n }\r\n this._entries.set(entry.id, entry)\r\n created.push(entry)\r\n }\r\n\r\n this.onDrawingsChanged.trigger(this.drawings)\r\n return created\r\n } finally {\r\n this._emit({ isLoading: false })\r\n }\r\n }\r\n\r\n /**\r\n * Activate a floorplan with progressive feedback. Camera, lighting and\r\n * clip planes are applied synchronously so the user gets an immediate\r\n * top-down framing. The slow steps — fill/cut highlighting and line\r\n * projection — run in parallel and emit stage events as each phase\r\n * completes, so a loading bar can advance through `init → resolve → cull\r\n * → fill → cut → project → done`.\r\n */\r\n async activate(id: string) {\r\n const entry = this._entries.get(id)\r\n if (!entry) return\r\n if (this._activeId === id) return\r\n\r\n const sourceWorld = this.components.get(CurrentWorld).world\r\n if (!sourceWorld) return\r\n\r\n const seq = ++this._activateSeq\r\n const wasActive = this._activeId !== null\r\n\r\n this._emit({ isLoading: true, stage: 'init' })\r\n\r\n // Mutual exclusion: deactivate any other view tool before claiming.\r\n await this._tryClaimCoordinator()\r\n if (seq !== this._activateSeq) return\r\n\r\n try {\r\n // ----- Phase 1: synchronous chrome + clip + camera (instant feedback)\r\n if (!wasActive) {\r\n this.chrome.applyLighting()\r\n this.chrome.applyDrawingBackground()\r\n // Lock to top-down (polar = 0).\r\n this.camera.lock(0)\r\n this.chrome.setCursor()\r\n this.chrome.disableHighlighter()\r\n this.chrome.hideGizmo()\r\n }\r\n\r\n // Section clip just above the drawing plane. StoreyProjector positions\r\n // the drawing at elevation + 1.5; clip normal is -Y so anything above\r\n // 1.55 is hidden but the drawing's own lines survive.\r\n this.clip.set(\r\n SLOT_SECTION,\r\n new THREE.Vector3(0, -1, 0),\r\n new THREE.Vector3(0, entry.elevation + 1.5 + 0.05, 0),\r\n )\r\n safeRun(() => this._applyLowerClip(entry), 'applyLower')\r\n safeRun(() => this.grid.hide(), 'hideGrid')\r\n\r\n // Hide previously visible drawings synchronously so they don't bleed\r\n // through while the new one is being projected.\r\n for (const other of this._entries.values()) {\r\n if (other.drawing && other.id !== entry.id) {\r\n other.drawing.three.visible = false\r\n }\r\n }\r\n\r\n this._frameCamera(entry)\r\n\r\n // Mark active immediately so the React UI shows Exit and the user can\r\n // bail out at any point during loading.\r\n this._activeId = id\r\n this.onActiveDrawingChanged.trigger(entry)\r\n\r\n // ----- Phase 2: parallel render + project (slow) -----\r\n const renderPromise = this.renderer.apply(entry, (stage) => {\r\n if (seq !== this._activateSeq) return\r\n this._emit({ isLoading: true, stage })\r\n })\r\n\r\n // Project once render-stage events have started flowing — this lets\r\n // the bar progress through resolve/cull/fill/cut while line generation\r\n // runs in the background. Once render completes we flip to 'project'.\r\n const projectPromise = this.projector.project(entry)\r\n\r\n renderPromise\r\n .then(() => {\r\n if (seq !== this._activateSeq) return\r\n this._emit({ isLoading: true, stage: 'project' })\r\n })\r\n .catch(() => {})\r\n\r\n await Promise.all([renderPromise, projectPromise])\r\n if (seq !== this._activateSeq) return\r\n\r\n // Drawing visible + editor swap (fast, post-projection).\r\n const editor = this.components.get(OBF.DrawingEditor)\r\n editor.activeDrawing = entry.drawing\r\n if (entry.drawing) {\r\n entry.drawing.three.visible = true\r\n }\r\n\r\n // Prepend Fill / Cut group-color controls so the sidebar shows them\r\n // at the top of the layers list.\r\n this._injectGroupColorLayers(entry)\r\n\r\n this._emit({ isLoading: false, stage: 'done' })\r\n } catch (error) {\r\n console.warn('[FloorplanTool] activate failed:', error)\r\n this._emit({ isLoading: false })\r\n }\r\n }\r\n\r\n /**\r\n * Exit floorplan mode. Each cleanup step is independently guarded so a\r\n * failure in one (e.g. resetHighlight on a huge model) cannot strand\r\n * camera, clip, or gizmo state.\r\n */\r\n async deactivate() {\r\n if (!this._activeId) return\r\n\r\n // Reset active state immediately so the React UI hides Exit and double\r\n // calls become no-ops.\r\n this._activeId = null\r\n this.onActiveDrawingChanged.trigger(null)\r\n\r\n // Drop any in-flight north-pick listener so the canvas stops\r\n // intercepting clicks once the floorplan is closed.\r\n safeRun(() => this.cancelPickNorth(), 'cancelPickNorth')\r\n\r\n // Clear editor + drawing visibility (synchronous, fast).\r\n safeRun(() => {\r\n const editor = this.components.get(OBF.DrawingEditor)\r\n editor.activeDrawing = null\r\n }, 'clear active drawing')\r\n for (const entry of this._entries.values()) {\r\n if (entry.drawing) {\r\n safeRun(() => {\r\n entry.drawing!.three.visible = false\r\n }, 'hide drawing')\r\n }\r\n }\r\n\r\n // Restore user-facing state FIRST so input feels responsive even if\r\n // model-render restoration takes a beat.\r\n safeRun(() => this.clip.removeAll(), 'removeClips')\r\n safeRun(() => this.grid.restore(), 'showGrid')\r\n safeRun(() => this.camera.unlock(), 'unlockCamera')\r\n safeRun(() => this.chrome.restoreCursor(), 'restoreCursor')\r\n safeRun(() => this.chrome.restoreHighlighter(), 'restoreHighlighter')\r\n safeRun(() => this.chrome.showGizmo(), 'showGizmo')\r\n safeRun(() => this.chrome.removeLighting(), 'removeLighting')\r\n safeRun(() => this.chrome.restoreBackground(), 'restoreBackground')\r\n\r\n // Slow async step last. Awaited so callers can chain on it, but failures\r\n // can't undo the synchronous restores above.\r\n await safeRunAsync(() => this.renderer.restore(), 'restoreModelRendering')\r\n\r\n safeRun(() => this._releaseCoordinator(), 'releaseCoordinator')\r\n }\r\n\r\n disposeEntriesForModel(modelId: string) {\r\n let touched = false\r\n for (const [id, entry] of this._entries) {\r\n if (entry.modelId !== modelId) continue\r\n if (this._activeId === id) void this.deactivate()\r\n disposeDrawing(this.components, entry.drawing)\r\n this._entries.delete(id)\r\n this.renderer.invalidateForEntry(id)\r\n touched = true\r\n }\r\n this.renderer.invalidateForModel(modelId)\r\n this.projector.invalidateForModel(modelId)\r\n if (touched) this.onDrawingsChanged.trigger(this.drawings)\r\n }\r\n\r\n dispose() {\r\n void this.deactivate()\r\n for (const entry of this._entries.values()) {\r\n disposeDrawing(this.components, entry.drawing)\r\n }\r\n this._entries.clear()\r\n this.onDrawingsChanged.trigger([])\r\n }\r\n\r\n private _emit(state: FloorplanLoadingState) {\r\n this.onGenerationStateChanged.trigger(state)\r\n }\r\n\r\n /** Mutual-exclusion claim. If ElevationsTool (or any other view tool)\r\n * was active, the coordinator awaits its deactivate() first. */\r\n private async _tryClaimCoordinator() {\r\n try {\r\n const coordinator = this.components.get(ViewModeCoordinator)\r\n await coordinator.claim(this)\r\n } catch {\r\n // ViewModeCoordinator not registered in this viewer — ignore.\r\n }\r\n }\r\n\r\n private _releaseCoordinator() {\r\n try {\r\n this.components.get(ViewModeCoordinator).release(this)\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n /** Lower clip plane sits just under the active floor's slab (normal +Y),\r\n * so ceiling fixtures / lights / equipment of the storey below never\r\n * bleed into the projection or the 3D underlay. */\r\n private _applyLowerClip(entry: FloorplanEntry) {\r\n const cutY = entry.elevation - 0.5\r\n this.clip.set(\r\n SLOT_LOWER,\r\n new THREE.Vector3(0, 1, 0),\r\n new THREE.Vector3(0, cutY, 0),\r\n )\r\n }\r\n\r\n /** Frame the camera over the active floor in orthographic top-down,\r\n * then re-apply the building's true-north rotation so storey switches\r\n * preserve the user's chosen orientation. */\r\n private _frameCamera(entry: FloorplanEntry) {\r\n const fragments = this.components.get(OBC.FragmentsManager)\r\n const model = fragments.list.get(entry.modelId)\r\n if (!model) return\r\n const center = new THREE.Vector3()\r\n const size = new THREE.Vector3()\r\n model.box.getCenter(center)\r\n model.box.getSize(size)\r\n const span = Math.max(size.x, size.z, 10)\r\n const target = new THREE.Vector3(\r\n center.x,\r\n entry.elevation + 1.2,\r\n center.z,\r\n )\r\n this.camera.frame(target, new THREE.Vector3(0, -1, 0), span)\r\n this._applyNorthToCamera()\r\n }\r\n\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/bim/src/FloorplanTool/index.ts"],"sourcesContent":["import * as OBC from '@thatopen/components'\r\nimport * as OBF from '@thatopen/components-front'\r\nimport * as THREE from 'three'\r\nimport { CurrentWorld } from '../CurrentWorld'\r\nimport { CameraController } from '../lib/CameraController'\r\nimport { ChromeController } from '../lib/ChromeController'\r\nimport { ClipController } from '../lib/ClipController'\r\nimport { CUT_CLASSES, FILL_CLASSES } from '../lib/drawingLayers'\r\nimport { disposeDrawing } from '../lib/drawingProjection'\r\nimport { GridController } from '../lib/GridController'\r\nimport { safeRun, safeRunAsync } from '../lib/safeRun'\r\nimport { stagePercent, ViewLoadingState } from '../lib/viewSection'\r\nimport { ViewModeCoordinator } from '../lib/ViewModeCoordinator'\r\nimport { FloorplanRenderer, RenderStage } from './src/FloorplanRenderer'\r\nimport { StoreyProjector } from './src/StoreyProjector'\r\nimport { CUT_COLOR, FILL_COLOR, FloorplanEntry, FLOORPLAN_TOOL_UUID } from './src/types'\r\nimport { normalizeElevation } from './src/utils'\r\n\r\nconst SLOT_SECTION = 'floorplan:section'\r\nconst SLOT_LOWER = 'floorplan:lower'\r\n\r\nexport type { FloorplanEntry } from './src/types'\r\n\r\nexport type FloorplanLoadingStage =\r\n | 'init'\r\n | 'readingStoreys'\r\n | RenderStage\r\n | 'project'\r\n | 'done'\r\n\r\nexport type FloorplanLoadingState = ViewLoadingState<FloorplanLoadingStage>\r\n\r\nconst STAGE_PERCENT: Record<FloorplanLoadingStage, number> = {\r\n init: 5,\r\n readingStoreys: 10,\r\n resolve: 20,\r\n cull: 35,\r\n switching: 35,\r\n cut: 55,\r\n fill: 75,\r\n project: 90,\r\n done: 100,\r\n}\r\n\r\nexport function getFloorplanStagePercent(\r\n stage: FloorplanLoadingStage | undefined,\r\n): number {\r\n return stagePercent(stage, STAGE_PERCENT)\r\n}\r\n\r\nexport class FloorplanTool extends OBC.Component {\r\n static uuid = FLOORPLAN_TOOL_UUID\r\n\r\n enabled = false\r\n\r\n readonly onDrawingsChanged = new OBC.Event<FloorplanEntry[]>()\r\n readonly onActiveDrawingChanged = new OBC.Event<FloorplanEntry | null>()\r\n readonly onGenerationStateChanged = new OBC.Event<FloorplanLoadingState>()\r\n /** Fires whenever an entry's layer metadata changes (toggle / color). */\r\n readonly onLayersChanged = new OBC.Event<FloorplanEntry>()\r\n /** Fires when the building's true-north angle (in degrees) is updated. */\r\n readonly onNorthAngleChanged = new OBC.Event<number>()\r\n /** Fires when line-pick mode toggles on/off. */\r\n readonly onPickingNorthChanged = new OBC.Event<boolean>()\r\n\r\n private _entries = new Map<string, FloorplanEntry>()\r\n private _activeId: string | null = null\r\n /** Sequence guard: when activate() is called multiple times in quick\r\n * succession (e.g. user clicks two storeys), only the last call applies\r\n * its final state. Prior calls bail out at await boundaries. */\r\n private _activateSeq = 0\r\n /** True-north rotation in degrees (clockwise from default screen-up).\r\n * Persists across storey switches but resets to 0 on tool dispose. */\r\n private _northAngle = 0\r\n /** True while the user is drawing the two-point north line. */\r\n private _pickingNorth = false\r\n private _pickHandler: ((e: MouseEvent) => void) | null = null\r\n private _moveHandler: ((e: MouseEvent) => void) | null = null\r\n private _keyHandler: ((e: KeyboardEvent) => void) | null = null\r\n /** First clicked point of the north line, in drawing-local space (Y=0).\r\n * Null until the user places the first point. */\r\n private _drawStart: THREE.Vector3 | null = null\r\n /** Cached snap targets — projected-geometry vertices flattened as\r\n * [x0,z0, x1,z1, …] in drawing-local space. Rebuilt each time draw mode\r\n * starts so it reflects the active storey's lines. */\r\n private _snapVerts: Float32Array | null = null\r\n /** Rubber-band preview line + cursor snap marker, parented to the active\r\n * drawing so they inherit its world transform. */\r\n private _previewLine: THREE.Line | null = null\r\n private _snapMarker: THREE.LineSegments | null = null\r\n /** Canvas cursor before draw mode swapped in the crosshair — typically the\r\n * floorplan-mode \"grab\" hand. Restored (not cleared) on exit so panning\r\n * keeps the hand; only leaving floorplan mode reverts to the default. */\r\n private _savedPickCursor: string | null = null\r\n\r\n private projector: StoreyProjector\r\n private renderer: FloorplanRenderer\r\n private camera: CameraController\r\n private chrome: ChromeController\r\n private clip: ClipController\r\n private grid: GridController\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(FloorplanTool.uuid, this)\r\n\r\n this.projector = new StoreyProjector(components)\r\n this.renderer = new FloorplanRenderer(components, this.projector)\r\n this.camera = new CameraController(components)\r\n this.chrome = new ChromeController(components)\r\n this.clip = new ClipController(components)\r\n this.grid = new GridController(components)\r\n\r\n const fragments = components.get(OBC.FragmentsManager)\r\n fragments.core.onModelLoaded.add((model) => {\r\n void this.generate(model.modelId)\r\n })\r\n }\r\n\r\n get drawings(): FloorplanEntry[] {\r\n return Array.from(this._entries.values()).sort(\r\n (a, b) => a.elevation - b.elevation,\r\n )\r\n }\r\n\r\n get activeDrawingId() {\r\n return this._activeId\r\n }\r\n\r\n get activeDrawing(): FloorplanEntry | null {\r\n return this._activeId ? this._entries.get(this._activeId) ?? null : null\r\n }\r\n\r\n /** Inject the world grid (typically `bimState.bim.grid`). Without this\r\n * the tool falls back to looking it up via `OBC.Grids`. */\r\n setGrid(grid: any | null) {\r\n this.grid.setGrid(grid)\r\n }\r\n\r\n /** Toggle visibility of a single per-class layer on an entry's drawing. */\r\n setLayerVisible(entryId: string, className: string, visible: boolean) {\r\n const entry = this._entries.get(entryId)\r\n if (!entry?.drawing || !entry.drawing.layers.has(className)) return\r\n entry.drawing.layers.setVisibility(className, visible)\r\n const meta = entry.layers.find((l) => l.className === className)\r\n if (meta) meta.visible = visible\r\n this._requestUpdate()\r\n this.onLayersChanged.trigger(entry)\r\n }\r\n\r\n /** Update the color (hex int, e.g. 0xff0000) of a per-class layer. Updates\r\n * both the projected-line color in the drawing AND the 3D highlight\r\n * color for FILL (slabs / roofs) and CUT (walls / columns / curtain-\r\n * walls) classes — so changing the IFCSLAB color repaints both its\r\n * outline and its solid fill underneath. */\r\n async setLayerColor(entryId: string, className: string, color: number) {\r\n const entry = this._entries.get(entryId)\r\n if (!entry?.drawing || !entry.drawing.layers.has(className)) return\r\n entry.drawing.layers.setColor(className, color)\r\n const meta = entry.layers.find((l) => l.className === className)\r\n if (meta) meta.color = color\r\n\r\n if (FILL_CLASSES.has(className) || CUT_CLASSES.has(className)) {\r\n await this._updateClassHighlight(entry, className, color)\r\n }\r\n\r\n this._requestUpdate()\r\n this.onLayersChanged.trigger(entry)\r\n }\r\n\r\n /** Prepend Fill / Cut group-color rows to entry.layers so the sidebar\r\n * shows group-level color pickers at the top of the layer list. Idempotent\r\n * — removes any existing group entries before re-inserting. */\r\n private _injectGroupColorLayers(entry: FloorplanEntry) {\r\n // Read current group colors from the renderer config so subsequent\r\n // setFillGroupColor / setCutGroupColor calls are reflected immediately.\r\n const groups = (this.renderer as any)._highlighter?.config?.groups ?? []\r\n const cutColor: number = (groups[0]?.color as number | undefined) ?? CUT_COLOR\r\n const fillColor: number = (groups[1]?.color as number | undefined) ?? FILL_COLOR\r\n const filtered = entry.layers.filter(\r\n (l) => l.className !== 'DrawingLayers.fill' && l.className !== 'DrawingLayers.cut',\r\n )\r\n entry.layers = [\r\n {\r\n className: 'DrawingLayers.cut',\r\n layerName: 'DrawingLayers.cut',\r\n visible: true,\r\n color: cutColor,\r\n itemCount: 0,\r\n displayKey: 'DrawingLayers.cut',\r\n },\r\n {\r\n className: 'DrawingLayers.fill',\r\n layerName: 'DrawingLayers.fill',\r\n visible: true,\r\n color: fillColor,\r\n itemCount: 0,\r\n displayKey: 'DrawingLayers.fill',\r\n },\r\n ...filtered,\r\n ]\r\n }\r\n\r\n /** Re-apply the 3D highlight (mesh fill color) for a single IFC class on\r\n * the active model, scoped to the active storey. Called after the user\r\n * edits a layer color in the sidebar so the 3D underlay stays in sync\r\n * with the line color. */\r\n private async _updateClassHighlight(\r\n entry: FloorplanEntry,\r\n className: string,\r\n color: number,\r\n ) {\r\n const fragments = this.components.get(OBC.FragmentsManager)\r\n const model = fragments.list.get(entry.modelId) as any\r\n if (!model) return\r\n\r\n // Match this specific class only (no partial-match regex).\r\n const map = await model.getItemsOfCategories([\r\n new RegExp(`^${className}$`),\r\n ])\r\n let ids = Object.values(map).flat() as number[]\r\n\r\n // Restrict to the active storey so changes don't leak into other floors.\r\n try {\r\n const storeyIds = await this.projector.getCachedStoreyIds(\r\n entry.modelId,\r\n entry.storeyLocalId,\r\n model,\r\n )\r\n if (storeyIds.length > 0) {\r\n const set = new Set(storeyIds)\r\n ids = ids.filter((id) => set.has(id))\r\n }\r\n } catch {\r\n // best-effort — fall through with unfiltered ids\r\n }\r\n\r\n if (ids.length === 0) return\r\n try {\r\n await model.highlight(ids, {\r\n color: new THREE.Color(color),\r\n opacity: 1,\r\n transparent: false,\r\n renderedFaces: 1,\r\n preserveOriginalMaterial: false,\r\n })\r\n fragments.core.update(true)\r\n } catch (error) {\r\n console.warn(\r\n `[FloorplanTool] 3D highlight update failed for ${className}:`,\r\n error,\r\n )\r\n }\r\n }\r\n\r\n /** Change the 3D fill color for ALL fill-group items (slabs / roofs) on\r\n * the active entry. Updates the layer metadata so the sidebar swatch\r\n * reflects the change immediately. */\r\n async setFillGroupColor(entryId: string, color: number) {\r\n const entry = this._entries.get(entryId)\r\n if (!entry) return\r\n await this.renderer.setFillColor(entryId, color)\r\n const meta = entry.layers.find((l) => l.className === 'DrawingLayers.fill')\r\n if (meta) meta.color = color\r\n this.onLayersChanged.trigger(entry)\r\n }\r\n\r\n /** Change the 3D cut color for ALL cut-group items (walls / columns /\r\n * curtain walls) on the active entry. */\r\n async setCutGroupColor(entryId: string, color: number) {\r\n const entry = this._entries.get(entryId)\r\n if (!entry) return\r\n await this.renderer.setCutColor(entryId, color)\r\n const meta = entry.layers.find((l) => l.className === 'DrawingLayers.cut')\r\n if (meta) meta.color = color\r\n this.onLayersChanged.trigger(entry)\r\n }\r\n\r\n /** Current true-north rotation (degrees, clockwise from default up). */\r\n get northAngle(): number {\r\n return this._northAngle\r\n }\r\n\r\n /** True while the line-pick mode is awaiting a click on the drawing. */\r\n get isPickingNorth(): boolean {\r\n return this._pickingNorth\r\n }\r\n\r\n /** Set the true-north rotation. Normalised to [0, 360). Applied to the\r\n * camera azimuth immediately if a floorplan is currently active so the\r\n * view re-orients. */\r\n setNorthAngle(degrees: number) {\r\n const normalized = ((degrees % 360) + 360) % 360\r\n if (this._northAngle === normalized) return\r\n this._northAngle = normalized\r\n this.onNorthAngleChanged.trigger(normalized)\r\n if (this._activeId) this._applyNorthToCamera()\r\n }\r\n\r\n /** Enter two-point draw mode. The user clicks two points on the active\r\n * floorplan (each snapped to the nearest projected-geometry vertex); the\r\n * bearing of the segment between them becomes the new true-north angle.\r\n * A rubber-band preview + snap marker track the cursor; Esc cancels.\r\n * No-op if no floorplan is active. */\r\n startPickNorth() {\r\n if (this._pickingNorth || !this._activeId) return\r\n const entry = this.activeDrawing\r\n const world = this.components.get(CurrentWorld).world\r\n if (!entry?.drawing || !world?.renderer) return\r\n const canvas = world.renderer.three.domElement\r\n\r\n this._drawStart = null\r\n this._snapVerts = this._collectSnapVertices(entry)\r\n\r\n this._pickHandler = (e: MouseEvent) => this._onPickClick(e)\r\n this._moveHandler = (e: MouseEvent) => this._onPickMove(e)\r\n this._keyHandler = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') this.cancelPickNorth()\r\n }\r\n canvas.addEventListener('click', this._pickHandler)\r\n canvas.addEventListener('mousemove', this._moveHandler)\r\n window.addEventListener('keydown', this._keyHandler)\r\n this._savedPickCursor = canvas.style.cursor\r\n canvas.style.cursor = 'crosshair'\r\n this._pickingNorth = true\r\n this.onPickingNorthChanged.trigger(true)\r\n }\r\n\r\n /** Exit draw mode without applying any rotation. Tears down the canvas\r\n * listeners and removes the rubber-band preview. */\r\n cancelPickNorth() {\r\n if (!this._pickingNorth) return\r\n const world = this.components.get(CurrentWorld).world\r\n const canvas = world?.renderer?.three.domElement ?? null\r\n if (canvas) {\r\n if (this._pickHandler) canvas.removeEventListener('click', this._pickHandler)\r\n if (this._moveHandler) canvas.removeEventListener('mousemove', this._moveHandler)\r\n // Restore the floorplan-mode cursor (the grab hand), not the OS default\r\n // — that only comes back when the tool deactivates.\r\n canvas.style.cursor = this._savedPickCursor ?? 'grab'\r\n }\r\n this._savedPickCursor = null\r\n if (this._keyHandler) window.removeEventListener('keydown', this._keyHandler)\r\n this._pickHandler = null\r\n this._moveHandler = null\r\n this._keyHandler = null\r\n this._drawStart = null\r\n this._snapVerts = null\r\n this._clearPreview()\r\n this._pickingNorth = false\r\n this.onPickingNorthChanged.trigger(false)\r\n }\r\n\r\n /** Click handler for draw mode. First click places the start point; the\r\n * second rotates the view so the drawn segment becomes parallel to the\r\n * screen X-axis (horizontal). Both points lie on the drawing's horizontal\r\n * plane (Y=0), so only the in-plane direction matters. */\r\n private _onPickClick(event: MouseEvent) {\r\n const entry = this.activeDrawing\r\n if (!entry?.drawing) return\r\n const local = this._eventToLocalPoint(event, entry)\r\n if (!local) return\r\n const point = this._snap(local)\r\n\r\n if (!this._drawStart) {\r\n this._drawStart = point\r\n this._updatePreview(entry, point)\r\n this._requestUpdate()\r\n return\r\n }\r\n\r\n // Ignore a degenerate (near-zero-length) segment so a stray double-click\r\n // can't set a bogus angle.\r\n const dx = point.x - this._drawStart.x\r\n const dz = point.z - this._drawStart.z\r\n if (dx * dx + dz * dz < 1e-6) return\r\n\r\n // Resolve the segment in WORLD space (the drawing has its own orientTo\r\n // rotation, so a drawing-local bearing wouldn't match the world-space\r\n // camera azimuth), then rotate the view to lay it on the X-axis.\r\n const drawing = entry.drawing\r\n drawing.three.updateWorldMatrix(true, false)\r\n const aWorld = drawing.three.localToWorld(this._drawStart.clone())\r\n const bWorld = drawing.three.localToWorld(point.clone())\r\n const azimuthDeg = this._azimuthToAlignLineToX(aWorld, bWorld)\r\n if (azimuthDeg !== null) this.setNorthAngle(azimuthDeg)\r\n this.cancelPickNorth()\r\n }\r\n\r\n /** Camera azimuth (degrees) that rotates the top-down view so the world\r\n * segment a→b lands parallel to the screen X-axis (horizontal).\r\n *\r\n * Derivation: with the standard Y-up spherical convention camera-controls\r\n * uses, the camera's screen-right world direction has angle ρ = −θ + C\r\n * (θ = azimuth). We measure ρ₀ and θ₀ from the live camera so C cancels —\r\n * making the result independent of the drawing's orientation and the\r\n * controls' up-space. We then solve ρ(θ) = λ (the segment's world angle),\r\n * giving θ = ρ₀ + θ₀ − λ. Returns null if the controls are unavailable. */\r\n private _azimuthToAlignLineToX(\r\n aWorld: THREE.Vector3,\r\n bWorld: THREE.Vector3,\r\n ): number | null {\r\n const world = this.components.get(CurrentWorld).world\r\n const cam = world?.camera?.three as THREE.Camera | undefined\r\n const controls = world?.camera?.controls as any\r\n if (!cam || typeof controls?.azimuthAngle !== 'number') return null\r\n\r\n const lx = bWorld.x - aWorld.x\r\n const lz = bWorld.z - aWorld.z\r\n if (lx * lx + lz * lz < 1e-12) return null\r\n const lambda = Math.atan2(lz, lx)\r\n\r\n // Screen-right = camera local +X axis = first column of matrixWorld.\r\n cam.updateMatrixWorld()\r\n const e = cam.matrixWorld.elements\r\n const rho0 = Math.atan2(e[2], e[0])\r\n const theta0 = controls.azimuthAngle as number\r\n\r\n return ((rho0 + theta0 - lambda) * 180) / Math.PI\r\n }\r\n\r\n /** Mousemove handler for draw mode — keeps the snap marker (and, after the\r\n * first click, the rubber-band line) glued to the cursor's snapped point. */\r\n private _onPickMove(event: MouseEvent) {\r\n const entry = this.activeDrawing\r\n if (!entry?.drawing) return\r\n const local = this._eventToLocalPoint(event, entry)\r\n if (!local) return\r\n this._updatePreview(entry, this._snap(local))\r\n this._requestUpdate()\r\n }\r\n\r\n /** Convert a canvas mouse event into a point on the drawing plane, in\r\n * drawing-local space (XZ plane, Y=0). Casts a ray from the active camera,\r\n * transforms it into the drawing's local frame, and intersects Y=0. */\r\n private _eventToLocalPoint(\r\n event: MouseEvent,\r\n entry: FloorplanEntry,\r\n ): THREE.Vector3 | null {\r\n const world = this.components.get(CurrentWorld).world\r\n if (!world?.renderer || !world.camera || !entry.drawing) return null\r\n const canvas = world.renderer.three.domElement\r\n const rect = canvas.getBoundingClientRect()\r\n const x = ((event.clientX - rect.left) / rect.width) * 2 - 1\r\n const y = -((event.clientY - rect.top) / rect.height) * 2 + 1\r\n const raycaster = new THREE.Raycaster()\r\n raycaster.setFromCamera(\r\n new THREE.Vector2(x, y),\r\n world.camera.three as THREE.Camera,\r\n )\r\n const drawing = entry.drawing\r\n drawing.three.updateWorldMatrix(true, false)\r\n const inv = new THREE.Matrix4().copy(drawing.three.matrixWorld).invert()\r\n const origin = raycaster.ray.origin.clone().applyMatrix4(inv)\r\n const dir = raycaster.ray.direction.clone().transformDirection(inv)\r\n if (Math.abs(dir.y) < 1e-9) return null\r\n const t = -origin.y / dir.y\r\n if (!Number.isFinite(t)) return null\r\n return new THREE.Vector3(\r\n origin.x + dir.x * t,\r\n 0,\r\n origin.z + dir.z * t,\r\n )\r\n }\r\n\r\n /** Snap a drawing-local point to the nearest projected-geometry vertex\r\n * within the snap radius; returns the point unchanged if none is close. */\r\n private _snap(local: THREE.Vector3): THREE.Vector3 {\r\n const verts = this._snapVerts\r\n if (!verts) return local\r\n const radius = this._snapRadiusLocal()\r\n let best = -1\r\n let bestD2 = radius * radius\r\n for (let i = 0; i < verts.length; i += 2) {\r\n const dx = verts[i] - local.x\r\n const dz = verts[i + 1] - local.z\r\n const d2 = dx * dx + dz * dz\r\n if (d2 < bestD2) {\r\n bestD2 = d2\r\n best = i\r\n }\r\n }\r\n if (best < 0) return local\r\n return new THREE.Vector3(verts[best], 0, verts[best + 1])\r\n }\r\n\r\n /** Snap radius in drawing-local units (= world units; the drawing is\r\n * unscaled), derived from a fixed ~12 px tolerance at the current ortho\r\n * zoom so it feels consistent as the user zooms. */\r\n private _snapRadiusLocal(): number {\r\n const SNAP_PX = 12\r\n const FALLBACK = 0.5\r\n const world = this.components.get(CurrentWorld).world\r\n const cam = world?.camera?.three as any\r\n const canvas = world?.renderer?.three.domElement\r\n if (!cam?.isOrthographicCamera || !canvas) return FALLBACK\r\n const worldWidth = (cam.right - cam.left) / (cam.zoom || 1)\r\n const worldPerPixel = worldWidth / Math.max(1, canvas.clientWidth)\r\n return worldPerPixel * SNAP_PX || FALLBACK\r\n }\r\n\r\n /** Flatten every visible projected line vertex into drawing-local [x,z]\r\n * pairs for endpoint snapping. Each vertex is transformed through its\r\n * owning object's world matrix into the drawing's local frame, so it\r\n * works regardless of how layers nest the `LineSegments`. */\r\n private _collectSnapVertices(entry: FloorplanEntry): Float32Array | null {\r\n const drawing = entry.drawing\r\n if (!drawing) return null\r\n drawing.three.updateWorldMatrix(true, true)\r\n const inv = new THREE.Matrix4().copy(drawing.three.matrixWorld).invert()\r\n const v = new THREE.Vector3()\r\n const out: number[] = []\r\n drawing.three.traverse((child: THREE.Object3D) => {\r\n if (!(child instanceof THREE.LineSegments) || !child.visible) return\r\n if (child.userData?.isDimension) return\r\n const pos = child.geometry?.attributes?.position as\r\n | THREE.BufferAttribute\r\n | undefined\r\n if (!pos) return\r\n for (let i = 0; i < pos.count; i++) {\r\n v.set(pos.getX(i), pos.getY(i), pos.getZ(i))\r\n .applyMatrix4(child.matrixWorld)\r\n .applyMatrix4(inv)\r\n out.push(v.x, v.z)\r\n }\r\n })\r\n return out.length ? new Float32Array(out) : null\r\n }\r\n\r\n /** Update (creating on first use) the cursor snap marker and, once the\r\n * first point is placed, the rubber-band line from start → cursor. Both\r\n * live in drawing-local space, Y=0. */\r\n private _updatePreview(entry: FloorplanEntry, current: THREE.Vector3) {\r\n const drawing = entry.drawing\r\n if (!drawing) return\r\n const COLOR = 0x2563eb\r\n\r\n if (!this._snapMarker) {\r\n // Unit-armed cross scaled to the snap radius so it reads as the\r\n // current snap tolerance.\r\n const geom = new THREE.BufferGeometry()\r\n geom.setAttribute(\r\n 'position',\r\n new THREE.Float32BufferAttribute(\r\n new Float32Array([-1, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 1]),\r\n 3,\r\n ),\r\n )\r\n this._snapMarker = new THREE.LineSegments(\r\n geom,\r\n new THREE.LineBasicMaterial({ color: COLOR }),\r\n )\r\n this._snapMarker.renderOrder = 999\r\n drawing.three.add(this._snapMarker)\r\n }\r\n const arm = this._snapRadiusLocal()\r\n this._snapMarker.scale.setScalar(arm)\r\n this._snapMarker.position.set(current.x, 0, current.z)\r\n this._snapMarker.visible = true\r\n\r\n if (!this._drawStart) {\r\n if (this._previewLine) this._previewLine.visible = false\r\n return\r\n }\r\n if (!this._previewLine) {\r\n const geom = new THREE.BufferGeometry()\r\n geom.setAttribute(\r\n 'position',\r\n new THREE.Float32BufferAttribute(new Float32Array(6), 3),\r\n )\r\n this._previewLine = new THREE.Line(\r\n geom,\r\n new THREE.LineBasicMaterial({ color: COLOR }),\r\n )\r\n this._previewLine.renderOrder = 999\r\n drawing.three.add(this._previewLine)\r\n }\r\n const pos = this._previewLine.geometry.attributes\r\n .position as THREE.BufferAttribute\r\n pos.setXYZ(0, this._drawStart.x, 0, this._drawStart.z)\r\n pos.setXYZ(1, current.x, 0, current.z)\r\n pos.needsUpdate = true\r\n this._previewLine.visible = true\r\n }\r\n\r\n /** Remove + dispose the preview line and snap marker. Uses each object's\r\n * own parent so it stays correct even when called from deactivate(),\r\n * where `_activeId` is already null. */\r\n private _clearPreview() {\r\n if (this._previewLine) {\r\n this._previewLine.parent?.remove(this._previewLine)\r\n this._previewLine.geometry.dispose()\r\n const m = this._previewLine.material\r\n if (!Array.isArray(m)) m.dispose()\r\n this._previewLine = null\r\n }\r\n if (this._snapMarker) {\r\n this._snapMarker.parent?.remove(this._snapMarker)\r\n this._snapMarker.geometry.dispose()\r\n const m = this._snapMarker.material\r\n if (!Array.isArray(m)) m.dispose()\r\n this._snapMarker = null\r\n }\r\n this._requestUpdate()\r\n }\r\n\r\n /** Push `_northAngle` into the camera-controls azimuth so the view\r\n * rotates around the active floor's centre. Called on every activate\r\n * and whenever the angle changes while a floorplan is active. */\r\n private _applyNorthToCamera() {\r\n const world = this.components.get(CurrentWorld).world\r\n if (!world?.camera?.controls) return\r\n const controls = world.camera.controls as any\r\n const radians = (this._northAngle * Math.PI) / 180\r\n if (typeof controls.rotateAzimuthTo === 'function') {\r\n void controls.rotateAzimuthTo(radians, true)\r\n } else {\r\n controls.azimuthAngle = radians\r\n }\r\n }\r\n\r\n /** Force a render update so layer visibility/color changes paint\r\n * immediately even when the camera is at rest. */\r\n private _requestUpdate() {\r\n try {\r\n const fragments = this.components.get(OBC.FragmentsManager)\r\n fragments.core.update(true)\r\n } catch {\r\n // FragmentsManager not initialized yet — no-op.\r\n }\r\n }\r\n\r\n /** Build one entry per IFC storey. Drawings are projected lazily on\r\n * first activation. */\r\n async generate(modelId: string): Promise<FloorplanEntry[]> {\r\n const fragments = this.components.get(OBC.FragmentsManager)\r\n const model = fragments.list.get(modelId)\r\n if (!model) return []\r\n\r\n const sourceWorld = this.components.get(CurrentWorld).world\r\n if (!sourceWorld) return []\r\n\r\n this._emit({ isLoading: true, stage: 'readingStoreys' })\r\n\r\n try {\r\n this.disposeEntriesForModel(modelId)\r\n await this.projector.ensureEditorReady()\r\n\r\n const storeyIds = Object.values(\r\n await model.getItemsOfCategories([/BUILDINGSTOREY/]),\r\n ).flat()\r\n if (storeyIds.length === 0) return []\r\n\r\n const storeysData = await model.getItemsData(storeyIds)\r\n const [, coordHeight] = await model.getCoordinates()\r\n const box = model.box\r\n const minY = box.min.y\r\n const maxY = box.max.y\r\n\r\n const created: FloorplanEntry[] = []\r\n\r\n // storeysData is parallel to storeyIds — preserve the mapping so each\r\n // entry remembers its storey's localId for later per-storey queries.\r\n for (let i = 0; i < storeysData.length; i++) {\r\n const storey = storeysData[i]\r\n const storeyLocalId = storeyIds[i]\r\n if (!('value' in storey.Name && 'value' in storey.Elevation)) continue\r\n\r\n const name = String(storey.Name.value)\r\n const rawElev = Number(storey.Elevation.value)\r\n const elevation = normalizeElevation(rawElev, coordHeight, minY, maxY)\r\n\r\n const entry: FloorplanEntry = {\r\n id: `${modelId}::${name}`,\r\n name,\r\n elevation,\r\n storeyLocalId,\r\n modelId,\r\n drawing: null,\r\n projected: false,\r\n layers: [],\r\n }\r\n this._entries.set(entry.id, entry)\r\n created.push(entry)\r\n }\r\n\r\n this.onDrawingsChanged.trigger(this.drawings)\r\n return created\r\n } finally {\r\n this._emit({ isLoading: false })\r\n }\r\n }\r\n\r\n /**\r\n * Activate a floorplan with progressive feedback. Camera, lighting and\r\n * clip planes are applied synchronously so the user gets an immediate\r\n * top-down framing. The slow steps — fill/cut highlighting and line\r\n * projection — run in parallel and emit stage events as each phase\r\n * completes, so a loading bar can advance through `init → resolve → cull\r\n * → fill → cut → project → done`.\r\n */\r\n async activate(id: string) {\r\n const entry = this._entries.get(id)\r\n if (!entry) return\r\n if (this._activeId === id) return\r\n\r\n const sourceWorld = this.components.get(CurrentWorld).world\r\n if (!sourceWorld) return\r\n\r\n const seq = ++this._activateSeq\r\n const wasActive = this._activeId !== null\r\n\r\n this._emit({ isLoading: true, stage: 'init' })\r\n\r\n // Mutual exclusion: deactivate any other view tool before claiming.\r\n await this._tryClaimCoordinator()\r\n if (seq !== this._activateSeq) return\r\n\r\n try {\r\n // ----- Phase 1: synchronous chrome + clip + camera (instant feedback)\r\n if (!wasActive) {\r\n this.chrome.applyLighting()\r\n this.chrome.applyDrawingBackground()\r\n // Lock to top-down (polar = 0).\r\n this.camera.lock(0)\r\n this.chrome.setCursor()\r\n this.chrome.disableHighlighter()\r\n this.chrome.hideGizmo()\r\n }\r\n\r\n // Section clip just above the drawing plane. StoreyProjector positions\r\n // the drawing at elevation + 1.5; clip normal is -Y so anything above\r\n // 1.55 is hidden but the drawing's own lines survive.\r\n this.clip.set(\r\n SLOT_SECTION,\r\n new THREE.Vector3(0, -1, 0),\r\n new THREE.Vector3(0, entry.elevation + 1.5 + 0.05, 0),\r\n )\r\n safeRun(() => this._applyLowerClip(entry), 'applyLower')\r\n safeRun(() => this.grid.hide(), 'hideGrid')\r\n\r\n // Hide previously visible drawings synchronously so they don't bleed\r\n // through while the new one is being projected.\r\n for (const other of this._entries.values()) {\r\n if (other.drawing && other.id !== entry.id) {\r\n other.drawing.three.visible = false\r\n }\r\n }\r\n\r\n this._frameCamera(entry)\r\n\r\n // Mark active immediately so the React UI shows Exit and the user can\r\n // bail out at any point during loading.\r\n this._activeId = id\r\n this.onActiveDrawingChanged.trigger(entry)\r\n\r\n // ----- Phase 2: parallel render + project (slow) -----\r\n const renderPromise = this.renderer.apply(entry, (stage) => {\r\n if (seq !== this._activateSeq) return\r\n this._emit({ isLoading: true, stage })\r\n })\r\n\r\n // Project once render-stage events have started flowing — this lets\r\n // the bar progress through resolve/cull/fill/cut while line generation\r\n // runs in the background. Once render completes we flip to 'project'.\r\n const projectPromise = this.projector.project(entry)\r\n\r\n renderPromise\r\n .then(() => {\r\n if (seq !== this._activateSeq) return\r\n this._emit({ isLoading: true, stage: 'project' })\r\n })\r\n .catch(() => {})\r\n\r\n await Promise.all([renderPromise, projectPromise])\r\n if (seq !== this._activateSeq) return\r\n\r\n // Drawing visible + editor swap (fast, post-projection).\r\n const editor = this.components.get(OBF.DrawingEditor)\r\n editor.activeDrawing = entry.drawing\r\n if (entry.drawing) {\r\n entry.drawing.three.visible = true\r\n }\r\n\r\n // Prepend Fill / Cut group-color controls so the sidebar shows them\r\n // at the top of the layers list.\r\n this._injectGroupColorLayers(entry)\r\n\r\n this._emit({ isLoading: false, stage: 'done' })\r\n } catch (error) {\r\n console.warn('[FloorplanTool] activate failed:', error)\r\n this._emit({ isLoading: false })\r\n }\r\n }\r\n\r\n /**\r\n * Exit floorplan mode. Each cleanup step is independently guarded so a\r\n * failure in one (e.g. resetHighlight on a huge model) cannot strand\r\n * camera, clip, or gizmo state.\r\n */\r\n async deactivate() {\r\n if (!this._activeId) return\r\n\r\n // Reset active state immediately so the React UI hides Exit and double\r\n // calls become no-ops.\r\n this._activeId = null\r\n this.onActiveDrawingChanged.trigger(null)\r\n\r\n // Drop any in-flight north-pick listener so the canvas stops\r\n // intercepting clicks once the floorplan is closed.\r\n safeRun(() => this.cancelPickNorth(), 'cancelPickNorth')\r\n\r\n // Clear editor + drawing visibility (synchronous, fast).\r\n safeRun(() => {\r\n const editor = this.components.get(OBF.DrawingEditor)\r\n editor.activeDrawing = null\r\n }, 'clear active drawing')\r\n for (const entry of this._entries.values()) {\r\n if (entry.drawing) {\r\n safeRun(() => {\r\n entry.drawing!.three.visible = false\r\n }, 'hide drawing')\r\n }\r\n }\r\n\r\n // Restore user-facing state FIRST so input feels responsive even if\r\n // model-render restoration takes a beat.\r\n safeRun(() => this.clip.removeAll(), 'removeClips')\r\n safeRun(() => this.grid.restore(), 'showGrid')\r\n safeRun(() => this.camera.unlock(), 'unlockCamera')\r\n safeRun(() => this.chrome.restoreCursor(), 'restoreCursor')\r\n safeRun(() => this.chrome.restoreHighlighter(), 'restoreHighlighter')\r\n safeRun(() => this.chrome.showGizmo(), 'showGizmo')\r\n safeRun(() => this.chrome.removeLighting(), 'removeLighting')\r\n safeRun(() => this.chrome.restoreBackground(), 'restoreBackground')\r\n\r\n // Slow async step last. Awaited so callers can chain on it, but failures\r\n // can't undo the synchronous restores above.\r\n await safeRunAsync(() => this.renderer.restore(), 'restoreModelRendering')\r\n\r\n safeRun(() => this._releaseCoordinator(), 'releaseCoordinator')\r\n }\r\n\r\n disposeEntriesForModel(modelId: string) {\r\n let touched = false\r\n for (const [id, entry] of this._entries) {\r\n if (entry.modelId !== modelId) continue\r\n if (this._activeId === id) void this.deactivate()\r\n disposeDrawing(this.components, entry.drawing)\r\n this._entries.delete(id)\r\n this.renderer.invalidateForEntry(id)\r\n touched = true\r\n }\r\n this.renderer.invalidateForModel(modelId)\r\n this.projector.invalidateForModel(modelId)\r\n if (touched) this.onDrawingsChanged.trigger(this.drawings)\r\n }\r\n\r\n dispose() {\r\n void this.deactivate()\r\n for (const entry of this._entries.values()) {\r\n disposeDrawing(this.components, entry.drawing)\r\n }\r\n this._entries.clear()\r\n this.onDrawingsChanged.trigger([])\r\n }\r\n\r\n private _emit(state: FloorplanLoadingState) {\r\n this.onGenerationStateChanged.trigger(state)\r\n }\r\n\r\n /** Mutual-exclusion claim. If ElevationsTool (or any other view tool)\r\n * was active, the coordinator awaits its deactivate() first. */\r\n private async _tryClaimCoordinator() {\r\n try {\r\n const coordinator = this.components.get(ViewModeCoordinator)\r\n await coordinator.claim(this)\r\n } catch {\r\n // ViewModeCoordinator not registered in this viewer — ignore.\r\n }\r\n }\r\n\r\n private _releaseCoordinator() {\r\n try {\r\n this.components.get(ViewModeCoordinator).release(this)\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n /** Lower clip plane sits just under the active floor's slab (normal +Y),\r\n * so ceiling fixtures / lights / equipment of the storey below never\r\n * bleed into the projection or the 3D underlay. */\r\n private _applyLowerClip(entry: FloorplanEntry) {\r\n const cutY = entry.elevation - 0.5\r\n this.clip.set(\r\n SLOT_LOWER,\r\n new THREE.Vector3(0, 1, 0),\r\n new THREE.Vector3(0, cutY, 0),\r\n )\r\n }\r\n\r\n /** Frame the camera over the active floor in orthographic top-down,\r\n * then re-apply the building's true-north rotation so storey switches\r\n * preserve the user's chosen orientation. */\r\n private _frameCamera(entry: FloorplanEntry) {\r\n const fragments = this.components.get(OBC.FragmentsManager)\r\n const model = fragments.list.get(entry.modelId)\r\n if (!model) return\r\n const center = new THREE.Vector3()\r\n const size = new THREE.Vector3()\r\n model.box.getCenter(center)\r\n model.box.getSize(size)\r\n const span = Math.max(size.x, size.z, 10)\r\n const target = new THREE.Vector3(\r\n center.x,\r\n entry.elevation + 1.2,\r\n center.z,\r\n )\r\n this.camera.frame(target, new THREE.Vector3(0, -1, 0), span)\r\n this._applyNorthToCamera()\r\n }\r\n\r\n}\r\n"],"mappings":"AAAA,YAAY,SAAS;AACrB,YAAY,SAAS;AACrB,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AACjC,SAAS,wBAAwB;AACjC,SAAS,sBAAsB;AAC/B,SAAS,aAAa,oBAAoB;AAC1C,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,SAAS,oBAAoB;AACtC,SAAS,oBAAsC;AAC/C,SAAS,2BAA2B;AACpC,SAAS,yBAAsC;AAC/C,SAAS,uBAAuB;AAChC,SAAS,WAAW,YAA4B,2BAA2B;AAC3E,SAAS,0BAA0B;AAEnC,MAAM,eAAe;AACrB,MAAM,aAAa;AAanB,MAAM,gBAAuD;AAAA,EAC3D,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,MAAM;AAAA,EACN,WAAW;AAAA,EACX,KAAK;AAAA,EACL,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AACR;AAEO,SAAS,yBACd,OACQ;AACR,SAAO,aAAa,OAAO,aAAa;AAC1C;AAEO,MAAM,iBAAN,MAAM,uBAAsB,IAAI,UAAU;AAAA,EAoD/C,YAAY,YAA4B;AACtC,UAAM,UAAU;AAlDlB,mBAAU;AAEV,SAAS,oBAAoB,IAAI,IAAI,MAAwB;AAC7D,SAAS,yBAAyB,IAAI,IAAI,MAA6B;AACvE,SAAS,2BAA2B,IAAI,IAAI,MAA6B;AAEzE;AAAA,SAAS,kBAAkB,IAAI,IAAI,MAAsB;AAEzD;AAAA,SAAS,sBAAsB,IAAI,IAAI,MAAc;AAErD;AAAA,SAAS,wBAAwB,IAAI,IAAI,MAAe;AAExD,SAAQ,WAAW,oBAAI,IAA4B;AACnD,SAAQ,YAA2B;AAInC;AAAA;AAAA;AAAA,SAAQ,eAAe;AAGvB;AAAA;AAAA,SAAQ,cAAc;AAEtB;AAAA,SAAQ,gBAAgB;AACxB,SAAQ,eAAiD;AACzD,SAAQ,eAAiD;AACzD,SAAQ,cAAmD;AAG3D;AAAA;AAAA,SAAQ,aAAmC;AAI3C;AAAA;AAAA;AAAA,SAAQ,aAAkC;AAG1C;AAAA;AAAA,SAAQ,eAAkC;AAC1C,SAAQ,cAAyC;AAIjD;AAAA;AAAA;AAAA,SAAQ,mBAAkC;AAWxC,eAAW,IAAI,eAAc,MAAM,IAAI;AAEvC,SAAK,YAAY,IAAI,gBAAgB,UAAU;AAC/C,SAAK,WAAW,IAAI,kBAAkB,YAAY,KAAK,SAAS;AAChE,SAAK,SAAS,IAAI,iBAAiB,UAAU;AAC7C,SAAK,SAAS,IAAI,iBAAiB,UAAU;AAC7C,SAAK,OAAO,IAAI,eAAe,UAAU;AACzC,SAAK,OAAO,IAAI,eAAe,UAAU;AAEzC,UAAM,YAAY,WAAW,IAAI,IAAI,gBAAgB;AACrD,cAAU,KAAK,cAAc,IAAI,CAAC,UAAU;AAC1C,WAAK,KAAK,SAAS,MAAM,OAAO;AAAA,IAClC,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,WAA6B;AAC/B,WAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC,EAAE;AAAA,MACxC,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,IAAI,kBAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,gBAAuC;AAjI7C;AAkII,WAAO,KAAK,aAAY,UAAK,SAAS,IAAI,KAAK,SAAS,MAAhC,YAAqC,OAAO;AAAA,EACtE;AAAA;AAAA;AAAA,EAIA,QAAQ,MAAkB;AACxB,SAAK,KAAK,QAAQ,IAAI;AAAA,EACxB;AAAA;AAAA,EAGA,gBAAgB,SAAiB,WAAmB,SAAkB;AACpE,UAAM,QAAQ,KAAK,SAAS,IAAI,OAAO;AACvC,QAAI,EAAC,+BAAO,YAAW,CAAC,MAAM,QAAQ,OAAO,IAAI,SAAS,EAAG;AAC7D,UAAM,QAAQ,OAAO,cAAc,WAAW,OAAO;AACrD,UAAM,OAAO,MAAM,OAAO,KAAK,CAAC,MAAM,EAAE,cAAc,SAAS;AAC/D,QAAI,KAAM,MAAK,UAAU;AACzB,SAAK,eAAe;AACpB,SAAK,gBAAgB,QAAQ,KAAK;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cAAc,SAAiB,WAAmB,OAAe;AACrE,UAAM,QAAQ,KAAK,SAAS,IAAI,OAAO;AACvC,QAAI,EAAC,+BAAO,YAAW,CAAC,MAAM,QAAQ,OAAO,IAAI,SAAS,EAAG;AAC7D,UAAM,QAAQ,OAAO,SAAS,WAAW,KAAK;AAC9C,UAAM,OAAO,MAAM,OAAO,KAAK,CAAC,MAAM,EAAE,cAAc,SAAS;AAC/D,QAAI,KAAM,MAAK,QAAQ;AAEvB,QAAI,aAAa,IAAI,SAAS,KAAK,YAAY,IAAI,SAAS,GAAG;AAC7D,YAAM,KAAK,sBAAsB,OAAO,WAAW,KAAK;AAAA,IAC1D;AAEA,SAAK,eAAe;AACpB,SAAK,gBAAgB,QAAQ,KAAK;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKQ,wBAAwB,OAAuB;AA7KzD;AAgLI,UAAM,UAAU,sBAAK,SAAiB,iBAAtB,mBAAoC,WAApC,mBAA4C,WAA5C,YAAsD,CAAC;AACvE,UAAM,YAAoB,kBAAO,CAAC,MAAR,mBAAW,UAAX,YAA2C;AACrE,UAAM,aAAqB,kBAAO,CAAC,MAAR,mBAAW,UAAX,YAA2C;AACtE,UAAM,WAAW,MAAM,OAAO;AAAA,MAC5B,CAAC,MAAM,EAAE,cAAc,wBAAwB,EAAE,cAAc;AAAA,IACjE;AACA,UAAM,SAAS;AAAA,MACb;AAAA,QACE,WAAW;AAAA,QACX,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,QACP,WAAW;AAAA,QACX,YAAY;AAAA,MACd;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,QACP,WAAW;AAAA,QACX,YAAY;AAAA,MACd;AAAA,MACA,GAAG;AAAA,IACL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,sBACZ,OACA,WACA,OACA;AACA,UAAM,YAAY,KAAK,WAAW,IAAI,IAAI,gBAAgB;AAC1D,UAAM,QAAQ,UAAU,KAAK,IAAI,MAAM,OAAO;AAC9C,QAAI,CAAC,MAAO;AAGZ,UAAM,MAAM,MAAM,MAAM,qBAAqB;AAAA,MAC3C,IAAI,OAAO,IAAI,SAAS,GAAG;AAAA,IAC7B,CAAC;AACD,QAAI,MAAM,OAAO,OAAO,GAAG,EAAE,KAAK;AAGlC,QAAI;AACF,YAAM,YAAY,MAAM,KAAK,UAAU;AAAA,QACrC,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,MACF;AACA,UAAI,UAAU,SAAS,GAAG;AACxB,cAAM,MAAM,IAAI,IAAI,SAAS;AAC7B,cAAM,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;AAAA,MACtC;AAAA,IACF,SAAQ;AAAA,IAER;AAEA,QAAI,IAAI,WAAW,EAAG;AACtB,QAAI;AACF,YAAM,MAAM,UAAU,KAAK;AAAA,QACzB,OAAO,IAAI,MAAM,MAAM,KAAK;AAAA,QAC5B,SAAS;AAAA,QACT,aAAa;AAAA,QACb,eAAe;AAAA,QACf,0BAA0B;AAAA,MAC5B,CAAC;AACD,gBAAU,KAAK,OAAO,IAAI;AAAA,IAC5B,SAAS,OAAO;AACd,cAAQ;AAAA,QACN,kDAAkD,SAAS;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAkB,SAAiB,OAAe;AACtD,UAAM,QAAQ,KAAK,SAAS,IAAI,OAAO;AACvC,QAAI,CAAC,MAAO;AACZ,UAAM,KAAK,SAAS,aAAa,SAAS,KAAK;AAC/C,UAAM,OAAO,MAAM,OAAO,KAAK,CAAC,MAAM,EAAE,cAAc,oBAAoB;AAC1E,QAAI,KAAM,MAAK,QAAQ;AACvB,SAAK,gBAAgB,QAAQ,KAAK;AAAA,EACpC;AAAA;AAAA;AAAA,EAIA,MAAM,iBAAiB,SAAiB,OAAe;AACrD,UAAM,QAAQ,KAAK,SAAS,IAAI,OAAO;AACvC,QAAI,CAAC,MAAO;AACZ,UAAM,KAAK,SAAS,YAAY,SAAS,KAAK;AAC9C,UAAM,OAAO,MAAM,OAAO,KAAK,CAAC,MAAM,EAAE,cAAc,mBAAmB;AACzE,QAAI,KAAM,MAAK,QAAQ;AACvB,SAAK,gBAAgB,QAAQ,KAAK;AAAA,EACpC;AAAA;AAAA,EAGA,IAAI,aAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,iBAA0B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,SAAiB;AAC7B,UAAM,cAAe,UAAU,MAAO,OAAO;AAC7C,QAAI,KAAK,gBAAgB,WAAY;AACrC,SAAK,cAAc;AACnB,SAAK,oBAAoB,QAAQ,UAAU;AAC3C,QAAI,KAAK,UAAW,MAAK,oBAAoB;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiB;AACf,QAAI,KAAK,iBAAiB,CAAC,KAAK,UAAW;AAC3C,UAAM,QAAQ,KAAK;AACnB,UAAM,QAAQ,KAAK,WAAW,IAAI,YAAY,EAAE;AAChD,QAAI,EAAC,+BAAO,YAAW,EAAC,+BAAO,UAAU;AACzC,UAAM,SAAS,MAAM,SAAS,MAAM;AAEpC,SAAK,aAAa;AAClB,SAAK,aAAa,KAAK,qBAAqB,KAAK;AAEjD,SAAK,eAAe,CAAC,MAAkB,KAAK,aAAa,CAAC;AAC1D,SAAK,eAAe,CAAC,MAAkB,KAAK,YAAY,CAAC;AACzD,SAAK,cAAc,CAAC,MAAqB;AACvC,UAAI,EAAE,QAAQ,SAAU,MAAK,gBAAgB;AAAA,IAC/C;AACA,WAAO,iBAAiB,SAAS,KAAK,YAAY;AAClD,WAAO,iBAAiB,aAAa,KAAK,YAAY;AACtD,WAAO,iBAAiB,WAAW,KAAK,WAAW;AACnD,SAAK,mBAAmB,OAAO,MAAM;AACrC,WAAO,MAAM,SAAS;AACtB,SAAK,gBAAgB;AACrB,SAAK,sBAAsB,QAAQ,IAAI;AAAA,EACzC;AAAA;AAAA;AAAA,EAIA,kBAAkB;AA1UpB;AA2UI,QAAI,CAAC,KAAK,cAAe;AACzB,UAAM,QAAQ,KAAK,WAAW,IAAI,YAAY,EAAE;AAChD,UAAM,UAAS,0CAAO,aAAP,mBAAiB,MAAM,eAAvB,YAAqC;AACpD,QAAI,QAAQ;AACV,UAAI,KAAK,aAAc,QAAO,oBAAoB,SAAS,KAAK,YAAY;AAC5E,UAAI,KAAK,aAAc,QAAO,oBAAoB,aAAa,KAAK,YAAY;AAGhF,aAAO,MAAM,UAAS,UAAK,qBAAL,YAAyB;AAAA,IACjD;AACA,SAAK,mBAAmB;AACxB,QAAI,KAAK,YAAa,QAAO,oBAAoB,WAAW,KAAK,WAAW;AAC5E,SAAK,eAAe;AACpB,SAAK,eAAe;AACpB,SAAK,cAAc;AACnB,SAAK,aAAa;AAClB,SAAK,aAAa;AAClB,SAAK,cAAc;AACnB,SAAK,gBAAgB;AACrB,SAAK,sBAAsB,QAAQ,KAAK;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,aAAa,OAAmB;AACtC,UAAM,QAAQ,KAAK;AACnB,QAAI,EAAC,+BAAO,SAAS;AACrB,UAAM,QAAQ,KAAK,mBAAmB,OAAO,KAAK;AAClD,QAAI,CAAC,MAAO;AACZ,UAAM,QAAQ,KAAK,MAAM,KAAK;AAE9B,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,aAAa;AAClB,WAAK,eAAe,OAAO,KAAK;AAChC,WAAK,eAAe;AACpB;AAAA,IACF;AAIA,UAAM,KAAK,MAAM,IAAI,KAAK,WAAW;AACrC,UAAM,KAAK,MAAM,IAAI,KAAK,WAAW;AACrC,QAAI,KAAK,KAAK,KAAK,KAAK,KAAM;AAK9B,UAAM,UAAU,MAAM;AACtB,YAAQ,MAAM,kBAAkB,MAAM,KAAK;AAC3C,UAAM,SAAS,QAAQ,MAAM,aAAa,KAAK,WAAW,MAAM,CAAC;AACjE,UAAM,SAAS,QAAQ,MAAM,aAAa,MAAM,MAAM,CAAC;AACvD,UAAM,aAAa,KAAK,uBAAuB,QAAQ,MAAM;AAC7D,QAAI,eAAe,KAAM,MAAK,cAAc,UAAU;AACtD,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,uBACN,QACA,QACe;AAjZnB;AAkZI,UAAM,QAAQ,KAAK,WAAW,IAAI,YAAY,EAAE;AAChD,UAAM,OAAM,oCAAO,WAAP,mBAAe;AAC3B,UAAM,YAAW,oCAAO,WAAP,mBAAe;AAChC,QAAI,CAAC,OAAO,QAAO,qCAAU,kBAAiB,SAAU,QAAO;AAE/D,UAAM,KAAK,OAAO,IAAI,OAAO;AAC7B,UAAM,KAAK,OAAO,IAAI,OAAO;AAC7B,QAAI,KAAK,KAAK,KAAK,KAAK,MAAO,QAAO;AACtC,UAAM,SAAS,KAAK,MAAM,IAAI,EAAE;AAGhC,QAAI,kBAAkB;AACtB,UAAM,IAAI,IAAI,YAAY;AAC1B,UAAM,OAAO,KAAK,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;AAClC,UAAM,SAAS,SAAS;AAExB,YAAS,OAAO,SAAS,UAAU,MAAO,KAAK;AAAA,EACjD;AAAA;AAAA;AAAA,EAIQ,YAAY,OAAmB;AACrC,UAAM,QAAQ,KAAK;AACnB,QAAI,EAAC,+BAAO,SAAS;AACrB,UAAM,QAAQ,KAAK,mBAAmB,OAAO,KAAK;AAClD,QAAI,CAAC,MAAO;AACZ,SAAK,eAAe,OAAO,KAAK,MAAM,KAAK,CAAC;AAC5C,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKQ,mBACN,OACA,OACsB;AACtB,UAAM,QAAQ,KAAK,WAAW,IAAI,YAAY,EAAE;AAChD,QAAI,EAAC,+BAAO,aAAY,CAAC,MAAM,UAAU,CAAC,MAAM,QAAS,QAAO;AAChE,UAAM,SAAS,MAAM,SAAS,MAAM;AACpC,UAAM,OAAO,OAAO,sBAAsB;AAC1C,UAAM,KAAM,MAAM,UAAU,KAAK,QAAQ,KAAK,QAAS,IAAI;AAC3D,UAAM,IAAI,GAAG,MAAM,UAAU,KAAK,OAAO,KAAK,UAAU,IAAI;AAC5D,UAAM,YAAY,IAAI,MAAM,UAAU;AACtC,cAAU;AAAA,MACR,IAAI,MAAM,QAAQ,GAAG,CAAC;AAAA,MACtB,MAAM,OAAO;AAAA,IACf;AACA,UAAM,UAAU,MAAM;AACtB,YAAQ,MAAM,kBAAkB,MAAM,KAAK;AAC3C,UAAM,MAAM,IAAI,MAAM,QAAQ,EAAE,KAAK,QAAQ,MAAM,WAAW,EAAE,OAAO;AACvE,UAAM,SAAS,UAAU,IAAI,OAAO,MAAM,EAAE,aAAa,GAAG;AAC5D,UAAM,MAAM,UAAU,IAAI,UAAU,MAAM,EAAE,mBAAmB,GAAG;AAClE,QAAI,KAAK,IAAI,IAAI,CAAC,IAAI,KAAM,QAAO;AACnC,UAAM,IAAI,CAAC,OAAO,IAAI,IAAI;AAC1B,QAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,WAAO,IAAI,MAAM;AAAA,MACf,OAAO,IAAI,IAAI,IAAI;AAAA,MACnB;AAAA,MACA,OAAO,IAAI,IAAI,IAAI;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA,EAIQ,MAAM,OAAqC;AACjD,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,SAAS,KAAK,iBAAiB;AACrC,QAAI,OAAO;AACX,QAAI,SAAS,SAAS;AACtB,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;AACxC,YAAM,KAAK,MAAM,CAAC,IAAI,MAAM;AAC5B,YAAM,KAAK,MAAM,IAAI,CAAC,IAAI,MAAM;AAChC,YAAM,KAAK,KAAK,KAAK,KAAK;AAC1B,UAAI,KAAK,QAAQ;AACf,iBAAS;AACT,eAAO;AAAA,MACT;AAAA,IACF;AACA,QAAI,OAAO,EAAG,QAAO;AACrB,WAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,GAAG,GAAG,MAAM,OAAO,CAAC,CAAC;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAA2B;AAzerC;AA0eI,UAAM,UAAU;AAChB,UAAM,WAAW;AACjB,UAAM,QAAQ,KAAK,WAAW,IAAI,YAAY,EAAE;AAChD,UAAM,OAAM,oCAAO,WAAP,mBAAe;AAC3B,UAAM,UAAS,oCAAO,aAAP,mBAAiB,MAAM;AACtC,QAAI,EAAC,2BAAK,yBAAwB,CAAC,OAAQ,QAAO;AAClD,UAAM,cAAc,IAAI,QAAQ,IAAI,SAAS,IAAI,QAAQ;AACzD,UAAM,gBAAgB,aAAa,KAAK,IAAI,GAAG,OAAO,WAAW;AACjE,WAAO,gBAAgB,WAAW;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAAqB,OAA4C;AACvE,UAAM,UAAU,MAAM;AACtB,QAAI,CAAC,QAAS,QAAO;AACrB,YAAQ,MAAM,kBAAkB,MAAM,IAAI;AAC1C,UAAM,MAAM,IAAI,MAAM,QAAQ,EAAE,KAAK,QAAQ,MAAM,WAAW,EAAE,OAAO;AACvE,UAAM,IAAI,IAAI,MAAM,QAAQ;AAC5B,UAAM,MAAgB,CAAC;AACvB,YAAQ,MAAM,SAAS,CAAC,UAA0B;AAhgBtD;AAigBM,UAAI,EAAE,iBAAiB,MAAM,iBAAiB,CAAC,MAAM,QAAS;AAC9D,WAAI,WAAM,aAAN,mBAAgB,YAAa;AACjC,YAAM,OAAM,iBAAM,aAAN,mBAAgB,eAAhB,mBAA4B;AAGxC,UAAI,CAAC,IAAK;AACV,eAAS,IAAI,GAAG,IAAI,IAAI,OAAO,KAAK;AAClC,UAAE,IAAI,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC,EACxC,aAAa,MAAM,WAAW,EAC9B,aAAa,GAAG;AACnB,YAAI,KAAK,EAAE,GAAG,EAAE,CAAC;AAAA,MACnB;AAAA,IACF,CAAC;AACD,WAAO,IAAI,SAAS,IAAI,aAAa,GAAG,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,OAAuB,SAAwB;AACpE,UAAM,UAAU,MAAM;AACtB,QAAI,CAAC,QAAS;AACd,UAAM,QAAQ;AAEd,QAAI,CAAC,KAAK,aAAa;AAGrB,YAAM,OAAO,IAAI,MAAM,eAAe;AACtC,WAAK;AAAA,QACH;AAAA,QACA,IAAI,MAAM;AAAA,UACR,IAAI,aAAa,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC;AAAA,UACvD;AAAA,QACF;AAAA,MACF;AACA,WAAK,cAAc,IAAI,MAAM;AAAA,QAC3B;AAAA,QACA,IAAI,MAAM,kBAAkB,EAAE,OAAO,MAAM,CAAC;AAAA,MAC9C;AACA,WAAK,YAAY,cAAc;AAC/B,cAAQ,MAAM,IAAI,KAAK,WAAW;AAAA,IACpC;AACA,UAAM,MAAM,KAAK,iBAAiB;AAClC,SAAK,YAAY,MAAM,UAAU,GAAG;AACpC,SAAK,YAAY,SAAS,IAAI,QAAQ,GAAG,GAAG,QAAQ,CAAC;AACrD,SAAK,YAAY,UAAU;AAE3B,QAAI,CAAC,KAAK,YAAY;AACpB,UAAI,KAAK,aAAc,MAAK,aAAa,UAAU;AACnD;AAAA,IACF;AACA,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,OAAO,IAAI,MAAM,eAAe;AACtC,WAAK;AAAA,QACH;AAAA,QACA,IAAI,MAAM,uBAAuB,IAAI,aAAa,CAAC,GAAG,CAAC;AAAA,MACzD;AACA,WAAK,eAAe,IAAI,MAAM;AAAA,QAC5B;AAAA,QACA,IAAI,MAAM,kBAAkB,EAAE,OAAO,MAAM,CAAC;AAAA,MAC9C;AACA,WAAK,aAAa,cAAc;AAChC,cAAQ,MAAM,IAAI,KAAK,YAAY;AAAA,IACrC;AACA,UAAM,MAAM,KAAK,aAAa,SAAS,WACpC;AACH,QAAI,OAAO,GAAG,KAAK,WAAW,GAAG,GAAG,KAAK,WAAW,CAAC;AACrD,QAAI,OAAO,GAAG,QAAQ,GAAG,GAAG,QAAQ,CAAC;AACrC,QAAI,cAAc;AAClB,SAAK,aAAa,UAAU;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB;AA5kB1B;AA6kBI,QAAI,KAAK,cAAc;AACrB,iBAAK,aAAa,WAAlB,mBAA0B,OAAO,KAAK;AACtC,WAAK,aAAa,SAAS,QAAQ;AACnC,YAAM,IAAI,KAAK,aAAa;AAC5B,UAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,GAAE,QAAQ;AACjC,WAAK,eAAe;AAAA,IACtB;AACA,QAAI,KAAK,aAAa;AACpB,iBAAK,YAAY,WAAjB,mBAAyB,OAAO,KAAK;AACrC,WAAK,YAAY,SAAS,QAAQ;AAClC,YAAM,IAAI,KAAK,YAAY;AAC3B,UAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,GAAE,QAAQ;AACjC,WAAK,cAAc;AAAA,IACrB;AACA,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAsB;AAjmBhC;AAkmBI,UAAM,QAAQ,KAAK,WAAW,IAAI,YAAY,EAAE;AAChD,QAAI,GAAC,oCAAO,WAAP,mBAAe,UAAU;AAC9B,UAAM,WAAW,MAAM,OAAO;AAC9B,UAAM,UAAW,KAAK,cAAc,KAAK,KAAM;AAC/C,QAAI,OAAO,SAAS,oBAAoB,YAAY;AAClD,WAAK,SAAS,gBAAgB,SAAS,IAAI;AAAA,IAC7C,OAAO;AACL,eAAS,eAAe;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA,EAIQ,iBAAiB;AACvB,QAAI;AACF,YAAM,YAAY,KAAK,WAAW,IAAI,IAAI,gBAAgB;AAC1D,gBAAU,KAAK,OAAO,IAAI;AAAA,IAC5B,SAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA,EAIA,MAAM,SAAS,SAA4C;AACzD,UAAM,YAAY,KAAK,WAAW,IAAI,IAAI,gBAAgB;AAC1D,UAAM,QAAQ,UAAU,KAAK,IAAI,OAAO;AACxC,QAAI,CAAC,MAAO,QAAO,CAAC;AAEpB,UAAM,cAAc,KAAK,WAAW,IAAI,YAAY,EAAE;AACtD,QAAI,CAAC,YAAa,QAAO,CAAC;AAE1B,SAAK,MAAM,EAAE,WAAW,MAAM,OAAO,iBAAiB,CAAC;AAEvD,QAAI;AACF,WAAK,uBAAuB,OAAO;AACnC,YAAM,KAAK,UAAU,kBAAkB;AAEvC,YAAM,YAAY,OAAO;AAAA,QACvB,MAAM,MAAM,qBAAqB,CAAC,gBAAgB,CAAC;AAAA,MACrD,EAAE,KAAK;AACP,UAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AAEpC,YAAM,cAAc,MAAM,MAAM,aAAa,SAAS;AACtD,YAAM,CAAC,EAAE,WAAW,IAAI,MAAM,MAAM,eAAe;AACnD,YAAM,MAAM,MAAM;AAClB,YAAM,OAAO,IAAI,IAAI;AACrB,YAAM,OAAO,IAAI,IAAI;AAErB,YAAM,UAA4B,CAAC;AAInC,eAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,cAAM,SAAS,YAAY,CAAC;AAC5B,cAAM,gBAAgB,UAAU,CAAC;AACjC,YAAI,EAAE,WAAW,OAAO,QAAQ,WAAW,OAAO,WAAY;AAE9D,cAAM,OAAO,OAAO,OAAO,KAAK,KAAK;AACrC,cAAM,UAAU,OAAO,OAAO,UAAU,KAAK;AAC7C,cAAM,YAAY,mBAAmB,SAAS,aAAa,MAAM,IAAI;AAErE,cAAM,QAAwB;AAAA,UAC5B,IAAI,GAAG,OAAO,KAAK,IAAI;AAAA,UACvB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,SAAS;AAAA,UACT,WAAW;AAAA,UACX,QAAQ,CAAC;AAAA,QACX;AACA,aAAK,SAAS,IAAI,MAAM,IAAI,KAAK;AACjC,gBAAQ,KAAK,KAAK;AAAA,MACpB;AAEA,WAAK,kBAAkB,QAAQ,KAAK,QAAQ;AAC5C,aAAO;AAAA,IACT,UAAE;AACA,WAAK,MAAM,EAAE,WAAW,MAAM,CAAC;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,SAAS,IAAY;AACzB,UAAM,QAAQ,KAAK,SAAS,IAAI,EAAE;AAClC,QAAI,CAAC,MAAO;AACZ,QAAI,KAAK,cAAc,GAAI;AAE3B,UAAM,cAAc,KAAK,WAAW,IAAI,YAAY,EAAE;AACtD,QAAI,CAAC,YAAa;AAElB,UAAM,MAAM,EAAE,KAAK;AACnB,UAAM,YAAY,KAAK,cAAc;AAErC,SAAK,MAAM,EAAE,WAAW,MAAM,OAAO,OAAO,CAAC;AAG7C,UAAM,KAAK,qBAAqB;AAChC,QAAI,QAAQ,KAAK,aAAc;AAE/B,QAAI;AAEF,UAAI,CAAC,WAAW;AACd,aAAK,OAAO,cAAc;AAC1B,aAAK,OAAO,uBAAuB;AAEnC,aAAK,OAAO,KAAK,CAAC;AAClB,aAAK,OAAO,UAAU;AACtB,aAAK,OAAO,mBAAmB;AAC/B,aAAK,OAAO,UAAU;AAAA,MACxB;AAKA,WAAK,KAAK;AAAA,QACR;AAAA,QACA,IAAI,MAAM,QAAQ,GAAG,IAAI,CAAC;AAAA,QAC1B,IAAI,MAAM,QAAQ,GAAG,MAAM,YAAY,MAAM,MAAM,CAAC;AAAA,MACtD;AACA,cAAQ,MAAM,KAAK,gBAAgB,KAAK,GAAG,YAAY;AACvD,cAAQ,MAAM,KAAK,KAAK,KAAK,GAAG,UAAU;AAI1C,iBAAW,SAAS,KAAK,SAAS,OAAO,GAAG;AAC1C,YAAI,MAAM,WAAW,MAAM,OAAO,MAAM,IAAI;AAC1C,gBAAM,QAAQ,MAAM,UAAU;AAAA,QAChC;AAAA,MACF;AAEA,WAAK,aAAa,KAAK;AAIvB,WAAK,YAAY;AACjB,WAAK,uBAAuB,QAAQ,KAAK;AAGzC,YAAM,gBAAgB,KAAK,SAAS,MAAM,OAAO,CAAC,UAAU;AAC1D,YAAI,QAAQ,KAAK,aAAc;AAC/B,aAAK,MAAM,EAAE,WAAW,MAAM,MAAM,CAAC;AAAA,MACvC,CAAC;AAKD,YAAM,iBAAiB,KAAK,UAAU,QAAQ,KAAK;AAEnD,oBACG,KAAK,MAAM;AACV,YAAI,QAAQ,KAAK,aAAc;AAC/B,aAAK,MAAM,EAAE,WAAW,MAAM,OAAO,UAAU,CAAC;AAAA,MAClD,CAAC,EACA,MAAM,MAAM;AAAA,MAAC,CAAC;AAEjB,YAAM,QAAQ,IAAI,CAAC,eAAe,cAAc,CAAC;AACjD,UAAI,QAAQ,KAAK,aAAc;AAG/B,YAAM,SAAS,KAAK,WAAW,IAAI,IAAI,aAAa;AACpD,aAAO,gBAAgB,MAAM;AAC7B,UAAI,MAAM,SAAS;AACjB,cAAM,QAAQ,MAAM,UAAU;AAAA,MAChC;AAIA,WAAK,wBAAwB,KAAK;AAElC,WAAK,MAAM,EAAE,WAAW,OAAO,OAAO,OAAO,CAAC;AAAA,IAChD,SAAS,OAAO;AACd,cAAQ,KAAK,oCAAoC,KAAK;AACtD,WAAK,MAAM,EAAE,WAAW,MAAM,CAAC;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aAAa;AACjB,QAAI,CAAC,KAAK,UAAW;AAIrB,SAAK,YAAY;AACjB,SAAK,uBAAuB,QAAQ,IAAI;AAIxC,YAAQ,MAAM,KAAK,gBAAgB,GAAG,iBAAiB;AAGvD,YAAQ,MAAM;AACZ,YAAM,SAAS,KAAK,WAAW,IAAI,IAAI,aAAa;AACpD,aAAO,gBAAgB;AAAA,IACzB,GAAG,sBAAsB;AACzB,eAAW,SAAS,KAAK,SAAS,OAAO,GAAG;AAC1C,UAAI,MAAM,SAAS;AACjB,gBAAQ,MAAM;AACZ,gBAAM,QAAS,MAAM,UAAU;AAAA,QACjC,GAAG,cAAc;AAAA,MACnB;AAAA,IACF;AAIA,YAAQ,MAAM,KAAK,KAAK,UAAU,GAAG,aAAa;AAClD,YAAQ,MAAM,KAAK,KAAK,QAAQ,GAAG,UAAU;AAC7C,YAAQ,MAAM,KAAK,OAAO,OAAO,GAAG,cAAc;AAClD,YAAQ,MAAM,KAAK,OAAO,cAAc,GAAG,eAAe;AAC1D,YAAQ,MAAM,KAAK,OAAO,mBAAmB,GAAG,oBAAoB;AACpE,YAAQ,MAAM,KAAK,OAAO,UAAU,GAAG,WAAW;AAClD,YAAQ,MAAM,KAAK,OAAO,eAAe,GAAG,gBAAgB;AAC5D,YAAQ,MAAM,KAAK,OAAO,kBAAkB,GAAG,mBAAmB;AAIlE,UAAM,aAAa,MAAM,KAAK,SAAS,QAAQ,GAAG,uBAAuB;AAEzE,YAAQ,MAAM,KAAK,oBAAoB,GAAG,oBAAoB;AAAA,EAChE;AAAA,EAEA,uBAAuB,SAAiB;AACtC,QAAI,UAAU;AACd,eAAW,CAAC,IAAI,KAAK,KAAK,KAAK,UAAU;AACvC,UAAI,MAAM,YAAY,QAAS;AAC/B,UAAI,KAAK,cAAc,GAAI,MAAK,KAAK,WAAW;AAChD,qBAAe,KAAK,YAAY,MAAM,OAAO;AAC7C,WAAK,SAAS,OAAO,EAAE;AACvB,WAAK,SAAS,mBAAmB,EAAE;AACnC,gBAAU;AAAA,IACZ;AACA,SAAK,SAAS,mBAAmB,OAAO;AACxC,SAAK,UAAU,mBAAmB,OAAO;AACzC,QAAI,QAAS,MAAK,kBAAkB,QAAQ,KAAK,QAAQ;AAAA,EAC3D;AAAA,EAEA,UAAU;AACR,SAAK,KAAK,WAAW;AACrB,eAAW,SAAS,KAAK,SAAS,OAAO,GAAG;AAC1C,qBAAe,KAAK,YAAY,MAAM,OAAO;AAAA,IAC/C;AACA,SAAK,SAAS,MAAM;AACpB,SAAK,kBAAkB,QAAQ,CAAC,CAAC;AAAA,EACnC;AAAA,EAEQ,MAAM,OAA8B;AAC1C,SAAK,yBAAyB,QAAQ,KAAK;AAAA,EAC7C;AAAA;AAAA;AAAA,EAIA,MAAc,uBAAuB;AACnC,QAAI;AACF,YAAM,cAAc,KAAK,WAAW,IAAI,mBAAmB;AAC3D,YAAM,YAAY,MAAM,IAAI;AAAA,IAC9B,SAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,sBAAsB;AAC5B,QAAI;AACF,WAAK,WAAW,IAAI,mBAAmB,EAAE,QAAQ,IAAI;AAAA,IACvD,SAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,OAAuB;AAC7C,UAAM,OAAO,MAAM,YAAY;AAC/B,SAAK,KAAK;AAAA,MACR;AAAA,MACA,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;AAAA,MACzB,IAAI,MAAM,QAAQ,GAAG,MAAM,CAAC;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,OAAuB;AAC1C,UAAM,YAAY,KAAK,WAAW,IAAI,IAAI,gBAAgB;AAC1D,UAAM,QAAQ,UAAU,KAAK,IAAI,MAAM,OAAO;AAC9C,QAAI,CAAC,MAAO;AACZ,UAAM,SAAS,IAAI,MAAM,QAAQ;AACjC,UAAM,OAAO,IAAI,MAAM,QAAQ;AAC/B,UAAM,IAAI,UAAU,MAAM;AAC1B,UAAM,IAAI,QAAQ,IAAI;AACtB,UAAM,OAAO,KAAK,IAAI,KAAK,GAAG,KAAK,GAAG,EAAE;AACxC,UAAM,SAAS,IAAI,MAAM;AAAA,MACvB,OAAO;AAAA,MACP,MAAM,YAAY;AAAA,MAClB,OAAO;AAAA,IACT;AACA,SAAK,OAAO,MAAM,QAAQ,IAAI,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,IAAI;AAC3D,SAAK,oBAAoB;AAAA,EAC3B;AAEF;AAz2Ba,eACJ,OAAO;AADT,IAAM,gBAAN;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/bim/src/ModelManager/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAM3C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAG7D,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC,cAAc,CAAA;IAC5B,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,OAAO,CAAA;IAC9B,QAAQ,CAAC,EAAE,KAAK,CAAC,KAAK,CAAA;IACtB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,qBAAa,YAAa,SAAQ,GAAG,CAAC,SAAS;IAC7C,MAAM,CAAC,IAAI,EAAG,sCAAsC,CAAS;IAE7D,OAAO,UAAO;IAEd,aAAa,uBAA6B;IAC1C,kBAAkB,uBAA6B;IAE/C,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,WAAW,CAAY;IAC/B,OAAO,CAAC,UAAU,CAAW;IAC7B,OAAO,CAAC,UAAU,CAAW;IAC7B,OAAO,CAAC,cAAc,CAAe;IACrC,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,eAAe,CAAiC;IACxD,OAAO,CAAC,YAAY,CAAsB;gBAE9B,UAAU,EAAE,GAAG,CAAC,UAAU;IAYtC;;OAEG;IACG,IAAI,CACR,SAAS,EAAE,IAAI,GAAG,MAAM,EACxB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA0E5B;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IA2B3B;;OAEG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI3C;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAOnD;;OAEG;IACH,YAAY,IAAI,SAAS,EAAE;IAI3B;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO;IAiBjD;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO;IASzD;;OAEG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,OAAO;IAe5D;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,GAAG,OAAO;IASvD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;YACW,eAAe;IAiB7B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAWhB;;OAEG;IACH,OAAO,CAAC,OAAO;IAWf;;OAEG;IACH,OAAO,CAAC,OAAO;IAWf;;OAEG;IACH,OAAO,CAAC,WAAW;IAWnB;;OAEG;IACH,OAAO,CAAC,eAAe;IAWvB;;OAEG;IACH,OAAO,CAAC,UAAU;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/bim/src/ModelManager/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -272,6 +272,15 @@ const _ModelManager = class _ModelManager extends OBC.Component {
|
|
|
272
272
|
throw new Error("World not available for gizmo setup");
|
|
273
273
|
}
|
|
274
274
|
const gizmoController = new GizmoController(this._world);
|
|
275
|
+
const endEditing = () => {
|
|
276
|
+
const modelInfo = [...this._models.values()].find((info) => info.model === model);
|
|
277
|
+
if (!modelInfo) return;
|
|
278
|
+
model.updateMatrixWorld(true);
|
|
279
|
+
modelInfo.gizmoController = void 0;
|
|
280
|
+
this.onModelTransformed.trigger(modelInfo);
|
|
281
|
+
};
|
|
282
|
+
gizmoController.onAccept = endEditing;
|
|
283
|
+
gizmoController.onCancel = endEditing;
|
|
275
284
|
gizmoController.attach(model);
|
|
276
285
|
return gizmoController;
|
|
277
286
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/bim/src/ModelManager/index.ts"],"sourcesContent":["import * as THREE from 'three'\r\nimport * as OBC from '@thatopen/components'\r\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'\r\nimport { OBJLoader } from 'three/addons/loaders/OBJLoader.js'\r\nimport { FBXLoader } from 'three/addons/loaders/FBXLoader.js'\r\nimport { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js'\r\nimport { CurrentWorld } from '../CurrentWorld'\r\nimport { GizmoController } from '../../utils/GizmoController'\r\nimport { ViewportGizmo } from '../ViewportGizmo'\r\n\r\nexport interface ModelInfo {\r\n id: string\r\n name: string\r\n model: THREE.Group\r\n mixer?: THREE.AnimationMixer\r\n gizmoController?: GizmoController\r\n fileUrl?: string\r\n}\r\n\r\nexport interface LoadModelOptions {\r\n position?: THREE.Vector3\r\n scale?: number | THREE.Vector3\r\n rotation?: THREE.Euler\r\n enableGizmo?: boolean\r\n enableAnimations?: boolean\r\n extension?: string\r\n}\r\n\r\nexport class ModelManager extends OBC.Component {\r\n static uuid = '4cf01658-4a4e-49bd-8c89-4574db0032e9' as const\r\n\r\n enabled = true\r\n\r\n onModelLoaded = new OBC.Event<ModelInfo>()\r\n onModelTransformed = new OBC.Event<ModelInfo>()\r\n\r\n private _components: OBC.Components\r\n private _world: OBC.World | null = null\r\n private _gltfLoader: GLTFLoader\r\n private _objLoader: OBJLoader \r\n private _fbxLoader: FBXLoader \r\n private _colladaLoader: ColladaLoader \r\n private _models: Map<string, ModelInfo> = new Map()\r\n private _animationClock: THREE.Clock = new THREE.Clock()\r\n private _animationId: number | null = null\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(ModelManager.uuid, this)\r\n this._components = components\r\n this._gltfLoader = new GLTFLoader()\r\n this._objLoader = new OBJLoader()\r\n this._fbxLoader = new FBXLoader()\r\n this._colladaLoader = new ColladaLoader()\r\n this._world = components.get(CurrentWorld).world\r\n this.startAnimationLoop()\r\n }\r\n\r\n /**\r\n * Load a 3D model (GLB/GLTF/OBJ/FBX/DAE) from a file or URL\r\n */\r\n async load(\r\n fileOrUrl: File | string,\r\n id: string,\r\n name: string,\r\n options: LoadModelOptions = {},\r\n ): Promise<ModelInfo | null> {\r\n if (!this._world) {\r\n console.error('World not available for model loading')\r\n return null\r\n }\r\n\r\n try {\r\n let fileUrl: string\r\n\r\n fileUrl = fileOrUrl instanceof File ? URL.createObjectURL(fileOrUrl) : fileOrUrl\r\n\r\n const extension = options.extension || this.getFileExtension(fileOrUrl)\r\n\r\n const result = await this.loadModelByType(fileUrl, extension)\r\n const model = result.scene || result\r\n\r\n if (options.position) {\r\n model.position.copy(options.position)\r\n }\r\n\r\n if (options.scale) {\r\n if (typeof options.scale === 'number') {\r\n model.scale.setScalar(options.scale)\r\n }\r\n else {\r\n model.scale.copy(options.scale)\r\n }\r\n }\r\n\r\n if (options.rotation) {\r\n model.rotation.copy(options.rotation)\r\n }\r\n\r\n this._world.scene.three.add(model)\r\n\r\n\r\n let mixer: THREE.AnimationMixer | undefined\r\n if (options.enableAnimations && result.animations && result.animations.length > 0) {\r\n mixer = this.setupAnimations(model, result.animations)\r\n }\r\n\r\n let gizmoController: GizmoController | undefined\r\n if (options.enableGizmo) {\r\n gizmoController = this.setupGizmo(model)\r\n }\r\n\r\n const modelInfo: ModelInfo = {\r\n id,\r\n name,\r\n model,\r\n mixer,\r\n gizmoController,\r\n fileUrl: fileOrUrl instanceof File ? fileUrl : undefined,\r\n }\r\n\r\n this._models.set(id, modelInfo);\r\n\r\n (model as any).modelId = id\r\n\r\n this.onModelLoaded.trigger(modelInfo)\r\n\r\n return modelInfo\r\n }\r\n catch (error) {\r\n console.error('Error loading 3D model:', error)\r\n\r\n if (fileOrUrl instanceof File) {\r\n URL.revokeObjectURL(fileOrUrl instanceof File ? URL.createObjectURL(fileOrUrl) : '')\r\n }\r\n\r\n return null\r\n }\r\n }\r\n\r\n /**\r\n * Remove a model from the scene\r\n */\r\n remove(id: string): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo || !this._world) return false\r\n\r\n this._world.scene.three.remove(modelInfo.model)\r\n\r\n\r\n if (modelInfo.gizmoController) {\r\n modelInfo.gizmoController.dispose()\r\n }\r\n\r\n // Cleanup mixer\r\n if (modelInfo.mixer) {\r\n modelInfo.mixer.stopAllAction()\r\n }\r\n\r\n // Cleanup file URL if it exists\r\n if (modelInfo.fileUrl) {\r\n URL.revokeObjectURL(modelInfo.fileUrl)\r\n }\r\n\r\n // Remove from storage\r\n this._models.delete(id)\r\n\r\n return true\r\n }\r\n\r\n /**\r\n * Get model info by ID\r\n */\r\n getModel(id: string): ModelInfo | undefined {\r\n return this._models.get(id)\r\n }\r\n\r\n /**\r\n * Get model info by name (file.name)\r\n */\r\n getModelByName(name: string): ModelInfo | undefined {\r\n for (const modelInfo of this._models.values()) {\r\n if (modelInfo.name === name) return modelInfo\r\n }\r\n return undefined\r\n }\r\n\r\n /**\r\n * Get all loaded models\r\n */\r\n getAllModels(): ModelInfo[] {\r\n return [...this._models.values()]\r\n }\r\n\r\n /**\r\n * Enable/disable gizmo for a model\r\n */\r\n toggleGizmo(id: string, enable: boolean): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo || !this._world) return false\r\n\r\n if (enable && !modelInfo.gizmoController) {\r\n modelInfo.gizmoController = this.setupGizmo(modelInfo.model)\r\n return true\r\n }\r\n else if (!enable && modelInfo.gizmoController) {\r\n modelInfo.gizmoController.dispose()\r\n modelInfo.gizmoController = undefined\r\n return true\r\n }\r\n\r\n return false\r\n }\r\n\r\n /**\r\n * Update model position\r\n */\r\n setPosition(id: string, position: THREE.Vector3): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo) return false\r\n\r\n modelInfo.model.position.copy(position)\r\n this.onModelTransformed.trigger(modelInfo)\r\n return true\r\n }\r\n\r\n /**\r\n * Update model scale\r\n */\r\n setScale(id: string, scale: number | THREE.Vector3): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo) return false\r\n\r\n if (typeof scale === 'number') {\r\n modelInfo.model.scale.setScalar(scale)\r\n }\r\n else {\r\n modelInfo.model.scale.copy(scale)\r\n }\r\n\r\n this.onModelTransformed.trigger(modelInfo)\r\n return true\r\n }\r\n\r\n /**\r\n * Update model rotation\r\n */\r\n setRotation(id: string, rotation: THREE.Euler): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo) return false\r\n\r\n modelInfo.model.rotation.copy(rotation)\r\n this.onModelTransformed.trigger(modelInfo)\r\n return true\r\n }\r\n\r\n /**\r\n * Get file extension from File or URL\r\n */\r\n private getFileExtension(fileOrUrl: File | string): string {\r\n if (fileOrUrl instanceof File) {\r\n return fileOrUrl.name.split('.').pop()?.toLowerCase() || ''\r\n }\r\n const urlPath = fileOrUrl.split('?')[0]\r\n return urlPath.split('.').pop()?.toLowerCase() || ''\r\n }\r\n\r\n /**\r\n * Load model based on file type\r\n */\r\n private async loadModelByType(url: string, extension: string): Promise<any> {\r\n switch (extension) {\r\n case 'gltf':\r\n case 'glb':\r\n return this.loadGLTF(url)\r\n case 'obj':\r\n return this.loadOBJ(url)\r\n case 'fbx':\r\n return this.loadFBX(url)\r\n case 'dae':\r\n case 'collada':\r\n return this.loadCollada(url)\r\n default:\r\n throw new Error(`Unsupported file format: ${extension}`)\r\n }\r\n }\r\n\r\n /**\r\n * Load GLTF file using promise\r\n */\r\n private loadGLTF(url: string): Promise<any> {\r\n return new Promise((resolve, reject) => {\r\n this._gltfLoader.load(\r\n url,\r\n gltf => resolve(gltf),\r\n undefined,\r\n error => reject(error),\r\n )\r\n })\r\n }\r\n\r\n /**\r\n * Load OBJ file using promise\r\n */\r\n private loadOBJ(url: string): Promise<THREE.Group> {\r\n return new Promise((resolve, reject) => {\r\n this._objLoader.load(\r\n url,\r\n obj => resolve(obj),\r\n undefined,\r\n error => reject(error),\r\n )\r\n })\r\n }\r\n\r\n /**\r\n * Load FBX file using promise\r\n */\r\n private loadFBX(url: string): Promise<THREE.Group> {\r\n return new Promise((resolve, reject) => {\r\n this._fbxLoader.load(\r\n url,\r\n fbx => resolve(fbx),\r\n undefined,\r\n error => reject(error),\r\n )\r\n })\r\n }\r\n\r\n /**\r\n * Load Collada file using promise\r\n */\r\n private loadCollada(url: string): Promise<any> {\r\n return new Promise((resolve, reject) => {\r\n this._colladaLoader.load(\r\n url,\r\n collada => resolve(collada),\r\n undefined,\r\n error => reject(error),\r\n )\r\n })\r\n }\r\n\r\n /**\r\n * Setup animations for a model\r\n */\r\n private setupAnimations(model: THREE.Group, animations: THREE.AnimationClip[]): THREE.AnimationMixer {\r\n const mixer = new THREE.AnimationMixer(model)\r\n\r\n for (const clip of animations) {\r\n const action = mixer.clipAction(clip)\r\n action.play()\r\n }\r\n\r\n return mixer\r\n }\r\n\r\n /**\r\n * Setup gizmo controls for a model\r\n */\r\n private setupGizmo(model: THREE.Group): GizmoController {\r\n if (!this._world) {\r\n throw new Error('World not available for gizmo setup')\r\n }\r\n\r\n const gizmoController = new GizmoController(this._world)\r\n gizmoController.attach(model)\r\n return gizmoController\r\n }\r\n\r\n /**\r\n * Start animation loop for all mixers\r\n */\r\n private startAnimationLoop(): void {\r\n const animate = () => {\r\n const delta = this._animationClock.getDelta()\r\n\r\n for (const [, modelInfo] of this._models) {\r\n if (modelInfo.mixer) {\r\n modelInfo.mixer.update(delta)\r\n }\r\n }\r\n\r\n this._animationId = requestAnimationFrame(animate)\r\n }\r\n\r\n animate()\r\n }\r\n\r\n /**\r\n * Stop animation loop\r\n */\r\n private stopAnimationLoop(): void {\r\n if (this._animationId !== null) {\r\n cancelAnimationFrame(this._animationId)\r\n this._animationId = null\r\n }\r\n }\r\n\r\n /**\r\n * Dispose and cleanup all resources\r\n */\r\n dispose(): void {\r\n this.stopAnimationLoop()\r\n\r\n for (const modelInfo of this._models.values()) {\r\n if (this._world) {\r\n this._world.scene.three.remove(modelInfo.model)\r\n }\r\n\r\n if (modelInfo.gizmoController) {\r\n modelInfo.gizmoController.dispose()\r\n }\r\n\r\n if (modelInfo.mixer) {\r\n modelInfo.mixer.stopAllAction()\r\n }\r\n\r\n if (modelInfo.fileUrl) {\r\n URL.revokeObjectURL(modelInfo.fileUrl)\r\n }\r\n }\r\n\r\n this._models.clear()\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/bim/src/ModelManager/index.ts"],"sourcesContent":["import * as THREE from 'three'\r\nimport * as OBC from '@thatopen/components'\r\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'\r\nimport { OBJLoader } from 'three/addons/loaders/OBJLoader.js'\r\nimport { FBXLoader } from 'three/addons/loaders/FBXLoader.js'\r\nimport { ColladaLoader } from 'three/addons/loaders/ColladaLoader.js'\r\nimport { CurrentWorld } from '../CurrentWorld'\r\nimport { GizmoController } from '../../utils/GizmoController'\r\nimport { ViewportGizmo } from '../ViewportGizmo'\r\n\r\nexport interface ModelInfo {\r\n id: string\r\n name: string\r\n model: THREE.Group\r\n mixer?: THREE.AnimationMixer\r\n gizmoController?: GizmoController\r\n fileUrl?: string\r\n}\r\n\r\nexport interface LoadModelOptions {\r\n position?: THREE.Vector3\r\n scale?: number | THREE.Vector3\r\n rotation?: THREE.Euler\r\n enableGizmo?: boolean\r\n enableAnimations?: boolean\r\n extension?: string\r\n}\r\n\r\nexport class ModelManager extends OBC.Component {\r\n static uuid = '4cf01658-4a4e-49bd-8c89-4574db0032e9' as const\r\n\r\n enabled = true\r\n\r\n onModelLoaded = new OBC.Event<ModelInfo>()\r\n onModelTransformed = new OBC.Event<ModelInfo>()\r\n\r\n private _components: OBC.Components\r\n private _world: OBC.World | null = null\r\n private _gltfLoader: GLTFLoader\r\n private _objLoader: OBJLoader \r\n private _fbxLoader: FBXLoader \r\n private _colladaLoader: ColladaLoader \r\n private _models: Map<string, ModelInfo> = new Map()\r\n private _animationClock: THREE.Clock = new THREE.Clock()\r\n private _animationId: number | null = null\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(ModelManager.uuid, this)\r\n this._components = components\r\n this._gltfLoader = new GLTFLoader()\r\n this._objLoader = new OBJLoader()\r\n this._fbxLoader = new FBXLoader()\r\n this._colladaLoader = new ColladaLoader()\r\n this._world = components.get(CurrentWorld).world\r\n this.startAnimationLoop()\r\n }\r\n\r\n /**\r\n * Load a 3D model (GLB/GLTF/OBJ/FBX/DAE) from a file or URL\r\n */\r\n async load(\r\n fileOrUrl: File | string,\r\n id: string,\r\n name: string,\r\n options: LoadModelOptions = {},\r\n ): Promise<ModelInfo | null> {\r\n if (!this._world) {\r\n console.error('World not available for model loading')\r\n return null\r\n }\r\n\r\n try {\r\n let fileUrl: string\r\n\r\n fileUrl = fileOrUrl instanceof File ? URL.createObjectURL(fileOrUrl) : fileOrUrl\r\n\r\n const extension = options.extension || this.getFileExtension(fileOrUrl)\r\n\r\n const result = await this.loadModelByType(fileUrl, extension)\r\n const model = result.scene || result\r\n\r\n if (options.position) {\r\n model.position.copy(options.position)\r\n }\r\n\r\n if (options.scale) {\r\n if (typeof options.scale === 'number') {\r\n model.scale.setScalar(options.scale)\r\n }\r\n else {\r\n model.scale.copy(options.scale)\r\n }\r\n }\r\n\r\n if (options.rotation) {\r\n model.rotation.copy(options.rotation)\r\n }\r\n\r\n this._world.scene.three.add(model)\r\n\r\n\r\n let mixer: THREE.AnimationMixer | undefined\r\n if (options.enableAnimations && result.animations && result.animations.length > 0) {\r\n mixer = this.setupAnimations(model, result.animations)\r\n }\r\n\r\n let gizmoController: GizmoController | undefined\r\n if (options.enableGizmo) {\r\n gizmoController = this.setupGizmo(model)\r\n }\r\n\r\n const modelInfo: ModelInfo = {\r\n id,\r\n name,\r\n model,\r\n mixer,\r\n gizmoController,\r\n fileUrl: fileOrUrl instanceof File ? fileUrl : undefined,\r\n }\r\n\r\n this._models.set(id, modelInfo);\r\n\r\n (model as any).modelId = id\r\n\r\n this.onModelLoaded.trigger(modelInfo)\r\n\r\n return modelInfo\r\n }\r\n catch (error) {\r\n console.error('Error loading 3D model:', error)\r\n\r\n if (fileOrUrl instanceof File) {\r\n URL.revokeObjectURL(fileOrUrl instanceof File ? URL.createObjectURL(fileOrUrl) : '')\r\n }\r\n\r\n return null\r\n }\r\n }\r\n\r\n /**\r\n * Remove a model from the scene\r\n */\r\n remove(id: string): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo || !this._world) return false\r\n\r\n this._world.scene.three.remove(modelInfo.model)\r\n\r\n\r\n if (modelInfo.gizmoController) {\r\n modelInfo.gizmoController.dispose()\r\n }\r\n\r\n // Cleanup mixer\r\n if (modelInfo.mixer) {\r\n modelInfo.mixer.stopAllAction()\r\n }\r\n\r\n // Cleanup file URL if it exists\r\n if (modelInfo.fileUrl) {\r\n URL.revokeObjectURL(modelInfo.fileUrl)\r\n }\r\n\r\n // Remove from storage\r\n this._models.delete(id)\r\n\r\n return true\r\n }\r\n\r\n /**\r\n * Get model info by ID\r\n */\r\n getModel(id: string): ModelInfo | undefined {\r\n return this._models.get(id)\r\n }\r\n\r\n /**\r\n * Get model info by name (file.name)\r\n */\r\n getModelByName(name: string): ModelInfo | undefined {\r\n for (const modelInfo of this._models.values()) {\r\n if (modelInfo.name === name) return modelInfo\r\n }\r\n return undefined\r\n }\r\n\r\n /**\r\n * Get all loaded models\r\n */\r\n getAllModels(): ModelInfo[] {\r\n return [...this._models.values()]\r\n }\r\n\r\n /**\r\n * Enable/disable gizmo for a model\r\n */\r\n toggleGizmo(id: string, enable: boolean): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo || !this._world) return false\r\n\r\n if (enable && !modelInfo.gizmoController) {\r\n modelInfo.gizmoController = this.setupGizmo(modelInfo.model)\r\n return true\r\n }\r\n else if (!enable && modelInfo.gizmoController) {\r\n modelInfo.gizmoController.dispose()\r\n modelInfo.gizmoController = undefined\r\n return true\r\n }\r\n\r\n return false\r\n }\r\n\r\n /**\r\n * Update model position\r\n */\r\n setPosition(id: string, position: THREE.Vector3): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo) return false\r\n\r\n modelInfo.model.position.copy(position)\r\n this.onModelTransformed.trigger(modelInfo)\r\n return true\r\n }\r\n\r\n /**\r\n * Update model scale\r\n */\r\n setScale(id: string, scale: number | THREE.Vector3): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo) return false\r\n\r\n if (typeof scale === 'number') {\r\n modelInfo.model.scale.setScalar(scale)\r\n }\r\n else {\r\n modelInfo.model.scale.copy(scale)\r\n }\r\n\r\n this.onModelTransformed.trigger(modelInfo)\r\n return true\r\n }\r\n\r\n /**\r\n * Update model rotation\r\n */\r\n setRotation(id: string, rotation: THREE.Euler): boolean {\r\n const modelInfo = this._models.get(id)\r\n if (!modelInfo) return false\r\n\r\n modelInfo.model.rotation.copy(rotation)\r\n this.onModelTransformed.trigger(modelInfo)\r\n return true\r\n }\r\n\r\n /**\r\n * Get file extension from File or URL\r\n */\r\n private getFileExtension(fileOrUrl: File | string): string {\r\n if (fileOrUrl instanceof File) {\r\n return fileOrUrl.name.split('.').pop()?.toLowerCase() || ''\r\n }\r\n const urlPath = fileOrUrl.split('?')[0]\r\n return urlPath.split('.').pop()?.toLowerCase() || ''\r\n }\r\n\r\n /**\r\n * Load model based on file type\r\n */\r\n private async loadModelByType(url: string, extension: string): Promise<any> {\r\n switch (extension) {\r\n case 'gltf':\r\n case 'glb':\r\n return this.loadGLTF(url)\r\n case 'obj':\r\n return this.loadOBJ(url)\r\n case 'fbx':\r\n return this.loadFBX(url)\r\n case 'dae':\r\n case 'collada':\r\n return this.loadCollada(url)\r\n default:\r\n throw new Error(`Unsupported file format: ${extension}`)\r\n }\r\n }\r\n\r\n /**\r\n * Load GLTF file using promise\r\n */\r\n private loadGLTF(url: string): Promise<any> {\r\n return new Promise((resolve, reject) => {\r\n this._gltfLoader.load(\r\n url,\r\n gltf => resolve(gltf),\r\n undefined,\r\n error => reject(error),\r\n )\r\n })\r\n }\r\n\r\n /**\r\n * Load OBJ file using promise\r\n */\r\n private loadOBJ(url: string): Promise<THREE.Group> {\r\n return new Promise((resolve, reject) => {\r\n this._objLoader.load(\r\n url,\r\n obj => resolve(obj),\r\n undefined,\r\n error => reject(error),\r\n )\r\n })\r\n }\r\n\r\n /**\r\n * Load FBX file using promise\r\n */\r\n private loadFBX(url: string): Promise<THREE.Group> {\r\n return new Promise((resolve, reject) => {\r\n this._fbxLoader.load(\r\n url,\r\n fbx => resolve(fbx),\r\n undefined,\r\n error => reject(error),\r\n )\r\n })\r\n }\r\n\r\n /**\r\n * Load Collada file using promise\r\n */\r\n private loadCollada(url: string): Promise<any> {\r\n return new Promise((resolve, reject) => {\r\n this._colladaLoader.load(\r\n url,\r\n collada => resolve(collada),\r\n undefined,\r\n error => reject(error),\r\n )\r\n })\r\n }\r\n\r\n /**\r\n * Setup animations for a model\r\n */\r\n private setupAnimations(model: THREE.Group, animations: THREE.AnimationClip[]): THREE.AnimationMixer {\r\n const mixer = new THREE.AnimationMixer(model)\r\n\r\n for (const clip of animations) {\r\n const action = mixer.clipAction(clip)\r\n action.play()\r\n }\r\n\r\n return mixer\r\n }\r\n\r\n /**\r\n * Setup gizmo controls for a model\r\n */\r\n private setupGizmo(model: THREE.Group): GizmoController {\r\n if (!this._world) {\r\n throw new Error('World not available for gizmo setup')\r\n }\r\n\r\n const gizmoController = new GizmoController(this._world)\r\n\r\n // Enter/Esc detach the gizmo internally; clear our reference so callers\r\n // (and the marker visibility check) know editing has ended.\r\n const endEditing = () => {\r\n const modelInfo = [...this._models.values()].find(info => info.model === model)\r\n if (!modelInfo) return\r\n model.updateMatrixWorld(true)\r\n modelInfo.gizmoController = undefined\r\n this.onModelTransformed.trigger(modelInfo)\r\n }\r\n gizmoController.onAccept = endEditing\r\n gizmoController.onCancel = endEditing\r\n\r\n gizmoController.attach(model)\r\n return gizmoController\r\n }\r\n\r\n /**\r\n * Start animation loop for all mixers\r\n */\r\n private startAnimationLoop(): void {\r\n const animate = () => {\r\n const delta = this._animationClock.getDelta()\r\n\r\n for (const [, modelInfo] of this._models) {\r\n if (modelInfo.mixer) {\r\n modelInfo.mixer.update(delta)\r\n }\r\n }\r\n\r\n this._animationId = requestAnimationFrame(animate)\r\n }\r\n\r\n animate()\r\n }\r\n\r\n /**\r\n * Stop animation loop\r\n */\r\n private stopAnimationLoop(): void {\r\n if (this._animationId !== null) {\r\n cancelAnimationFrame(this._animationId)\r\n this._animationId = null\r\n }\r\n }\r\n\r\n /**\r\n * Dispose and cleanup all resources\r\n */\r\n dispose(): void {\r\n this.stopAnimationLoop()\r\n\r\n for (const modelInfo of this._models.values()) {\r\n if (this._world) {\r\n this._world.scene.three.remove(modelInfo.model)\r\n }\r\n\r\n if (modelInfo.gizmoController) {\r\n modelInfo.gizmoController.dispose()\r\n }\r\n\r\n if (modelInfo.mixer) {\r\n modelInfo.mixer.stopAllAction()\r\n }\r\n\r\n if (modelInfo.fileUrl) {\r\n URL.revokeObjectURL(modelInfo.fileUrl)\r\n }\r\n }\r\n\r\n this._models.clear()\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SimpleBimViewer.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/bim/src/SimpleBimViewer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAc/B,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAMnD,UAAU,KAAK;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,eAAe,CAAC,EAAC,IAAI,EAAE,KAAc,EAAE,MAAe,EAAC,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"SimpleBimViewer.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/bim/src/SimpleBimViewer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAc/B,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAMnD,UAAU,KAAK;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,eAAe,CAAC,EAAC,IAAI,EAAE,KAAc,EAAE,MAAe,EAAC,EAAE,KAAK,qBAsR7E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/bim/src/SimpleBimViewer.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport * as OBC from \"@thatopen/components\";\r\nimport * as OBF from \"@thatopen/components-front\"\r\nimport * as THREE from \"three\";\r\nimport { BimContext } from \"../../../../store\";\r\nimport { LoadingSpinner } from '../../../ui/LoadingSpinner';\r\n\r\nimport { CurrentWorld } from \"../src/CurrentWorld\";\r\nimport { CurrentCamera } from '../src/CurrentCamera';\r\nimport { FitCamera } from './FitCamera';\r\nimport { CameraProjection } from './CameraProjection';\r\nimport { SpatialStructure } from './SpatialStructure';\r\nimport { LoadModels } from './LoadModels';\r\nimport { IfcToFragments } from './IfcToFragments';\r\nimport { DbFile } from '../../../../types/dbTypes';\r\nimport { ModelManager } from './ModelManager';\r\nimport { FloorplanTool } from './FloorplanTool';\r\nimport { ElevationsTool } from './ElevationsTool';\r\nimport { ViewModeCoordinator } from './lib/ViewModeCoordinator';\r\n\r\ninterface Props {\r\n file: DbFile\r\n width?: string\r\n height?: string\r\n}\r\n\r\nexport function SimpleBimViewer({file, width = \"100%\", height = \"100%\"}: Props) {\r\n\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext);\r\n const { bimComponents } = bimState.bim;\r\n\r\n const containerRef = React.useRef<HTMLDivElement | null>(null);\r\n const workerUrlRef = React.useRef<string | null>(null); // To store the worker URL for cleanup\r\n const resizeObserverRef = React.useRef<ResizeObserver | null>(null); // To store the ResizeObserver for cleanup\r\n const [isLoading, setIsLoading] = React.useState(false);\r\n const [loadingMessage, setLoadingMessage] = React.useState('');\r\n\r\n const createViewer = React.useCallback(\r\n async () => {\r\n\r\n THREE.Object3D.DEFAULT_UP.set(0, 1, 0); // Y-up\r\n\r\n if (!containerRef.current || bimComponents) return;\r\n\r\n const container = containerRef.current;\r\n const components = new OBC.Components();\r\n const worlds = components.get(OBC.Worlds);\r\n const world = worlds.create<\r\n OBC.ShadowedScene,\r\n OBC.OrthoPerspectiveCamera,\r\n OBF.PostproductionRenderer\r\n >();\r\n\r\n world.scene = new OBC.ShadowedScene(components);\r\n\r\n world.renderer = new OBF.PostproductionRenderer(components, container);\r\n world.camera = new OBC.OrthoPerspectiveCamera(components);\r\n\r\n components.init();\r\n\r\n world.scene.setup();\r\n world.scene.three.background = null;\r\n\r\n const grids = components.get(OBC.Grids);\r\n const grid = grids.create(world);\r\n const axesHelper = new THREE.AxesHelper(5);\r\n world.scene.three.add(axesHelper);\r\n\r\n const fragments = components.get(OBC.FragmentsManager);\r\n\r\n const githubUrl =\r\n \"https://thatopen.github.io/engine_fragment/resources/worker.mjs\";\r\n const fetchedUrl = await fetch(githubUrl);\r\n const workerBlob = await fetchedUrl.blob();\r\n const workerFile = new File([workerBlob], \"worker.mjs\", {\r\n type: \"text/javascript\",\r\n });\r\n const workerUrl = URL.createObjectURL(workerFile);\r\n workerUrlRef.current = workerUrl; // Store reference for cleanup\r\n fragments.init(workerUrl);\r\n\r\n world.camera.controls.addEventListener(\"control\", () =>\r\n fragments.core.update(),\r\n );\r\n\r\n world.camera.controls.restThreshold = 0.005;\r\n world.camera.controls.addEventListener(\"rest\", () =>\r\n fragments.core.update(true)\r\n );\r\n\r\n components.get(CurrentWorld).world = world;\r\n components.get(CurrentCamera).camera = world.camera;\r\n\r\n // Enable shadows\r\n world.renderer.three.shadowMap.enabled = true;\r\n world.renderer.three.shadowMap.type = THREE.PCFSoftShadowMap;\r\n world.scene.setup({\r\n shadows: {\r\n cascade: 1,\r\n resolution: 1024,\r\n },\r\n });\r\n\r\n world.scene.distanceRenderer.excludedObjects.add(grid.three);\r\n\r\n await world.scene.updateShadows();\r\n\r\n world.camera.controls.addEventListener(\"rest\", async () => {\r\n await world.scene.updateShadows();\r\n });\r\n\r\n world.scene.three.background = null;\r\n\r\n bimDispatch({\r\n type: \"SET_COMPONENTS\",\r\n payload: { bimComponents: components, world, fragments }\r\n });\r\n\r\n // Use requestAnimationFrame to ensure resize happens after render\r\n requestAnimationFrame(() => {\r\n const containerWidth = container.clientWidth || 1303;\r\n const containerHeight = container.clientHeight || 500;\r\n world.renderer.resize(new THREE.Vector2(containerWidth, containerHeight));\r\n });\r\n\r\n // Get components (this will instantiate them if needed)\r\n components.get(FitCamera);\r\n components.get(CameraProjection);\r\n components.get(SpatialStructure);\r\n components.get(LoadModels);\r\n components.get(ViewModeCoordinator);\r\n components.get(FloorplanTool);\r\n components.get(ElevationsTool);\r\n\r\n // Grid injection is safe here — fragments.core is initialized.\r\n if (grid) {\r\n components.get(FloorplanTool).setGrid(grid);\r\n components.get(ElevationsTool).setGrid(grid);\r\n }\r\n const ifcToFragments = components.get(IfcToFragments);\r\n const loadModels = components.get(LoadModels);\r\n const modelManager = components.get(ModelManager);\r\n\r\n // Handle resize using container dimensions\r\n const handleResize = () => {\r\n const width = container.clientWidth || 1303;\r\n const height = container.clientHeight || 500;\r\n world.renderer?.resize(new THREE.Vector2(width, height));\r\n };\r\n \r\n // Watch for container size changes using ResizeObserver. This\r\n // already covers the window-resize case: when the window resizes,\r\n // the flex layout reshapes this container, and ResizeObserver\r\n // fires.\r\n const resizeObserver = new ResizeObserver(() => {\r\n handleResize();\r\n });\r\n resizeObserver.observe(container);\r\n resizeObserverRef.current = resizeObserver;\r\n\r\n // Audit Phase 1.E (F-18): same fix as BimViewer.tsx — removed\r\n // the unmatched window.addEventListener('resize', handleResize)\r\n // that leaked one listener per SimpleBimViewer mount.\r\n\r\n // Listen to loading state changes from both components\r\n ifcToFragments.onLoadingStateChanged.add(({ isLoading, message }) => {\r\n setIsLoading(isLoading);\r\n setLoadingMessage(message);\r\n });\r\n\r\n loadModels.onLoadingStateChanged.add(({ isLoading, message }) => {\r\n setIsLoading(isLoading);\r\n setLoadingMessage(message);\r\n });\r\n\r\n // Load the model based on file extension\r\n try {\r\n const ext = file.extension.toLowerCase();\r\n \r\n if (ext === 'frag') {\r\n await loadModels.load(file.url, String(file.id));\r\n } else if (ext === 'ifc') {\r\n const fetchedFile = await fetch(file.url);\r\n const ifcFile = new File([await fetchedFile.arrayBuffer()], file.name, { type: \"application/octet-stream\" });\r\n const fragmentBytes = await ifcToFragments.loadFromFile(ifcFile);\r\n \r\n const fragmentBuffer = fragmentBytes.buffer as ArrayBuffer;\r\n const model = await fragments.core.load(fragmentBuffer, { modelId: String(file.id) });\r\n \r\n world.scene.three.add(model.object);\r\n model.useCamera(world.camera.three);\r\n \r\n setIsLoading(false);\r\n setLoadingMessage('');\r\n } else if (['gltf', 'glb', 'fbx', 'obj', 'dae', 'collada'].includes(ext)) {\r\n setIsLoading(true);\r\n setLoadingMessage('Loading 3D Model...');\r\n \r\n const urlWithExtension = file.url.includes('.') ? file.url : `${file.url}?ext=${ext}`;\r\n \r\n const fetchedFile = await fetch(file.url);\r\n const blob = await fetchedFile.blob();\r\n const modelFile = new File([blob], `${file.name}.${ext}`, { type: blob.type });\r\n \r\n const modelInfo = await modelManager.load(\r\n modelFile,\r\n String(file.id),\r\n file.name,\r\n {\r\n enableAnimations: true,\r\n enableGizmo: false\r\n }\r\n );\r\n \r\n if (modelInfo) {\r\n const box = new THREE.Box3().setFromObject(modelInfo.model);\r\n const center = box.getCenter(new THREE.Vector3());\r\n const size = box.getSize(new THREE.Vector3());\r\n \r\n const maxDim = Math.max(size.x, size.y, size.z);\r\n const fov = world.camera.three.fov * (Math.PI / 180);\r\n let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2));\r\n \r\n cameraZ *= 1.5;\r\n \r\n const camX = center.x;\r\n const camY = center.y;\r\n const camZ = center.z + cameraZ;\r\n \r\n world.camera.controls.setLookAt(\r\n camX, camY, camZ,\r\n center.x, center.y, center.z,\r\n true\r\n );\r\n }\r\n \r\n setIsLoading(false);\r\n setLoadingMessage('');\r\n }\r\n\r\n fragments.core.update(true);\r\n \r\n setTimeout(() => {\r\n const width = container.clientWidth || 1303;\r\n const height = container.clientHeight || 500;\r\n world.renderer.resize(new THREE.Vector2(width, height));\r\n \r\n window.dispatchEvent(new Event('resize'));\r\n }, 100);\r\n } catch (error) {\r\n console.error('Error loading BIM file:', error);\r\n setIsLoading(false);\r\n }\r\n \r\n }, []\r\n );\r\n\r\n React.useEffect(() => {\r\n createViewer();\r\n\r\n // Cleanup function to properly dispose of components when unmounting\r\n return () => {\r\n if (resizeObserverRef.current) {\r\n resizeObserverRef.current.disconnect();\r\n }\r\n if (bimComponents) {\r\n bimComponents.dispose();\r\n }\r\n bimDispatch({\r\n type: \"DISPOSE-BIM\"\r\n });\r\n };\r\n }, []);\r\n\r\n return (\r\n <div\r\n style={{\r\n position: \"relative\",\r\n width,\r\n height,\r\n minHeight: height,\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\"\r\n }}\r\n >\r\n {isLoading && (\r\n <div className=\"absolute z-10 inset-0 bg-background/50 backdrop-blur-[1px] flex items-center justify-center pointer-events-none\">\r\n <div className=\"self-stretch text-center text-foreground text-xl font-semibold font-['Inter'] leading-7 flex items-center justify-center gap-2\">\r\n {loadingMessage || \"Loading BIM Model\"}\r\n <LoadingSpinner size={20} />\r\n </div>\r\n </div>\r\n )}\r\n <div\r\n className=\"bim-container\"\r\n id=\"bim-viewer-container\"\r\n ref={containerRef}\r\n style={{\r\n width: \"100%\",\r\n height: \"100%\",\r\n flex: 1,\r\n position: \"relative\",\r\n background: \"radial-gradient(circle, rgba(255, 255, 255, 1) 50%, rgba(220, 220, 220, 1) 100%)\",\r\n }}\r\n />\r\n </div>\r\n );\r\n}"],"mappings":"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","names":["width","height","isLoading"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/bim/src/SimpleBimViewer.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport * as OBC from \"@thatopen/components\";\r\nimport * as OBF from \"@thatopen/components-front\"\r\nimport * as THREE from \"three\";\r\nimport { BimContext } from \"../../../../store\";\r\nimport { LoadingSpinner } from '../../../ui/LoadingSpinner';\r\n\r\nimport { CurrentWorld } from \"../src/CurrentWorld\";\r\nimport { CurrentCamera } from '../src/CurrentCamera';\r\nimport { FitCamera } from './FitCamera';\r\nimport { CameraProjection } from './CameraProjection';\r\nimport { SpatialStructure } from './SpatialStructure';\r\nimport { LoadModels } from './LoadModels';\r\nimport { IfcToFragments } from './IfcToFragments';\r\nimport { DbFile } from '../../../../types/dbTypes';\r\nimport { ModelManager } from './ModelManager';\r\nimport { FloorplanTool } from './FloorplanTool';\r\nimport { ElevationsTool } from './ElevationsTool';\r\nimport { ViewModeCoordinator } from './lib/ViewModeCoordinator';\r\n\r\ninterface Props {\r\n file: DbFile\r\n width?: string\r\n height?: string\r\n}\r\n\r\nexport function SimpleBimViewer({file, width = \"100%\", height = \"100%\"}: Props) {\r\n\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext);\r\n const { bimComponents } = bimState.bim;\r\n\r\n const containerRef = React.useRef<HTMLDivElement | null>(null);\r\n const workerUrlRef = React.useRef<string | null>(null); // To store the worker URL for cleanup\r\n const resizeObserverRef = React.useRef<ResizeObserver | null>(null); // To store the ResizeObserver for cleanup\r\n const [isLoading, setIsLoading] = React.useState(false);\r\n const [loadingMessage, setLoadingMessage] = React.useState('');\r\n\r\n const createViewer = React.useCallback(\r\n async () => {\r\n\r\n THREE.Object3D.DEFAULT_UP.set(0, 1, 0); // Y-up\r\n\r\n if (!containerRef.current || bimComponents) return;\r\n\r\n const container = containerRef.current;\r\n const components = new OBC.Components();\r\n const worlds = components.get(OBC.Worlds);\r\n const world = worlds.create<\r\n OBC.ShadowedScene,\r\n OBC.OrthoPerspectiveCamera,\r\n OBF.PostproductionRenderer\r\n >();\r\n\r\n world.scene = new OBC.ShadowedScene(components);\r\n\r\n world.renderer = new OBF.PostproductionRenderer(components, container);\r\n world.camera = new OBC.OrthoPerspectiveCamera(components);\r\n\r\n components.init();\r\n\r\n world.scene.setup();\r\n world.scene.three.background = null;\r\n\r\n const grids = components.get(OBC.Grids);\r\n const grid = grids.create(world);\r\n const axesHelper = new THREE.AxesHelper(5);\r\n world.scene.three.add(axesHelper);\r\n\r\n const fragments = components.get(OBC.FragmentsManager);\r\n\r\n const githubUrl =\r\n \"https://thatopen.github.io/engine_fragment/resources/worker.mjs\";\r\n const fetchedUrl = await fetch(githubUrl);\r\n const workerBlob = await fetchedUrl.blob();\r\n const workerFile = new File([workerBlob], \"worker.mjs\", {\r\n type: \"text/javascript\",\r\n });\r\n const workerUrl = URL.createObjectURL(workerFile);\r\n workerUrlRef.current = workerUrl; // Store reference for cleanup\r\n fragments.init(workerUrl);\r\n\r\n world.camera.controls.addEventListener(\"control\", () =>\r\n fragments.core.update(),\r\n );\r\n\r\n world.camera.controls.restThreshold = 0.005;\r\n world.camera.controls.addEventListener(\"rest\", () =>\r\n fragments.core.update(true)\r\n );\r\n\r\n components.get(CurrentWorld).world = world;\r\n components.get(CurrentCamera).camera = world.camera;\r\n\r\n // Enable shadows\r\n world.renderer.three.shadowMap.enabled = true;\r\n world.renderer.three.shadowMap.type = THREE.PCFSoftShadowMap;\r\n world.scene.setup({\r\n shadows: {\r\n cascade: 1,\r\n resolution: 1024,\r\n },\r\n });\r\n\r\n world.scene.distanceRenderer.excludedObjects.add(grid.three);\r\n\r\n await world.scene.updateShadows();\r\n\r\n world.camera.controls.addEventListener(\"rest\", async () => {\r\n await world.scene.updateShadows();\r\n });\r\n\r\n world.scene.three.background = null;\r\n\r\n bimDispatch({\r\n type: \"SET_COMPONENTS\",\r\n payload: { bimComponents: components, world, fragments }\r\n });\r\n\r\n // Use requestAnimationFrame to ensure resize happens after render\r\n requestAnimationFrame(() => {\r\n const containerWidth = container.clientWidth || 1303;\r\n const containerHeight = container.clientHeight || 500;\r\n world.renderer.resize(new THREE.Vector2(containerWidth, containerHeight));\r\n });\r\n\r\n // Get components (this will instantiate them if needed)\r\n components.get(FitCamera);\r\n components.get(CameraProjection);\r\n components.get(SpatialStructure);\r\n components.get(LoadModels);\r\n components.get(ViewModeCoordinator);\r\n components.get(FloorplanTool);\r\n components.get(ElevationsTool);\r\n\r\n // Grid injection is safe here — fragments.core is initialized.\r\n if (grid) {\r\n components.get(FloorplanTool).setGrid(grid);\r\n components.get(ElevationsTool).setGrid(grid);\r\n }\r\n const ifcToFragments = components.get(IfcToFragments);\r\n const loadModels = components.get(LoadModels);\r\n const modelManager = components.get(ModelManager);\r\n\r\n // Handle resize using container dimensions\r\n const handleResize = () => {\r\n const width = container.clientWidth || 1303;\r\n const height = container.clientHeight || 500;\r\n world.renderer?.resize(new THREE.Vector2(width, height));\r\n };\r\n \r\n // Watch for container size changes using ResizeObserver. This\r\n // already covers the window-resize case: when the window resizes,\r\n // the flex layout reshapes this container, and ResizeObserver\r\n // fires.\r\n const resizeObserver = new ResizeObserver(() => {\r\n handleResize();\r\n });\r\n resizeObserver.observe(container);\r\n resizeObserverRef.current = resizeObserver;\r\n\r\n // Listen to loading state changes from both components\r\n ifcToFragments.onLoadingStateChanged.add(({ isLoading, message }) => {\r\n setIsLoading(isLoading);\r\n setLoadingMessage(message);\r\n });\r\n\r\n loadModels.onLoadingStateChanged.add(({ isLoading, message }) => {\r\n setIsLoading(isLoading);\r\n setLoadingMessage(message);\r\n });\r\n\r\n // Load the model based on file extension\r\n try {\r\n const ext = file.extension.toLowerCase();\r\n \r\n if (ext === 'frag') {\r\n await loadModels.load(file.url, String(file.id));\r\n } else if (ext === 'ifc') {\r\n const fetchedFile = await fetch(file.url);\r\n const ifcFile = new File([await fetchedFile.arrayBuffer()], file.name, { type: \"application/octet-stream\" });\r\n const fragmentBytes = await ifcToFragments.loadFromFile(ifcFile);\r\n \r\n const fragmentBuffer = fragmentBytes.buffer as ArrayBuffer;\r\n const model = await fragments.core.load(fragmentBuffer, { modelId: String(file.id) });\r\n \r\n world.scene.three.add(model.object);\r\n model.useCamera(world.camera.three);\r\n \r\n setIsLoading(false);\r\n setLoadingMessage('');\r\n } else if (['gltf', 'glb', 'fbx', 'obj', 'dae', 'collada'].includes(ext)) {\r\n setIsLoading(true);\r\n setLoadingMessage('Loading 3D Model...');\r\n \r\n const urlWithExtension = file.url.includes('.') ? file.url : `${file.url}?ext=${ext}`;\r\n \r\n const fetchedFile = await fetch(file.url);\r\n const blob = await fetchedFile.blob();\r\n const modelFile = new File([blob], `${file.name}.${ext}`, { type: blob.type });\r\n \r\n const modelInfo = await modelManager.load(\r\n modelFile,\r\n String(file.id),\r\n file.name,\r\n {\r\n enableAnimations: true,\r\n enableGizmo: false\r\n }\r\n );\r\n \r\n if (modelInfo) {\r\n const box = new THREE.Box3().setFromObject(modelInfo.model);\r\n const center = box.getCenter(new THREE.Vector3());\r\n const size = box.getSize(new THREE.Vector3());\r\n \r\n const maxDim = Math.max(size.x, size.y, size.z);\r\n const fov = world.camera.three.fov * (Math.PI / 180);\r\n let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2));\r\n \r\n cameraZ *= 1.5;\r\n \r\n const camX = center.x;\r\n const camY = center.y;\r\n const camZ = center.z + cameraZ;\r\n \r\n world.camera.controls.setLookAt(\r\n camX, camY, camZ,\r\n center.x, center.y, center.z,\r\n true\r\n );\r\n }\r\n \r\n setIsLoading(false);\r\n setLoadingMessage('');\r\n }\r\n\r\n fragments.core.update(true);\r\n \r\n setTimeout(() => {\r\n const width = container.clientWidth || 1303;\r\n const height = container.clientHeight || 500;\r\n world.renderer.resize(new THREE.Vector2(width, height));\r\n \r\n window.dispatchEvent(new Event('resize'));\r\n }, 100);\r\n } catch (error) {\r\n console.error('Error loading BIM file:', error);\r\n setIsLoading(false);\r\n }\r\n \r\n }, []\r\n );\r\n\r\n React.useEffect(() => {\r\n createViewer();\r\n\r\n // Cleanup function to properly dispose of components when unmounting\r\n return () => {\r\n if (resizeObserverRef.current) {\r\n resizeObserverRef.current.disconnect();\r\n }\r\n if (bimComponents) {\r\n bimComponents.dispose();\r\n }\r\n bimDispatch({\r\n type: \"DISPOSE-BIM\"\r\n });\r\n };\r\n }, []);\r\n\r\n return (\r\n <div\r\n style={{\r\n position: \"relative\",\r\n width,\r\n height,\r\n minHeight: height,\r\n overflow: \"hidden\",\r\n display: \"flex\",\r\n flexDirection: \"column\"\r\n }}\r\n >\r\n {isLoading && (\r\n <div className=\"absolute z-10 inset-0 bg-background/50 backdrop-blur-[1px] flex items-center justify-center pointer-events-none\">\r\n <div className=\"self-stretch text-center text-foreground text-xl font-semibold font-['Inter'] leading-7 flex items-center justify-center gap-2\">\r\n {loadingMessage || \"Loading BIM Model\"}\r\n <LoadingSpinner size={20} />\r\n </div>\r\n </div>\r\n )}\r\n <div\r\n className=\"bim-container\"\r\n id=\"bim-viewer-container\"\r\n ref={containerRef}\r\n style={{\r\n width: \"100%\",\r\n height: \"100%\",\r\n flex: 1,\r\n position: \"relative\",\r\n background: \"radial-gradient(circle, rgba(255, 255, 255, 1) 50%, rgba(220, 220, 220, 1) 100%)\",\r\n }}\r\n />\r\n </div>\r\n );\r\n}"],"mappings":"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","names":["width","height","isLoading"]}
|
|
@@ -14,8 +14,9 @@ interface Props {
|
|
|
14
14
|
* Compass-icon button + popover that lets the user set the building's
|
|
15
15
|
* true north for floorplan views. Two ways to set it:
|
|
16
16
|
* 1. Type a value 0-360 °.
|
|
17
|
-
* 2. Click "Pick line", then
|
|
18
|
-
*
|
|
17
|
+
* 2. Click "Pick line", then draw a line by clicking two points on the
|
|
18
|
+
* active floorplan — each point snaps to the nearest geometry vertex,
|
|
19
|
+
* and the bearing between them becomes the new north (Esc cancels).
|
|
19
20
|
*
|
|
20
21
|
* The popover closes when the user starts a pick (so the canvas isn't
|
|
21
22
|
* obscured) and stays in sync via the `northAngle` / `pickingNorth` props
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TrueNorthPopover.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/bim/src/lib/TrueNorthPopover.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,UAAU,KAAK;IACb,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAA;IAClB,wEAAwE;IACxE,YAAY,EAAE,OAAO,CAAA;IACrB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAA;IACjB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB;AAED
|
|
1
|
+
{"version":3,"file":"TrueNorthPopover.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/bim/src/lib/TrueNorthPopover.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,UAAU,KAAK;IACb,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAA;IAClB,wEAAwE;IACxE,YAAY,EAAE,OAAO,CAAA;IACrB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAA;IACjB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,WAAW,EACX,YAAY,GACb,EAAE,KAAK,qBA0GP"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/bim/src/lib/TrueNorthPopover.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { Button } from '../../../../ui/Button'\r\nimport { Input } from '../../../../ui/Input'\r\nimport {\r\n Popover,\r\n PopoverContent,\r\n PopoverTrigger,\r\n} from '../../../../ui/Popover'\r\n\r\ninterface Props {\r\n /** Current rotation in degrees (0-360, clockwise from default screen-up). */\r\n northAngle: number\r\n /** Whether the floorplan tool is currently waiting for a line click. */\r\n pickingNorth: boolean\r\n /** Disabled when no floorplan is active (rotation makes no sense yet). */\r\n disabled: boolean\r\n onChangeAngle: (degrees: number) => void\r\n onStartPick: () => void\r\n onCancelPick: () => void\r\n}\r\n\r\n/**\r\n * Compass-icon button + popover that lets the user set the building's\r\n * true north for floorplan views. Two ways to set it:\r\n * 1. Type a value 0-360 °.\r\n * 2. Click \"Pick line\", then
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/bim/src/lib/TrueNorthPopover.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { Button } from '../../../../ui/Button'\r\nimport { Input } from '../../../../ui/Input'\r\nimport {\r\n Popover,\r\n PopoverContent,\r\n PopoverTrigger,\r\n} from '../../../../ui/Popover'\r\n\r\ninterface Props {\r\n /** Current rotation in degrees (0-360, clockwise from default screen-up). */\r\n northAngle: number\r\n /** Whether the floorplan tool is currently waiting for a line click. */\r\n pickingNorth: boolean\r\n /** Disabled when no floorplan is active (rotation makes no sense yet). */\r\n disabled: boolean\r\n onChangeAngle: (degrees: number) => void\r\n onStartPick: () => void\r\n onCancelPick: () => void\r\n}\r\n\r\n/**\r\n * Compass-icon button + popover that lets the user set the building's\r\n * true north for floorplan views. Two ways to set it:\r\n * 1. Type a value 0-360 °.\r\n * 2. Click \"Pick line\", then draw a line by clicking two points on the\r\n * active floorplan — each point snaps to the nearest geometry vertex,\r\n * and the bearing between them becomes the new north (Esc cancels).\r\n *\r\n * The popover closes when the user starts a pick (so the canvas isn't\r\n * obscured) and stays in sync via the `northAngle` / `pickingNorth` props\r\n * coming from FloorplanTool.\r\n */\r\nexport function TrueNorthPopover({\r\n northAngle,\r\n pickingNorth,\r\n disabled,\r\n onChangeAngle,\r\n onStartPick,\r\n onCancelPick,\r\n}: Props) {\r\n const t = useTranslations('ViewSection')\r\n const [open, setOpen] = React.useState(false)\r\n const [draft, setDraft] = React.useState<string>(formatAngle(northAngle))\r\n\r\n // Keep the input in sync when the angle changes from outside (e.g. line\r\n // pick committed a new value).\r\n React.useEffect(() => {\r\n setDraft(formatAngle(northAngle))\r\n }, [northAngle])\r\n\r\n // Close the popover automatically when the user kicks off a line pick.\r\n React.useEffect(() => {\r\n if (pickingNorth) setOpen(false)\r\n }, [pickingNorth])\r\n\r\n const commitDraft = () => {\r\n const value = Number(draft)\r\n if (!Number.isFinite(value)) {\r\n setDraft(formatAngle(northAngle))\r\n return\r\n }\r\n onChangeAngle(value)\r\n }\r\n\r\n return (\r\n <Popover open={open} onOpenChange={setOpen}>\r\n <PopoverTrigger asChild>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-6 w-6 p-0\"\r\n title={t('trueNorthTitle')}\r\n disabled={disabled}\r\n >\r\n <LR.Compass className=\"h-3.5 w-3.5\" />\r\n </Button>\r\n </PopoverTrigger>\r\n <PopoverContent align=\"end\" className=\"w-64 p-3\">\r\n <div className=\"space-y-3\">\r\n <div className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\r\n {t('trueNorthTitle')}\r\n </div>\r\n\r\n <label className=\"block space-y-1\">\r\n <span className=\"text-xs text-muted-foreground\">\r\n {t('trueNorthAngleLabel')}\r\n </span>\r\n <div className=\"flex items-center gap-2\">\r\n <Input\r\n type=\"number\"\r\n min={0}\r\n max={360}\r\n step={1}\r\n value={draft}\r\n onChange={(e) => setDraft(e.target.value)}\r\n onBlur={commitDraft}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Enter') {\r\n commitDraft()\r\n e.currentTarget.blur()\r\n }\r\n }}\r\n className=\"h-8 text-sm\"\r\n disabled={disabled}\r\n />\r\n <span className=\"text-xs text-muted-foreground\">°</span>\r\n </div>\r\n </label>\r\n\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className=\"w-full justify-start gap-2 h-8 text-xs\"\r\n disabled={disabled || pickingNorth}\r\n onClick={onStartPick}\r\n >\r\n <LR.MousePointerClick className=\"h-3.5 w-3.5\" />\r\n {pickingNorth ? t('trueNorthPicking') : t('trueNorthPickLine')}\r\n </Button>\r\n\r\n {pickingNorth && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className=\"w-full h-7 text-xs\"\r\n onClick={onCancelPick}\r\n >\r\n {t('trueNorthCancelPick')}\r\n </Button>\r\n )}\r\n\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className=\"w-full h-7 text-xs\"\r\n disabled={disabled || northAngle === 0}\r\n onClick={() => onChangeAngle(0)}\r\n >\r\n <LR.RotateCcw className=\"h-3 w-3 mr-1\" />\r\n {t('trueNorthReset')}\r\n </Button>\r\n </div>\r\n </PopoverContent>\r\n </Popover>\r\n )\r\n}\r\n\r\nfunction formatAngle(degrees: number): string {\r\n // Show 0 decimals for whole numbers, 1 decimal otherwise — keeps the\r\n // input clean for typed values but readable after a line pick.\r\n const rounded = Math.round(degrees * 10) / 10\r\n return Number.isInteger(rounded) ? rounded.toFixed(0) : rounded.toFixed(1)\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFriendlyIfcClassName.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"useFriendlyIfcClassName.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAgBvE"}
|
|
@@ -4,10 +4,9 @@ function useFriendlyIfcClassName() {
|
|
|
4
4
|
const tLayers = useTranslations("DrawingLayers");
|
|
5
5
|
return React.useCallback(
|
|
6
6
|
(className) => {
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return className;
|
|
7
|
+
if (className.startsWith("IFC")) return className;
|
|
8
|
+
const key = className.startsWith("DrawingLayers.") ? className.slice("DrawingLayers.".length) : className;
|
|
9
|
+
return tLayers.has(key) ? tLayers(key) : className;
|
|
11
10
|
},
|
|
12
11
|
[tLayers]
|
|
13
12
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.ts"],"sourcesContent":["import * as React from 'react'\r\nimport { useTranslations } from 'next-intl'\r\n\r\n/**\r\n * Hook returning a function that maps a layer class name to a display label.\r\n * IFC class names (IFCWALL, IFCSLAB, …) are returned verbatim per-spec.\r\n * Special non-IFC layer keys (e.g. \"Door Swings\") are looked up in the\r\n * DrawingLayers i18n namespace so they get localized.\r\n */\r\nexport function useFriendlyIfcClassName(): (className: string) => string {\r\n const tLayers = useTranslations('DrawingLayers')\r\n\r\n return React.useCallback(\r\n (className: string) => {\r\n if (
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.ts"],"sourcesContent":["import * as React from 'react'\r\nimport { useTranslations } from 'next-intl'\r\n\r\n/**\r\n * Hook returning a function that maps a layer class name to a display label.\r\n * IFC class names (IFCWALL, IFCSLAB, …) are returned verbatim per-spec.\r\n * Special non-IFC layer keys (e.g. \"Door Swings\") are looked up in the\r\n * DrawingLayers i18n namespace so they get localized.\r\n */\r\nexport function useFriendlyIfcClassName(): (className: string) => string {\r\n const tLayers = useTranslations('DrawingLayers')\r\n\r\n return React.useCallback(\r\n (className: string) => {\r\n if (className.startsWith('IFC')) return className\r\n // The Fill/Cut group rows arrive fully qualified (\"DrawingLayers.cut\").\r\n // Strip the namespace prefix so the lookup lands on the bare key (\"cut\")\r\n // instead of resolving the dotted path as nested keys (which misses).\r\n const key = className.startsWith('DrawingLayers.')\r\n ? className.slice('DrawingLayers.'.length)\r\n : className\r\n return tLayers.has(key) ? tLayers(key) : className\r\n },\r\n [tLayers],\r\n )\r\n}\r\n"],"mappings":"AAAA,YAAY,WAAW;AACvB,SAAS,uBAAuB;AAQzB,SAAS,0BAAyD;AACvE,QAAM,UAAU,gBAAgB,eAAe;AAE/C,SAAO,MAAM;AAAA,IACX,CAAC,cAAsB;AACrB,UAAI,UAAU,WAAW,KAAK,EAAG,QAAO;AAIxC,YAAM,MAAM,UAAU,WAAW,gBAAgB,IAC7C,UAAU,MAAM,iBAAiB,MAAM,IACvC;AACJ,aAAO,QAAQ,IAAI,GAAG,IAAI,QAAQ,GAAG,IAAI;AAAA,IAC3C;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,OAAO,KAAK,EAAE,IAAI,EAAmB,MAAM,+BAA+B,CAAA;AAmB1E,UAAU,aAAa;IACrB,IAAI,EAAE,IAAI,CAAA;CACX;AAkCD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE,aAAa,qBA2PvD"}
|