@collabdt/core 0.0.42 → 0.0.43
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/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/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/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 +1 -0
- 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/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/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/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"}
|
|
@@ -3,6 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import * as LR from "lucide-react";
|
|
5
5
|
import Image from "next/image";
|
|
6
|
+
import { useTranslations } from "next-intl";
|
|
6
7
|
import { BuildingsContext, MenusContext, ToolsContext } from "../../../../../../store";
|
|
7
8
|
import { BimContext } from "../../../../../../store/BIM/context";
|
|
8
9
|
import { ViewerNames } from "../../../../../../types/dbTypes";
|
|
@@ -16,7 +17,7 @@ import Position3DCard from "./src/Position3DCard";
|
|
|
16
17
|
import { FileAdderDialog } from "../../../../map/src/tools/AddTools/AddFile/FileAdder";
|
|
17
18
|
import { CommentInput } from "../../../../../ui/Comments/CommentInput";
|
|
18
19
|
import { SensorInput } from "../../../../../ui/Sensors/SensorInput";
|
|
19
|
-
import { useFilesByBuildingId, useUploadFileToBuilding } from "../../../../../../hooks/files/files";
|
|
20
|
+
import { useFilesByBuildingId, useUploadFileToBuilding, useDeleteFile } from "../../../../../../hooks/files/files";
|
|
20
21
|
import { ViewerContextMenu } from "../../../../../ui/FilesManager";
|
|
21
22
|
const FilePreview = ({ file, mousePosition }) => {
|
|
22
23
|
const [imageUrl, setImageUrl] = React.useState(null);
|
|
@@ -44,6 +45,7 @@ const FilePreview = ({ file, mousePosition }) => {
|
|
|
44
45
|
};
|
|
45
46
|
function AddToBim({ tool }) {
|
|
46
47
|
var _a, _b;
|
|
48
|
+
const t = useTranslations("AddToBim");
|
|
47
49
|
const { dispatch: toolsDispatch, state: toolsState } = React.useContext(ToolsContext);
|
|
48
50
|
const { state: bimState } = React.useContext(BimContext);
|
|
49
51
|
const { bimComponents, world, fragments } = bimState.bim;
|
|
@@ -56,25 +58,45 @@ function AddToBim({ tool }) {
|
|
|
56
58
|
const { addPendingComment, removePendingComment, commentCount } = useCommentMarkers(world, buildingId);
|
|
57
59
|
const { addPendingSensor, removePendingSensor, sensorCount } = useSensorMarkers(world, buildingId);
|
|
58
60
|
const { uploadFile } = useUploadFileToBuilding(buildingId);
|
|
61
|
+
const { deleteFile } = useDeleteFile(buildingId);
|
|
62
|
+
const filesData = useFilesByBuildingId(buildingId).files || [];
|
|
63
|
+
const filesDataRef = React.useRef(filesData);
|
|
64
|
+
filesDataRef.current = filesData;
|
|
65
|
+
const visibleFileCount = filesData.filter((f) => {
|
|
66
|
+
var _a2;
|
|
67
|
+
const ext = (_a2 = f.extension) == null ? void 0 : _a2.toLowerCase();
|
|
68
|
+
return ext !== "ifc" && ext !== "frag" && f.tag !== "user" && f.tag !== "bim-file" && f.isVisible;
|
|
69
|
+
}).length;
|
|
70
|
+
const placementRef = React.useRef(null);
|
|
71
|
+
const handleMarkerAction = React.useCallback((id, action) => {
|
|
72
|
+
const api = placementRef.current;
|
|
73
|
+
if (!api) return;
|
|
74
|
+
if (action === "delete") {
|
|
75
|
+
const placed = api.getPlacedFile(id);
|
|
76
|
+
api.removePlacedFile(id);
|
|
77
|
+
const dbFile = placed ? filesDataRef.current.find((f) => {
|
|
78
|
+
var _a2;
|
|
79
|
+
return f.name === placed.name && ((_a2 = f.id) != null ? _a2 : 0) > 0;
|
|
80
|
+
}) : void 0;
|
|
81
|
+
if (dbFile == null ? void 0 : dbFile.id) deleteFile(dbFile.id).catch(() => {
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
api.editPlacedFile(id, action === "move" ? "translate" : action);
|
|
86
|
+
}, [deleteFile]);
|
|
59
87
|
const filePlacement = useFilePlacement(
|
|
60
88
|
bimComponents,
|
|
61
89
|
world,
|
|
62
90
|
fragments,
|
|
63
91
|
toolsDispatch,
|
|
64
92
|
buildingId,
|
|
65
|
-
uploadFile
|
|
93
|
+
uploadFile,
|
|
94
|
+
handleMarkerAction
|
|
66
95
|
);
|
|
67
|
-
|
|
68
|
-
const visibleFileCount = filesData.filter((f) => {
|
|
69
|
-
var _a2;
|
|
70
|
-
const ext = (_a2 = f.extension) == null ? void 0 : _a2.toLowerCase();
|
|
71
|
-
return ext !== "ifc" && ext !== "frag" && f.tag !== "user" && f.tag !== "bim-file" && f.isVisible;
|
|
72
|
-
}).length;
|
|
96
|
+
placementRef.current = filePlacement;
|
|
73
97
|
React.useEffect(() => {
|
|
74
98
|
if (world) initializeCSS2DRenderer(world);
|
|
75
99
|
}, [world]);
|
|
76
|
-
const filesDataRef = React.useRef(filesData);
|
|
77
|
-
filesDataRef.current = filesData;
|
|
78
100
|
React.useEffect(() => {
|
|
79
101
|
var _a2, _b2;
|
|
80
102
|
if (!world || !fragments) return;
|
|
@@ -94,7 +116,7 @@ function AddToBim({ tool }) {
|
|
|
94
116
|
setContextMenu({
|
|
95
117
|
x: e.clientX,
|
|
96
118
|
y: e.clientY,
|
|
97
|
-
file: matchedFile != null ? matchedFile : { id: 0, name: "
|
|
119
|
+
file: matchedFile != null ? matchedFile : { id: 0, name: t("bimObject"), url: "", type: "bim-file" }
|
|
98
120
|
});
|
|
99
121
|
};
|
|
100
122
|
canvas.addEventListener("contextmenu", handleContextMenu);
|
|
@@ -177,13 +199,13 @@ function AddToBim({ tool }) {
|
|
|
177
199
|
{
|
|
178
200
|
isOpen: true,
|
|
179
201
|
onClose: cancelAdding,
|
|
180
|
-
title: "
|
|
202
|
+
title: t("addFile"),
|
|
181
203
|
icon: LR.FilePlus,
|
|
182
204
|
accept: "*/*",
|
|
183
205
|
onFileSelect: (e) => filePlacement.handleFileSelect(e, addingMode),
|
|
184
206
|
onFileDrop: (file) => filePlacement.handleFileDrop(file, addingMode),
|
|
185
|
-
dropZoneTitle: "
|
|
186
|
-
dropZoneSubtext: "
|
|
207
|
+
dropZoneTitle: t("dropZoneTitle"),
|
|
208
|
+
dropZoneSubtext: t("dropZoneSubtext"),
|
|
187
209
|
hide: filePlacement.isPlacingFile && !!filePlacement.selectedFile
|
|
188
210
|
}
|
|
189
211
|
),
|
|
@@ -192,13 +214,13 @@ function AddToBim({ tool }) {
|
|
|
192
214
|
{
|
|
193
215
|
isOpen: true,
|
|
194
216
|
onClose: cancelAdding,
|
|
195
|
-
title: "
|
|
196
|
-
icon: LR.
|
|
217
|
+
title: t("addCadFile"),
|
|
218
|
+
icon: LR.DraftingCompass,
|
|
197
219
|
accept: ".dxf,.dwg",
|
|
198
220
|
onFileSelect: (e) => filePlacement.handleFileSelect(e, addingMode),
|
|
199
221
|
onFileDrop: (file) => filePlacement.handleFileDrop(file, addingMode),
|
|
200
|
-
dropZoneTitle: "
|
|
201
|
-
dropZoneSubtext: "
|
|
222
|
+
dropZoneTitle: t("dropZoneTitleCad"),
|
|
223
|
+
dropZoneSubtext: t("dropZoneSubtextCad"),
|
|
202
224
|
hide: filePlacement.isPlacingFile && !!filePlacement.selectedFile
|
|
203
225
|
}
|
|
204
226
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/index.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport * as React from \"react\"\r\nimport * as LR from \"lucide-react\"\r\nimport Image from \"next/image\"\r\n\r\nimport { BuildingsContext, MenusContext, ToolsContext } from \"../../../../../../store\"\r\nimport { BimContext } from \"../../../../../../store/BIM/context\"\r\nimport { ViewerNames } from \"../../../../../../types/dbTypes\"\r\nimport type { Tool, ToolbarToolType } from \"../../../../../../types/tools\"\r\nimport type { BimToolbarToolsType } from \"../bimToolbar\"\r\nimport { Button, useSidebar } from \"../../../../../ui\"\r\n\r\nimport { initializeCSS2DRenderer } from \"./src/FileMarkerUtils\"\r\nimport { useCommentMarkers } from \"./src/useCommentMarkers\"\r\nimport { useSensorMarkers } from \"./src/useSensorMarkers\"\r\nimport { useFilePlacement } from \"./src/useFilePlacement\"\r\nimport { AddToBimToolbar } from \"./src/AddToBimToolbar\"\r\nimport Position3DCard from \"./src/Position3DCard\"\r\nimport { FileAdderDialog } from \"../../../../map/src/tools/AddTools/AddFile/FileAdder\"\r\nimport { CommentInput } from \"../../../../../ui/Comments/CommentInput\"\r\nimport { SensorInput } from \"../../../../../ui/Sensors/SensorInput\"\r\nimport { useFilesByBuildingId, useUploadFileToBuilding } from \"../../../../../../hooks/files/files\"\r\nimport { ViewerContextMenu } from \"../../../../../ui/FilesManager\"\r\nimport type { DbFile } from \"../../../../../../types/dbTypes\"\r\nimport type { FileAction } from \"../../../../../../types/global\"\r\n\r\ninterface AddToBimProps {\r\n tool: Tool\r\n}\r\n\r\nconst FilePreview: React.FC<{\r\n file: File\r\n mousePosition: { x: number; y: number }\r\n}> = ({ file, mousePosition }) => {\r\n const [imageUrl, setImageUrl] = React.useState<string | null>(null)\r\n const isImage = file.type.startsWith(\"image/\")\r\n\r\n React.useEffect(() => {\r\n if (isImage) {\r\n const url = URL.createObjectURL(file)\r\n setImageUrl(url)\r\n return () => URL.revokeObjectURL(url)\r\n }\r\n }, [file, isImage])\r\n\r\n return (\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className=\"fixed pointer-events-none z-[9999] bg-white/95 flex items-center gap-2 cursor-crosshair text-xs\"\r\n style={{ left: mousePosition.x + 10, top: mousePosition.y - 10 }}\r\n >\r\n {isImage && imageUrl ? (\r\n <Image src={imageUrl} alt={file.name} width={16} height={16} className=\"object-cover rounded\" />\r\n ) : (\r\n <LR.File size={16} className=\"text-black\" />\r\n )}\r\n <span className=\"overflow-hidden text-ellipsis whitespace-nowrap flex-1\">{file.name}</span>\r\n </Button>\r\n )\r\n}\r\n\r\nexport default function AddToBim({ tool }: AddToBimProps) {\r\n const { dispatch: toolsDispatch, state: toolsState } = React.useContext(ToolsContext)\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { bimComponents, world, fragments } = bimState.bim\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n const buildingId = buildingState?.buildings?.building?.id || -1\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { setOpenInfo } = useSidebar()\r\n\r\n const [addingMode, setAddingMode] = React.useState<BimToolbarToolsType>(null)\r\n\r\n // Context menu state for right-click on BIM scene objects\r\n const [contextMenu, setContextMenu] = React.useState<{ x: number; y: number; file: DbFile } | null>(null)\r\n\r\n // Extracted hooks\r\n const { addPendingComment, removePendingComment, commentCount } = useCommentMarkers(world, buildingId)\r\n const { addPendingSensor, removePendingSensor, sensorCount } = useSensorMarkers(world, buildingId)\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const filePlacement = useFilePlacement(\r\n bimComponents, world, fragments, toolsDispatch, buildingId, uploadFile,\r\n )\r\n\r\n // File count for badge: only visible (added to scene) non-BIM files\r\n const filesData = useFilesByBuildingId(buildingId).files || []\r\n const visibleFileCount = filesData.filter(f => {\r\n const ext = f.extension?.toLowerCase()\r\n return ext !== \"ifc\" && ext !== \"frag\" && f.tag !== \"user\" && f.tag !== \"bim-file\" && f.isVisible\r\n }).length\r\n\r\n // Initialize CSS2D renderer\r\n React.useEffect(() => {\r\n if (world) initializeCSS2DRenderer(world)\r\n }, [world])\r\n\r\n // Canvas-level right-click: raycast to detect hit, show context menu\r\n const filesDataRef = React.useRef(filesData)\r\n filesDataRef.current = filesData\r\n\r\n React.useEffect(() => {\r\n if (!world || !fragments) return\r\n const canvas = world.renderer?.three?.domElement\r\n if (!canvas) return\r\n\r\n const handleContextMenu = async (e: MouseEvent) => {\r\n // Prevent the browser default menu immediately, regardless of what we hit\r\n e.preventDefault()\r\n\r\n const mouse = new (await import(\"three\")).Vector2(e.clientX, e.clientY)\r\n const result = await filePlacement.raycast({\r\n camera: world.camera.three,\r\n mouse,\r\n dom: canvas,\r\n })\r\n\r\n // Try to identify which file was hit by matching model names in fragments\r\n const hitModelId = (result as any)?.modelId ?? (result as any)?.fragments?.modelId\r\n const matchedFile = hitModelId\r\n ? filesDataRef.current.find(f => f.name === hitModelId)\r\n : null\r\n\r\n setContextMenu({\r\n x: e.clientX,\r\n y: e.clientY,\r\n file: matchedFile ?? { id: 0, name: \"BIM Object\", url: \"\", type: \"bim-file\" } as DbFile,\r\n })\r\n }\r\n\r\n canvas.addEventListener(\"contextmenu\", handleContextMenu)\r\n return () => canvas.removeEventListener(\"contextmenu\", handleContextMenu)\r\n }, [world, fragments, filePlacement.raycast])\r\n\r\n // Allow other UI to trigger AddToBim modes via currentToolId\r\n const { currentToolId } = toolsState.tools\r\n React.useEffect(() => {\r\n const bimAddModes: BimToolbarToolsType[] = [\r\n \"bim-add-file\", \"bim-add-comment\", \"bim-add-ids\",\r\n \"bim-add-ifc\", \"bim-add-bcf\", \"bim-add-cad\", \"bim-add-sensor\",\r\n ]\r\n if (bimAddModes.includes(currentToolId as BimToolbarToolsType)) {\r\n setAddingMode(currentToolId as BimToolbarToolsType)\r\n }\r\n }, [currentToolId])\r\n\r\n const startAdding = React.useCallback((mode: BimToolbarToolsType) => {\r\n setAddingMode(mode)\r\n toolsDispatch({ type: \"SET-TOOL\", payload: { currentToolId: mode } })\r\n // Crosshair is set by useFilePlacement.handleFileSelect after the user picks a file,\r\n // not here — so the cursor stays normal while the file picker is open.\r\n }, [toolsDispatch])\r\n\r\n const cancelAdding = React.useCallback(() => {\r\n setAddingMode(null)\r\n filePlacement.cancelPlacement()\r\n }, [filePlacement])\r\n\r\n // Sidebar navigation callbacks\r\n const openFileTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"file\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const openCommentsTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"communication\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const openSensorsTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"sensors\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const handleContextMenuAction = React.useCallback((_action: FileAction, _file: DbFile) => {\r\n openFileTab()\r\n }, [openFileTab])\r\n\r\n return (\r\n <>\r\n {/* File preview tooltip following cursor during placement */}\r\n {filePlacement.selectedFile && filePlacement.isPlacingFile && (\r\n <FilePreview file={filePlacement.selectedFile} mousePosition={filePlacement.mousePosition} />\r\n )}\r\n\r\n {/* Toolbar submenu */}\r\n <AddToBimToolbar\r\n tool={tool}\r\n onStartFile={() => startAdding(\"bim-add-file\")}\r\n onStartComment={() => startAdding(\"bim-add-comment\")}\r\n onStartCAD={() => startAdding(\"bim-add-cad\")}\r\n onStartSensor={() => startAdding(\"bim-add-sensor\")}\r\n fileCount={visibleFileCount}\r\n commentCount={commentCount}\r\n sensorCount={sensorCount}\r\n onFileBadgeClick={openFileTab}\r\n onCommentBadgeClick={openCommentsTab}\r\n onSensorBadgeClick={openSensorsTab}\r\n />\r\n\r\n {/* 3D position/scale card for DXF/GLB placement */}\r\n {filePlacement.show3DScaleCard && filePlacement.selectedFile && filePlacement.current3DFileType && (\r\n <Position3DCard\r\n fileType={filePlacement.current3DFileType}\r\n fileName={filePlacement.selectedFile.name}\r\n scale={filePlacement.fileScale}\r\n rotation={filePlacement.fileRotation}\r\n onScaleChange={filePlacement.setFileScale}\r\n onRotationChange={filePlacement.setFileRotation}\r\n onConfirm={() => {\r\n filePlacement.confirmPlacement()\r\n setAddingMode(null)\r\n }}\r\n onCancel={cancelAdding}\r\n />\r\n )}\r\n\r\n {/* File input card */}\r\n {addingMode === \"bim-add-file\" && !filePlacement.show3DScaleCard && (\r\n <FileAdderDialog\r\n isOpen={true}\r\n onClose={cancelAdding}\r\n title=\"Add File\"\r\n icon={LR.FilePlus}\r\n accept=\"*/*\"\r\n onFileSelect={(e) => filePlacement.handleFileSelect(e, addingMode)}\r\n onFileDrop={(file) => filePlacement.handleFileDrop(file, addingMode)}\r\n dropZoneTitle=\"Drag and drop a file\"\r\n dropZoneSubtext=\"or click to browse\"\r\n hide={filePlacement.isPlacingFile && !!filePlacement.selectedFile}\r\n />\r\n )}\r\n\r\n {addingMode === \"bim-add-cad\" && !filePlacement.show3DScaleCard && (\r\n <FileAdderDialog\r\n isOpen={true}\r\n onClose={cancelAdding}\r\n title=\"Add CAD File\"\r\n icon={LR.FileCode2}\r\n accept=\".dxf,.dwg\"\r\n onFileSelect={(e) => filePlacement.handleFileSelect(e, addingMode)}\r\n onFileDrop={(file) => filePlacement.handleFileDrop(file, addingMode)}\r\n dropZoneTitle=\"Drag and drop a CAD file\"\r\n dropZoneSubtext=\"DXF is currently supported\"\r\n hide={filePlacement.isPlacingFile && !!filePlacement.selectedFile}\r\n />\r\n )}\r\n\r\n {/* Comment input */}\r\n <CommentInput\r\n viewer={ViewerNames.bim}\r\n layout=\"dialog\"\r\n isOpen={addingMode === \"bim-add-comment\"}\r\n onCancel={cancelAdding}\r\n bim={{\r\n world,\r\n raycast: filePlacement.raycast,\r\n buildingId,\r\n setCursor: filePlacement.setCursor,\r\n addPendingMarker: addPendingComment,\r\n removePendingMarker: removePendingComment,\r\n }}\r\n />\r\n\r\n {/* Sensor input */}\r\n <SensorInput\r\n viewer={ViewerNames.bim}\r\n layout=\"dialog\"\r\n isOpen={addingMode === \"bim-add-sensor\"}\r\n onCancel={cancelAdding}\r\n bim={{\r\n world,\r\n raycast: filePlacement.raycast,\r\n buildingId,\r\n setCursor: filePlacement.setCursor,\r\n addPendingMarker: addPendingSensor,\r\n removePendingMarker: removePendingSensor,\r\n }}\r\n />\r\n\r\n {/* Right-click context menu on file markers */}\r\n {contextMenu && (\r\n <ViewerContextMenu\r\n x={contextMenu.x}\r\n y={contextMenu.y}\r\n file={contextMenu.file}\r\n options={['view', 'move', 'delete']}\r\n onAction={handleContextMenuAction}\r\n onClose={() => setContextMenu(null)}\r\n />\r\n )}\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/index.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport * as React from \"react\"\r\nimport * as LR from \"lucide-react\"\r\nimport Image from \"next/image\"\r\nimport { useTranslations } from 'next-intl'\r\n\r\nimport { BuildingsContext, MenusContext, ToolsContext } from \"../../../../../../store\"\r\nimport { BimContext } from \"../../../../../../store/BIM/context\"\r\nimport { ViewerNames } from \"../../../../../../types/dbTypes\"\r\nimport type { Tool, ToolbarToolType } from \"../../../../../../types/tools\"\r\nimport type { BimToolbarToolsType } from \"../bimToolbar\"\r\nimport { Button, useSidebar } from \"../../../../../ui\"\r\n\r\nimport { initializeCSS2DRenderer } from \"./src/FileMarkerUtils\"\r\nimport { useCommentMarkers } from \"./src/useCommentMarkers\"\r\nimport { useSensorMarkers } from \"./src/useSensorMarkers\"\r\nimport { useFilePlacement } from \"./src/useFilePlacement\"\r\nimport { AddToBimToolbar } from \"./src/AddToBimToolbar\"\r\nimport Position3DCard from \"./src/Position3DCard\"\r\nimport { FileAdderDialog } from \"../../../../map/src/tools/AddTools/AddFile/FileAdder\"\r\nimport { CommentInput } from \"../../../../../ui/Comments/CommentInput\"\r\nimport { SensorInput } from \"../../../../../ui/Sensors/SensorInput\"\r\nimport { useFilesByBuildingId, useUploadFileToBuilding, useDeleteFile } from \"../../../../../../hooks/files/files\"\r\nimport { ViewerContextMenu } from \"../../../../../ui/FilesManager\"\r\nimport type { FileMarkerAction } from \"../../../../../ui/FilesManager/src/FileMarker\"\r\nimport type { DbFile } from \"../../../../../../types/dbTypes\"\r\nimport type { FileAction } from \"../../../../../../types/global\"\r\n\r\ninterface AddToBimProps {\r\n tool: Tool\r\n}\r\n\r\nconst FilePreview: React.FC<{\r\n file: File\r\n mousePosition: { x: number; y: number }\r\n}> = ({ file, mousePosition }) => {\r\n const [imageUrl, setImageUrl] = React.useState<string | null>(null)\r\n const isImage = file.type.startsWith(\"image/\")\r\n\r\n React.useEffect(() => {\r\n if (isImage) {\r\n const url = URL.createObjectURL(file)\r\n setImageUrl(url)\r\n return () => URL.revokeObjectURL(url)\r\n }\r\n }, [file, isImage])\r\n\r\n return (\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className=\"fixed pointer-events-none z-[9999] bg-white/95 flex items-center gap-2 cursor-crosshair text-xs\"\r\n style={{ left: mousePosition.x + 10, top: mousePosition.y - 10 }}\r\n >\r\n {isImage && imageUrl ? (\r\n <Image src={imageUrl} alt={file.name} width={16} height={16} className=\"object-cover rounded\" />\r\n ) : (\r\n <LR.File size={16} className=\"text-black\" />\r\n )}\r\n <span className=\"overflow-hidden text-ellipsis whitespace-nowrap flex-1\">{file.name}</span>\r\n </Button>\r\n )\r\n}\r\n\r\nexport default function AddToBim({ tool }: AddToBimProps) {\r\n const t = useTranslations('AddToBim')\r\n const { dispatch: toolsDispatch, state: toolsState } = React.useContext(ToolsContext)\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { bimComponents, world, fragments } = bimState.bim\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n const buildingId = buildingState?.buildings?.building?.id || -1\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { setOpenInfo } = useSidebar()\r\n\r\n const [addingMode, setAddingMode] = React.useState<BimToolbarToolsType>(null)\r\n\r\n // Context menu state for right-click on BIM scene objects\r\n const [contextMenu, setContextMenu] = React.useState<{ x: number; y: number; file: DbFile } | null>(null)\r\n\r\n // Extracted hooks\r\n const { addPendingComment, removePendingComment, commentCount } = useCommentMarkers(world, buildingId)\r\n const { addPendingSensor, removePendingSensor, sensorCount } = useSensorMarkers(world, buildingId)\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const { deleteFile } = useDeleteFile(buildingId)\r\n\r\n // Visible (added-to-scene) non-BIM files; also used to resolve a marker to its DB record.\r\n const filesData = useFilesByBuildingId(buildingId).files || []\r\n const filesDataRef = React.useRef(filesData)\r\n filesDataRef.current = filesData\r\n const visibleFileCount = filesData.filter(f => {\r\n const ext = f.extension?.toLowerCase()\r\n return ext !== \"ifc\" && ext !== \"frag\" && f.tag !== \"user\" && f.tag !== \"bim-file\" && f.isVisible\r\n }).length\r\n\r\n // Marker actions reach into the placement API after it is created, so route through a ref.\r\n const placementRef = React.useRef<ReturnType<typeof useFilePlacement> | null>(null)\r\n const handleMarkerAction = React.useCallback((id: string, action: FileMarkerAction) => {\r\n const api = placementRef.current\r\n if (!api) return\r\n if (action === \"delete\") {\r\n const placed = api.getPlacedFile(id)\r\n api.removePlacedFile(id)\r\n const dbFile = placed\r\n ? filesDataRef.current.find(f => f.name === placed.name && (f.id ?? 0) > 0)\r\n : undefined\r\n if (dbFile?.id) deleteFile(dbFile.id).catch(() => { })\r\n return\r\n }\r\n api.editPlacedFile(id, action === \"move\" ? \"translate\" : action)\r\n }, [deleteFile])\r\n\r\n const filePlacement = useFilePlacement(\r\n bimComponents, world, fragments, toolsDispatch, buildingId, uploadFile,\r\n handleMarkerAction,\r\n )\r\n placementRef.current = filePlacement\r\n\r\n // Initialize CSS2D renderer\r\n React.useEffect(() => {\r\n if (world) initializeCSS2DRenderer(world)\r\n }, [world])\r\n\r\n // Canvas-level right-click: raycast to detect hit, show context menu\r\n React.useEffect(() => {\r\n if (!world || !fragments) return\r\n const canvas = world.renderer?.three?.domElement\r\n if (!canvas) return\r\n\r\n const handleContextMenu = async (e: MouseEvent) => {\r\n // Prevent the browser default menu immediately, regardless of what we hit\r\n e.preventDefault()\r\n\r\n const mouse = new (await import(\"three\")).Vector2(e.clientX, e.clientY)\r\n const result = await filePlacement.raycast({\r\n camera: world.camera.three,\r\n mouse,\r\n dom: canvas,\r\n })\r\n\r\n // Try to identify which file was hit by matching model names in fragments\r\n const hitModelId = (result as any)?.modelId ?? (result as any)?.fragments?.modelId\r\n const matchedFile = hitModelId\r\n ? filesDataRef.current.find(f => f.name === hitModelId)\r\n : null\r\n\r\n setContextMenu({\r\n x: e.clientX,\r\n y: e.clientY,\r\n file: matchedFile ?? { id: 0, name: t('bimObject'), url: \"\", type: \"bim-file\" } as DbFile,\r\n })\r\n }\r\n\r\n canvas.addEventListener(\"contextmenu\", handleContextMenu)\r\n return () => canvas.removeEventListener(\"contextmenu\", handleContextMenu)\r\n }, [world, fragments, filePlacement.raycast])\r\n\r\n // Allow other UI to trigger AddToBim modes via currentToolId\r\n const { currentToolId } = toolsState.tools\r\n React.useEffect(() => {\r\n const bimAddModes: BimToolbarToolsType[] = [\r\n \"bim-add-file\", \"bim-add-comment\", \"bim-add-ids\",\r\n \"bim-add-ifc\", \"bim-add-bcf\", \"bim-add-cad\", \"bim-add-sensor\",\r\n ]\r\n if (bimAddModes.includes(currentToolId as BimToolbarToolsType)) {\r\n setAddingMode(currentToolId as BimToolbarToolsType)\r\n }\r\n }, [currentToolId])\r\n\r\n const startAdding = React.useCallback((mode: BimToolbarToolsType) => {\r\n setAddingMode(mode)\r\n toolsDispatch({ type: \"SET-TOOL\", payload: { currentToolId: mode } })\r\n // Crosshair is set by useFilePlacement.handleFileSelect after the user picks a file,\r\n // not here — so the cursor stays normal while the file picker is open.\r\n }, [toolsDispatch])\r\n\r\n const cancelAdding = React.useCallback(() => {\r\n setAddingMode(null)\r\n filePlacement.cancelPlacement()\r\n }, [filePlacement])\r\n\r\n // Sidebar navigation callbacks\r\n const openFileTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"file\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const openCommentsTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"communication\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const openSensorsTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"sensors\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const handleContextMenuAction = React.useCallback((_action: FileAction, _file: DbFile) => {\r\n openFileTab()\r\n }, [openFileTab])\r\n\r\n return (\r\n <>\r\n {/* File preview tooltip following cursor during placement */}\r\n {filePlacement.selectedFile && filePlacement.isPlacingFile && (\r\n <FilePreview file={filePlacement.selectedFile} mousePosition={filePlacement.mousePosition} />\r\n )}\r\n\r\n {/* Toolbar submenu */}\r\n <AddToBimToolbar\r\n tool={tool}\r\n onStartFile={() => startAdding(\"bim-add-file\")}\r\n onStartComment={() => startAdding(\"bim-add-comment\")}\r\n onStartCAD={() => startAdding(\"bim-add-cad\")}\r\n onStartSensor={() => startAdding(\"bim-add-sensor\")}\r\n fileCount={visibleFileCount}\r\n commentCount={commentCount}\r\n sensorCount={sensorCount}\r\n onFileBadgeClick={openFileTab}\r\n onCommentBadgeClick={openCommentsTab}\r\n onSensorBadgeClick={openSensorsTab}\r\n />\r\n\r\n {/* 3D position/scale card for DXF/GLB placement */}\r\n {filePlacement.show3DScaleCard && filePlacement.selectedFile && filePlacement.current3DFileType && (\r\n <Position3DCard\r\n fileType={filePlacement.current3DFileType}\r\n fileName={filePlacement.selectedFile.name}\r\n scale={filePlacement.fileScale}\r\n rotation={filePlacement.fileRotation}\r\n onScaleChange={filePlacement.setFileScale}\r\n onRotationChange={filePlacement.setFileRotation}\r\n onConfirm={() => {\r\n filePlacement.confirmPlacement()\r\n setAddingMode(null)\r\n }}\r\n onCancel={cancelAdding}\r\n />\r\n )}\r\n\r\n {/* File input card */}\r\n {addingMode === \"bim-add-file\" && !filePlacement.show3DScaleCard && (\r\n <FileAdderDialog\r\n isOpen={true}\r\n onClose={cancelAdding}\r\n title={t('addFile')}\r\n icon={LR.FilePlus}\r\n accept=\"*/*\"\r\n onFileSelect={(e) => filePlacement.handleFileSelect(e, addingMode)}\r\n onFileDrop={(file) => filePlacement.handleFileDrop(file, addingMode)}\r\n dropZoneTitle={t('dropZoneTitle')}\r\n dropZoneSubtext={t('dropZoneSubtext')}\r\n hide={filePlacement.isPlacingFile && !!filePlacement.selectedFile}\r\n />\r\n )}\r\n\r\n {addingMode === \"bim-add-cad\" && !filePlacement.show3DScaleCard && (\r\n <FileAdderDialog\r\n isOpen={true}\r\n onClose={cancelAdding}\r\n title={t('addCadFile')}\r\n icon={LR.DraftingCompass}\r\n accept=\".dxf,.dwg\"\r\n onFileSelect={(e) => filePlacement.handleFileSelect(e, addingMode)}\r\n onFileDrop={(file) => filePlacement.handleFileDrop(file, addingMode)}\r\n dropZoneTitle={t('dropZoneTitleCad')}\r\n dropZoneSubtext={t('dropZoneSubtextCad')}\r\n hide={filePlacement.isPlacingFile && !!filePlacement.selectedFile}\r\n />\r\n )}\r\n\r\n {/* Comment input */}\r\n <CommentInput\r\n viewer={ViewerNames.bim}\r\n layout=\"dialog\"\r\n isOpen={addingMode === \"bim-add-comment\"}\r\n onCancel={cancelAdding}\r\n bim={{\r\n world,\r\n raycast: filePlacement.raycast,\r\n buildingId,\r\n setCursor: filePlacement.setCursor,\r\n addPendingMarker: addPendingComment,\r\n removePendingMarker: removePendingComment,\r\n }}\r\n />\r\n\r\n {/* Sensor input */}\r\n <SensorInput\r\n viewer={ViewerNames.bim}\r\n layout=\"dialog\"\r\n isOpen={addingMode === \"bim-add-sensor\"}\r\n onCancel={cancelAdding}\r\n bim={{\r\n world,\r\n raycast: filePlacement.raycast,\r\n buildingId,\r\n setCursor: filePlacement.setCursor,\r\n addPendingMarker: addPendingSensor,\r\n removePendingMarker: removePendingSensor,\r\n }}\r\n />\r\n\r\n {/* Right-click context menu on BIM scene objects */}\r\n {contextMenu && (\r\n <ViewerContextMenu\r\n x={contextMenu.x}\r\n y={contextMenu.y}\r\n file={contextMenu.file}\r\n options={['view', 'move', 'delete']}\r\n onAction={handleContextMenuAction}\r\n onClose={() => setContextMenu(null)}\r\n />\r\n )}\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b"]}
|
|
@@ -9,6 +9,7 @@ export interface DxfInfo {
|
|
|
9
9
|
position: THREE.Vector3;
|
|
10
10
|
scale: number;
|
|
11
11
|
rotation: number;
|
|
12
|
+
fileUrl: string;
|
|
12
13
|
gizmoController?: GizmoController;
|
|
13
14
|
}
|
|
14
15
|
export interface DxfLoadOptions {
|
|
@@ -18,7 +19,6 @@ export interface DxfLoadOptions {
|
|
|
18
19
|
enableGizmo?: boolean;
|
|
19
20
|
}
|
|
20
21
|
export declare class AddDxf {
|
|
21
|
-
private _bimComponents;
|
|
22
22
|
private _world;
|
|
23
23
|
private _dxfManager;
|
|
24
24
|
private _loadedDxfs;
|
|
@@ -30,16 +30,13 @@ export declare class AddDxf {
|
|
|
30
30
|
updateRotation(id: string, rotation: number): boolean;
|
|
31
31
|
updatePosition(id: string, position: THREE.Vector3): boolean;
|
|
32
32
|
toggleGizmo(id: string, enable: boolean): boolean;
|
|
33
|
+
setGizmoMode(id: string, mode: 'translate' | 'rotate' | 'scale'): void;
|
|
33
34
|
confirmPlacement(id: string): boolean;
|
|
34
35
|
removeDxf(id: string): boolean;
|
|
35
36
|
getDxf(id: string): DxfInfo | undefined;
|
|
36
37
|
getAllDxfs(): DxfInfo[];
|
|
37
38
|
private setupGizmo;
|
|
38
39
|
static isDxfFile(file: File): boolean;
|
|
39
|
-
static validateDxfFile(file: File): {
|
|
40
|
-
valid: boolean;
|
|
41
|
-
error?: string;
|
|
42
|
-
};
|
|
43
40
|
dispose(): void;
|
|
44
41
|
}
|
|
45
42
|
//# sourceMappingURL=AddDxf.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddDxf.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAA;AAEnE,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,IAAI,CAAA;IACV,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,eAAe,CAAA;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;
|
|
1
|
+
{"version":3,"file":"AddDxf.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAA;AAEnE,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,IAAI,CAAA;IACV,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,eAAe,CAAA;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAID,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,WAAW,CAAkC;IAErD,WAAW,qBAA2B;IACtC,gBAAgB,qBAA2B;gBAE/B,aAAa,EAAE,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK;IAKrD,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IA4CvF,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAS/C,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IASrD,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO;IAS5D,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO;IAgBjD,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI;IAItE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQrC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAU9B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIvC,UAAU,IAAI,OAAO,EAAE;IAIvB,OAAO,CAAC,UAAU;IAsBlB,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAIrC,OAAO,IAAI,IAAI;CAQhB"}
|