From fc33bd0d594a1ac8de79b11080ead48630a8ca27 Mon Sep 17 00:00:00 2001 From: Alireza Date: Fri, 4 Apr 2025 12:11:29 -0400 Subject: [PATCH] feat(image capture): Enhance SEG and RTSS Support with Dependency Updates and Bug Fixes (#4924) --- bun.lock | 276 ++++----- .../cornerstone-dicom-pmap/package.json | 4 +- extensions/cornerstone-dicom-seg/package.json | 4 +- .../src/commandsModule.ts | 3 - .../viewports/OHIFCornerstoneSEGViewport.tsx | 4 +- extensions/cornerstone-dicom-sr/package.json | 6 +- .../cornerstone-dynamic-volume/package.json | 4 +- extensions/cornerstone/package.json | 14 +- extensions/cornerstone/src/commandsModule.ts | 27 +- .../DicomUpload/DicomUploadProgress.tsx | 2 +- .../DicomUpload/DicomUploadProgressItem.tsx | 2 +- extensions/cornerstone/src/index.tsx | 13 +- extensions/cornerstone/src/init.tsx | 20 +- .../src/panels/PanelSegmentation.tsx | 8 +- .../SegmentationService.ts | 11 +- .../CornerstoneViewportService.ts | 9 +- .../utils/CornerstoneViewportDownloadForm.tsx | 57 +- .../cornerstone/src/utils/hydrationUtils.ts | 31 + .../src/utils/segmentationHandlers.ts | 17 +- .../DataSourceConfigurationModalComponent.tsx | 2 +- .../StudyBrowser/PanelStudyBrowserHeader.tsx | 2 +- .../HeaderPatientInfo/HeaderPatientInfo.tsx | 8 +- .../default/src/ViewerLayout/ViewerHeader.tsx | 2 +- extensions/default/src/utils/Toolbox.tsx | 2 +- extensions/measurement-tracking/package.json | 4 +- extensions/tmtv/src/Panels/PanelPetSUV.tsx | 24 +- .../PanelROIThresholdExport.tsx | 7 +- extensions/tmtv/src/Panels/PanelTMTV.tsx | 59 +- extensions/tmtv/src/commandsModule.ts | 43 +- extensions/tmtv/src/getPanelModule.tsx | 19 +- modes/longitudinal/src/toolbarButtons.ts | 12 +- platform/app/package.json | 2 +- platform/app/src/routes/Debug.tsx | 2 +- platform/app/src/routes/WorkList/WorkList.tsx | 2 +- platform/core/package.json | 2 +- .../patterns/patterns-segmentation-raw.tsx | 503 +++++++++++++++ .../pages/patterns/patterns-segmentation.tsx | 572 ++---------------- platform/ui-next/assets/data/index.ts | 180 +++++- .../ui-next/src/components/Header/Header.tsx | 4 +- .../SegmentationTable/AddSegmentationRow.tsx | 2 +- .../SegmentationExpanded.tsx | 13 +- .../SegmentationSegments.tsx | 64 +- .../SegmentationTable/SegmentationTable.tsx | 2 +- .../SegmentationTableConfig.tsx | 6 +- .../src/components/SidePanel/SidePanel.tsx | 16 +- .../src/components/Thumbnail/Thumbnail.tsx | 4 +- .../ui-next/src/utils/getToggledClassName.tsx | 2 +- tests/SEGHydrationFromMPR.spec.ts | 42 ++ ...PR.spec.ts => SEGHydrationThenMPR.spec.ts} | 4 +- .../mprAfterSEG.png | Bin 0 -> 253460 bytes .../mprAfterSegHydrated.png | Bin 0 -> 245122 bytes .../mprAfterSegHydratedAfterLayoutChange.png | Bin 0 -> 291868 bytes .../mprBeforeSEG.png | Bin 0 -> 218217 bytes .../segPostHydration.png | Bin .../segPostHydrationMPRAxialPrimary.png | Bin tests/utils/screenShotPaths.ts | 8 +- yarn.lock | 56 +- 57 files changed, 1247 insertions(+), 935 deletions(-) create mode 100644 extensions/cornerstone/src/utils/hydrationUtils.ts create mode 100644 platform/docs/src/pages/patterns/patterns-segmentation-raw.tsx create mode 100644 tests/SEGHydrationFromMPR.spec.ts rename tests/{SEGHydrationMPR.spec.ts => SEGHydrationThenMPR.spec.ts} (88%) create mode 100644 tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSEG.png create mode 100644 tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydrated.png create mode 100644 tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydratedAfterLayoutChange.png create mode 100644 tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprBeforeSEG.png rename tests/screenshots/chromium/{SEGHydrationMPR.spec.ts => SEGHydrationThenMPR.spec.ts}/segPostHydration.png (100%) rename tests/screenshots/chromium/{SEGHydrationMPR.spec.ts => SEGHydrationThenMPR.spec.ts}/segPostHydrationMPRAxialPrimary.png (100%) diff --git a/bun.lock b/bun.lock index a70f55f9c..7739c1f2d 100644 --- a/bun.lock +++ b/bun.lock @@ -33,7 +33,7 @@ }, "addOns/externals/devDependencies": { "name": "@externals/devDependencies", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "@kitware/vtk.js": "32.12.0", @@ -121,22 +121,22 @@ }, "addOns/externals/dicom-microscopy-viewer": { "name": "@externals/dicom-microscopy-viewer", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "dicom-microscopy-viewer": "^0.46.1", }, }, "extensions/cornerstone": { "name": "@ohif/extension-cornerstone", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^3.8.4", - "@cornerstonejs/ai": "^3.8.4", - "@cornerstonejs/core": "^3.8.4", - "@cornerstonejs/labelmap-interpolation": "^3.8.4", - "@cornerstonejs/polymorphic-segmentation": "^3.8.4", - "@cornerstonejs/tools": "^3.8.4", + "@cornerstonejs/adapters": "^3.8.5", + "@cornerstonejs/ai": "^3.8.5", + "@cornerstonejs/core": "^3.8.5", + "@cornerstonejs/labelmap-interpolation": "^3.8.5", + "@cornerstonejs/polymorphic-segmentation": "^3.8.5", + "@cornerstonejs/tools": "^3.8.5", "@itk-wasm/morphological-contour-interpolation": "1.1.0", "@kitware/vtk.js": "32.12.0", "html2canvas": "^1.4.1", @@ -153,9 +153,9 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.4", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^3.8.4", - "@ohif/core": "3.10.0-beta.144", - "@ohif/ui": "3.10.0-beta.144", + "@cornerstonejs/dicom-image-loader": "^3.8.5", + "@ohif/core": "3.10.0-beta.147", + "@ohif/ui": "3.10.0-beta.147", "dcmjs": "*", "dicom-parser": "^1.8.21", "hammerjs": "^2.0.8", @@ -167,19 +167,19 @@ }, "extensions/cornerstone-dicom-pmap": { "name": "@ohif/extension-cornerstone-dicom-pmap", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^3.8.4", - "@cornerstonejs/core": "^3.8.4", + "@cornerstonejs/adapters": "^3.8.5", + "@cornerstonejs/core": "^3.8.5", "@kitware/vtk.js": "32.12.0", "react-color": "^2.19.3", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/i18n": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/i18n": "3.10.0-beta.147", "prop-types": "^15.6.2", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -190,16 +190,16 @@ }, "extensions/cornerstone-dicom-rt": { "name": "@ohif/extension-cornerstone-dicom-rt", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "react-color": "^2.19.3", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/i18n": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/i18n": "3.10.0-beta.147", "prop-types": "^15.6.2", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -210,19 +210,19 @@ }, "extensions/cornerstone-dicom-seg": { "name": "@ohif/extension-cornerstone-dicom-seg", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^3.8.4", - "@cornerstonejs/core": "^3.8.4", + "@cornerstonejs/adapters": "^3.8.5", + "@cornerstonejs/core": "^3.8.5", "@kitware/vtk.js": "32.12.0", "react-color": "^2.19.3", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/i18n": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/i18n": "3.10.0-beta.147", "prop-types": "^15.6.2", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -233,19 +233,19 @@ }, "extensions/cornerstone-dicom-sr": { "name": "@ohif/extension-cornerstone-dicom-sr", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^3.8.4", - "@cornerstonejs/core": "^3.8.4", - "@cornerstonejs/tools": "^3.8.4", + "@cornerstonejs/adapters": "^3.8.5", + "@cornerstonejs/core": "^3.8.5", + "@cornerstonejs/tools": "^3.8.5", "classnames": "^2.3.2", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone": "3.10.0-beta.144", - "@ohif/extension-measurement-tracking": "3.10.0-beta.144", - "@ohif/ui": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone": "3.10.0-beta.147", + "@ohif/extension-measurement-tracking": "3.10.0-beta.147", + "@ohif/ui": "3.10.0-beta.147", "dcmjs": "*", "dicom-parser": "^1.8.9", "hammerjs": "^2.0.8", @@ -255,19 +255,19 @@ }, "extensions/cornerstone-dynamic-volume": { "name": "@ohif/extension-cornerstone-dynamic-volume", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/core": "^3.8.4", - "@cornerstonejs/tools": "^3.8.4", + "@cornerstonejs/core": "^3.8.5", + "@cornerstonejs/tools": "^3.8.5", "classnames": "^2.3.2", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/i18n": "3.10.0-beta.144", - "@ohif/ui": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/i18n": "3.10.0-beta.147", + "@ohif/ui": "3.10.0-beta.147", "dcmjs": "*", "dicom-parser": "^1.8.21", "hammerjs": "^2.0.8", @@ -277,7 +277,7 @@ }, "extensions/default": { "name": "@ohif/extension-default", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "@cornerstonejs/calculate-suv": "^1.1.0", @@ -285,8 +285,8 @@ "lodash.uniqby": "^4.7.0", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/i18n": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/i18n": "3.10.0-beta.147", "dcmjs": "*", "dicomweb-client": "^0.10.4", "prop-types": "^15.6.2", @@ -300,7 +300,7 @@ }, "extensions/dicom-microscopy": { "name": "@ohif/extension-dicom-microscopy", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "@cornerstonejs/codec-charls": "^1.2.3", @@ -311,10 +311,10 @@ "mathjs": "^12.4.2", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/i18n": "3.10.0-beta.144", - "@ohif/ui": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/i18n": "3.10.0-beta.147", + "@ohif/ui": "3.10.0-beta.147", "prop-types": "^15.6.2", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -325,14 +325,14 @@ }, "extensions/dicom-pdf": { "name": "@ohif/extension-dicom-pdf", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "classnames": "^2.3.2", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/ui": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/ui": "3.10.0-beta.147", "dcmjs": "*", "dicom-parser": "^1.8.9", "hammerjs": "^2.0.8", @@ -342,14 +342,14 @@ }, "extensions/dicom-video": { "name": "@ohif/extension-dicom-video", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "classnames": "^2.3.2", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/ui": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/ui": "3.10.0-beta.147", "dcmjs": "*", "dicom-parser": "^1.8.9", "hammerjs": "^2.0.8", @@ -359,20 +359,20 @@ }, "extensions/measurement-tracking": { "name": "@ohif/extension-measurement-tracking", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", - "@ohif/ui": "3.10.0-beta.144", + "@ohif/ui": "3.10.0-beta.147", "@xstate/react": "^3.2.2", "xstate": "^4.10.0", }, "peerDependencies": { - "@cornerstonejs/core": "^3.8.4", - "@cornerstonejs/tools": "^3.8.4", - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/ui": "3.10.0-beta.144", + "@cornerstonejs/core": "^3.8.5", + "@cornerstonejs/tools": "^3.8.5", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/ui": "3.10.0-beta.147", "classnames": "^2.3.2", "dcmjs": "*", "lodash.debounce": "^4.0.8", @@ -385,14 +385,14 @@ }, "extensions/test-extension": { "name": "@ohif/extension-test", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "classnames": "^2.3.2", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/ui": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/ui": "3.10.0-beta.147", "dcmjs": "0.38.0", "dicom-parser": "^1.8.9", "hammerjs": "^2.0.8", @@ -402,7 +402,7 @@ }, "extensions/tmtv": { "name": "@ohif/extension-tmtv", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "classnames": "^2.3.2", @@ -419,7 +419,7 @@ }, "modes/basic-dev-mode": { "name": "@ohif/mode-basic-dev-mode", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "i18next": "^17.0.3", @@ -429,17 +429,17 @@ "webpack-merge": "^5.7.3", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone": "3.10.0-beta.144", - "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/extension-dicom-pdf": "3.10.0-beta.144", - "@ohif/extension-dicom-video": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone": "3.10.0-beta.147", + "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/extension-dicom-pdf": "3.10.0-beta.147", + "@ohif/extension-dicom-video": "3.10.0-beta.147", }, }, "modes/basic-test-mode": { "name": "@ohif/mode-test", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "i18next": "^17.0.3", @@ -449,19 +449,19 @@ "webpack-merge": "^5.7.3", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone": "3.10.0-beta.144", - "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/extension-dicom-pdf": "3.10.0-beta.144", - "@ohif/extension-dicom-video": "3.10.0-beta.144", - "@ohif/extension-measurement-tracking": "3.10.0-beta.144", - "@ohif/extension-test": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone": "3.10.0-beta.147", + "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/extension-dicom-pdf": "3.10.0-beta.147", + "@ohif/extension-dicom-video": "3.10.0-beta.147", + "@ohif/extension-measurement-tracking": "3.10.0-beta.147", + "@ohif/extension-test": "3.10.0-beta.147", }, }, "modes/longitudinal": { "name": "@ohif/mode-longitudinal", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "i18next": "^17.0.3", @@ -471,32 +471,32 @@ "webpack-merge": "^5.7.3", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone": "3.10.0-beta.144", - "@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.144", - "@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.144", - "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/extension-dicom-pdf": "3.10.0-beta.144", - "@ohif/extension-dicom-video": "3.10.0-beta.144", - "@ohif/extension-measurement-tracking": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone": "3.10.0-beta.147", + "@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.147", + "@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.147", + "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/extension-dicom-pdf": "3.10.0-beta.147", + "@ohif/extension-dicom-video": "3.10.0-beta.147", + "@ohif/extension-measurement-tracking": "3.10.0-beta.147", }, }, "modes/microscopy": { "name": "@ohif/mode-microscopy", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "i18next": "^17.0.3", }, "peerDependencies": { - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-dicom-microscopy": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-dicom-microscopy": "3.10.0-beta.147", }, }, "modes/preclinical-4d": { "name": "@ohif/mode-preclinical-4d", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", }, @@ -515,7 +515,7 @@ }, "modes/segmentation": { "name": "@ohif/mode-segmentation", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "i18next": "^17.0.3", @@ -559,7 +559,7 @@ }, "modes/tmtv": { "name": "@ohif/mode-tmtv", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "i18next": "^17.0.3", @@ -580,32 +580,32 @@ }, "platform/app": { "name": "@ohif/app", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.4", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^3.8.4", + "@cornerstonejs/dicom-image-loader": "^3.8.5", "@emotion/serialize": "^1.1.3", - "@ohif/core": "3.10.0-beta.144", - "@ohif/extension-cornerstone": "3.10.0-beta.144", - "@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.144", - "@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.144", - "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144", - "@ohif/extension-default": "3.10.0-beta.144", - "@ohif/extension-dicom-microscopy": "3.10.0-beta.144", - "@ohif/extension-dicom-pdf": "3.10.0-beta.144", - "@ohif/extension-dicom-video": "3.10.0-beta.144", - "@ohif/extension-test": "3.10.0-beta.144", - "@ohif/i18n": "3.10.0-beta.144", - "@ohif/mode-basic-dev-mode": "3.10.0-beta.144", - "@ohif/mode-longitudinal": "3.10.0-beta.144", - "@ohif/mode-microscopy": "3.10.0-beta.144", - "@ohif/mode-test": "3.10.0-beta.144", - "@ohif/ui": "3.10.0-beta.144", - "@ohif/ui-next": "3.10.0-beta.144", + "@ohif/core": "3.10.0-beta.147", + "@ohif/extension-cornerstone": "3.10.0-beta.147", + "@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.147", + "@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.147", + "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147", + "@ohif/extension-default": "3.10.0-beta.147", + "@ohif/extension-dicom-microscopy": "3.10.0-beta.147", + "@ohif/extension-dicom-pdf": "3.10.0-beta.147", + "@ohif/extension-dicom-video": "3.10.0-beta.147", + "@ohif/extension-test": "3.10.0-beta.147", + "@ohif/i18n": "3.10.0-beta.147", + "@ohif/mode-basic-dev-mode": "3.10.0-beta.147", + "@ohif/mode-longitudinal": "3.10.0-beta.147", + "@ohif/mode-microscopy": "3.10.0-beta.147", + "@ohif/mode-test": "3.10.0-beta.147", + "@ohif/ui": "3.10.0-beta.147", + "@ohif/ui-next": "3.10.0-beta.147", "@svgr/webpack": "^8.1.0", "@types/react": "^18.3.3", "classnames": "^2.3.2", @@ -646,7 +646,7 @@ }, "platform/cli": { "name": "@ohif/cli", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "bin": { "ohif-cli": "src/index.js", }, @@ -670,7 +670,7 @@ }, "platform/core": { "name": "@ohif/core", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "dcmjs": "*", @@ -695,15 +695,15 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.4", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^3.8.4", - "@ohif/ui": "3.10.0-beta.144", + "@cornerstonejs/dicom-image-loader": "^3.8.5", + "@ohif/ui": "3.10.0-beta.147", "cornerstone-math": "0.1.9", "dicom-parser": "^1.8.21", }, }, "platform/i18n": { "name": "@ohif/i18n", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@babel/runtime": "^7.20.13", "i18next-locize-backend": "^2.0.0", @@ -728,7 +728,7 @@ }, "platform/ui": { "name": "@ohif/ui", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@testing-library/react": "^13.1.0", "browser-detect": "^0.2.28", @@ -788,7 +788,7 @@ }, "platform/ui-next": { "name": "@ohif/ui-next", - "version": "3.10.0-beta.144", + "version": "3.10.0-beta.147", "dependencies": { "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-checkbox": "^1.1.1", @@ -1099,9 +1099,9 @@ "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], - "@cornerstonejs/adapters": ["@cornerstonejs/adapters@3.8.4", "", { "dependencies": { "@babel/runtime-corejs2": "^7.17.8", "buffer": "^6.0.3", "dcmjs": "^0.29.8", "gl-matrix": "^3.4.3", "ndarray": "^1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.4", "@cornerstonejs/tools": "^3.8.4" } }, "sha512-TFTvNZQBW54rEcyjDfyHZYYvIPHVGZoUCtjW3isHfpoXkjHz2cliwC55e0kcNJ0zbyI6H41c4VsYH8h2MTYOdA=="], + "@cornerstonejs/adapters": ["@cornerstonejs/adapters@3.8.5", "", { "dependencies": { "@babel/runtime-corejs2": "^7.17.8", "buffer": "^6.0.3", "dcmjs": "^0.29.8", "gl-matrix": "^3.4.3", "ndarray": "^1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.5", "@cornerstonejs/tools": "^3.8.5" } }, "sha512-dQlKKSCbV6V3QpivDdZZiI7N792l1VqsOj8H+e8iiarpy6MvwiEvck2u50/GBhto9D/U9QvRkZVs9TH0ewUs5w=="], - "@cornerstonejs/ai": ["@cornerstonejs/ai@3.8.4", "", { "dependencies": { "@babel/runtime-corejs2": "^7.17.8", "buffer": "^6.0.3", "dcmjs": "^0.29.8", "gl-matrix": "^3.4.3", "lodash.clonedeep": "^4.5.0", "ndarray": "^1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.4", "@cornerstonejs/tools": "^3.8.4" } }, "sha512-scc2srdL1BqNTSFEro4y7UlNBsPtBVSF31eT4SC1psxZQcr/iyaCKuOQg/hglk1yobFkdIKvjRAvvqvYmGduQw=="], + "@cornerstonejs/ai": ["@cornerstonejs/ai@3.8.5", "", { "dependencies": { "@babel/runtime-corejs2": "^7.17.8", "buffer": "^6.0.3", "dcmjs": "^0.29.8", "gl-matrix": "^3.4.3", "lodash.clonedeep": "^4.5.0", "ndarray": "^1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.5", "@cornerstonejs/tools": "^3.8.5" } }, "sha512-9QR7PmTtkeXZOjDFh9WulLJGnraWLaM/g/qNB0ZaPKDMo1PLT7/tHn1F6wsyT2cmpEb3/G5towdxtEvhvlpm/w=="], "@cornerstonejs/calculate-suv": ["@cornerstonejs/calculate-suv@1.1.0", "", {}, "sha512-Q9XraiDJif9aMFArD2iEuDO/HXbcRVCqB7KfaHgDrdTTjgDFovS91Psbdim7crypRSvE6dh/+HKeFNHdvNkA6w=="], @@ -1113,15 +1113,15 @@ "@cornerstonejs/codec-openjph": ["@cornerstonejs/codec-openjph@2.4.7", "", {}, "sha512-qvP4q4JDib7mi9r7LqKOwqz7YZ8gjtDX4ZCezeYf8+eb7MBXCz5uXAMeVF3yz9Axw4XiIMdB/pqXkm8tqCl13w=="], - "@cornerstonejs/core": ["@cornerstonejs/core@3.8.4", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "^4.4.1", "gl-matrix": "^3.4.3", "loglevel": "^1.9.2" } }, "sha512-8spQvERZRn4I/0EhzTNZ5ruPBrVn5F4msb/q0ttBtiVEa9JaVwoWh3yEcO4q5NpK7t1BhpfSPMKwU98Dy02ugQ=="], + "@cornerstonejs/core": ["@cornerstonejs/core@3.8.5", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "^4.4.1", "gl-matrix": "^3.4.3", "loglevel": "^1.9.2" } }, "sha512-x3LZwZUqhVldChpwtLqNwUWx8eXq9q1ndwrx5lVpnX4ZUT9qQIAro01Ih8DPiXlLb1NXbN3sG4e/x/TxuQ+5bw=="], - "@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@3.8.4", "", { "dependencies": { "@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.5", "comlink": "^4.4.1", "dicom-parser": "^1.8.9", "jpeg-lossless-decoder-js": "^2.1.0", "pako": "^2.0.4", "uuid": "^9.0.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.4" } }, "sha512-N2D4U8JDhen+dCY1HrLOoK8W3CD2AAf/Cd+GiJMEGjtrihIuQ+Ha+hZTVQzaDMc7v2Ruvx2DpJ7i9HUJXoyWHw=="], + "@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@3.8.5", "", { "dependencies": { "@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.5", "comlink": "^4.4.1", "dicom-parser": "^1.8.9", "jpeg-lossless-decoder-js": "^2.1.0", "pako": "^2.0.4", "uuid": "^9.0.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.5" } }, "sha512-yuLLU2sbtFWueNKVbeYBddPoJnfhrUQ+rRnXJb+y9iczwZMQLy4tYyk3Q5INo+/nIdwCVeZg7P9qORZERsBivw=="], - "@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@3.8.4", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.4", "@cornerstonejs/tools": "^3.8.4", "@kitware/vtk.js": "32.12.1" } }, "sha512-KKDTeVJg1s0trplkaHglBm0foQ3XL9BhsTfatFCTzgIOCT0wolrKbr+xAKIeZuBQifiFazDWpYaeBHRbkWEEMA=="], + "@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@3.8.5", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.5", "@cornerstonejs/tools": "^3.8.5", "@kitware/vtk.js": "32.12.1" } }, "sha512-u80OTJE8eju+tZ+S/N1b/HTIw2mPjEE2o5u3T/7/w90FX7OaxJJiMYjPPfnSZWXjE2HhRu0gEAKHOU3dr8pL4w=="], - "@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@3.8.4", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.4", "@cornerstonejs/tools": "^3.8.4", "@kitware/vtk.js": "32.12.1" } }, "sha512-+3o280NOniUkG9GcWTeTi6x3qy8Rm7WpYFycWpxXIufJzVudJi0zAR+EIWDq/uvTQ3DH1SrBDe4wzYNqD+yirw=="], + "@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@3.8.5", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.5", "@cornerstonejs/tools": "^3.8.5", "@kitware/vtk.js": "32.12.1" } }, "sha512-6L9a9f36CnUIuGsydA8kkkC0z1Gjid4KfclCQoYYaxcsLWLDpba5usFERmxCLIm9VyBsA3POqesIGDqWTgJmFg=="], - "@cornerstonejs/tools": ["@cornerstonejs/tools@3.8.4", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "^4.4.1", "lodash.get": "^4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.4", "@kitware/vtk.js": "32.12.1", "@types/d3-array": "^3.0.4", "@types/d3-interpolate": "^3.0.1", "d3-array": "^3.2.3", "d3-interpolate": "^3.0.1", "gl-matrix": "^3.4.3" } }, "sha512-yr8RLtnxGh9BsPOFpX+zwT++UGJDqWMo7noWGzxkuISDriLr+HGZI8gL910Mw62obaF8r9qpeOSvSOzEHN3nSw=="], + "@cornerstonejs/tools": ["@cornerstonejs/tools@3.8.5", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "^4.4.1", "lodash.get": "^4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "^3.8.5", "@kitware/vtk.js": "32.12.1", "@types/d3-array": "^3.0.4", "@types/d3-interpolate": "^3.0.1", "d3-array": "^3.2.3", "d3-interpolate": "^3.0.1", "gl-matrix": "^3.4.3" } }, "sha512-aTaziRHJakCCxvbHhbrSkEuiamURGakcK83KXGJPjSdStWC/2cVpkCzPxc8V7FZBIstsvo5pgciXSpNc6Yt7Og=="], "@csstools/postcss-cascade-layers": ["@csstools/postcss-cascade-layers@1.1.1", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.2", "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.2" } }, "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA=="], diff --git a/extensions/cornerstone-dicom-pmap/package.json b/extensions/cornerstone-dicom-pmap/package.json index 79a987a15..93f11a18e 100644 --- a/extensions/cornerstone-dicom-pmap/package.json +++ b/extensions/cornerstone-dicom-pmap/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^3.8.4", - "@cornerstonejs/core": "^3.8.4", + "@cornerstonejs/adapters": "^3.8.5", + "@cornerstonejs/core": "^3.8.5", "@kitware/vtk.js": "32.12.0", "react-color": "^2.19.3" } diff --git a/extensions/cornerstone-dicom-seg/package.json b/extensions/cornerstone-dicom-seg/package.json index 7f6ded26d..0a46446ea 100644 --- a/extensions/cornerstone-dicom-seg/package.json +++ b/extensions/cornerstone-dicom-seg/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^3.8.4", - "@cornerstonejs/core": "^3.8.4", + "@cornerstonejs/adapters": "^3.8.5", + "@cornerstonejs/core": "^3.8.5", "@kitware/vtk.js": "32.12.0", "react-color": "^2.19.3" } diff --git a/extensions/cornerstone-dicom-seg/src/commandsModule.ts b/extensions/cornerstone-dicom-seg/src/commandsModule.ts index 562ca7335..36e5cf045 100644 --- a/extensions/cornerstone-dicom-seg/src/commandsModule.ts +++ b/extensions/cornerstone-dicom-seg/src/commandsModule.ts @@ -305,9 +305,6 @@ const commandsModule = ({ }; const definitions = { - loadSegmentationDisplaySetsForViewport: { - commandFn: actions.loadSegmentationDisplaySetsForViewport, - }, loadSegmentationsForViewport: { commandFn: actions.loadSegmentationsForViewport, }, diff --git a/extensions/cornerstone-dicom-seg/src/viewports/OHIFCornerstoneSEGViewport.tsx b/extensions/cornerstone-dicom-seg/src/viewports/OHIFCornerstoneSEGViewport.tsx index 2cf4d0c0c..cebcf10df 100644 --- a/extensions/cornerstone-dicom-seg/src/viewports/OHIFCornerstoneSEGViewport.tsx +++ b/extensions/cornerstone-dicom-seg/src/viewports/OHIFCornerstoneSEGViewport.tsx @@ -47,7 +47,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) { const [viewportGrid, viewportGridService] = useViewportGrid(); // States - let selectedSegmentObjectIndex: number = 0; + const selectedSegmentObjectIndex: number = 0; const { setPositionPresentation } = usePositionPresentationStore(); // Hydration means that the SEG is opened and segments are loaded into the @@ -160,7 +160,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) { displaySetInstanceUID: referencedDisplaySet.displaySetInstanceUID, }); - viewportGridService.setDisplaySetsForViewport({ + commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', { viewportId, displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID], }); diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json index b0fdc2456..2c59e150b 100644 --- a/extensions/cornerstone-dicom-sr/package.json +++ b/extensions/cornerstone-dicom-sr/package.json @@ -46,9 +46,9 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^3.8.4", - "@cornerstonejs/core": "^3.8.4", - "@cornerstonejs/tools": "^3.8.4", + "@cornerstonejs/adapters": "^3.8.5", + "@cornerstonejs/core": "^3.8.5", + "@cornerstonejs/tools": "^3.8.5", "classnames": "^2.3.2" } } diff --git a/extensions/cornerstone-dynamic-volume/package.json b/extensions/cornerstone-dynamic-volume/package.json index d04e8531f..6e7423b72 100644 --- a/extensions/cornerstone-dynamic-volume/package.json +++ b/extensions/cornerstone-dynamic-volume/package.json @@ -42,8 +42,8 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/core": "^3.8.4", - "@cornerstonejs/tools": "^3.8.4", + "@cornerstonejs/core": "^3.8.5", + "@cornerstonejs/tools": "^3.8.5", "classnames": "^2.3.2" } } diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index 51390e72e..6916b616a 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -38,7 +38,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.4", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^3.8.4", + "@cornerstonejs/dicom-image-loader": "^3.8.5", "@ohif/core": "3.10.0-beta.148", "@ohif/ui": "3.10.0-beta.148", "dcmjs": "*", @@ -51,12 +51,12 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^3.8.4", - "@cornerstonejs/ai": "^3.8.4", - "@cornerstonejs/core": "^3.8.4", - "@cornerstonejs/labelmap-interpolation": "^3.8.4", - "@cornerstonejs/polymorphic-segmentation": "^3.8.4", - "@cornerstonejs/tools": "^3.8.4", + "@cornerstonejs/adapters": "^3.8.5", + "@cornerstonejs/ai": "^3.8.5", + "@cornerstonejs/core": "^3.8.5", + "@cornerstonejs/labelmap-interpolation": "^3.8.5", + "@cornerstonejs/polymorphic-segmentation": "^3.8.5", + "@cornerstonejs/tools": "^3.8.5", "@itk-wasm/morphological-contour-interpolation": "1.1.0", "@kitware/vtk.js": "32.12.0", "html2canvas": "^1.4.1", diff --git a/extensions/cornerstone/src/commandsModule.ts b/extensions/cornerstone/src/commandsModule.ts index 20fdc8d9f..b3bff6094 100644 --- a/extensions/cornerstone/src/commandsModule.ts +++ b/extensions/cornerstone/src/commandsModule.ts @@ -35,6 +35,7 @@ import { toolNames } from './initCornerstoneTools'; import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm'; import { updateSegmentBidirectionalStats } from './utils/updateSegmentationStats'; import { generateSegmentationCSVReport } from './utils/generateSegmentationCSVReport'; +import { getUpdatedViewportsForSegmentation } from './utils/hydrationUtils'; const { DefaultHistoryMemo } = csUtils.HistoryMemo; const toggleSyncFunctions = { @@ -495,7 +496,6 @@ function commandsModule({ downloadCSVSegmentationReport: ({ segmentationId }) => { const segmentation = segmentationService.getSegmentation(segmentationId); - const cachedStats = segmentation.cachedStats; const { representationData } = segmentation; const { Labelmap } = representationData; @@ -1021,6 +1021,10 @@ function commandsModule({ const toolGroup = toolGroupService.getToolGroupForViewport(viewportId); + if (!toolGroup.hasTool(toolName)) { + return; + } + const prevConfig = toolGroup?.getToolConfiguration(toolName); toolGroup?.setToolConfiguration( toolName, @@ -1539,7 +1543,8 @@ function commandsModule({ DefaultHistoryMemo.redo(); }, toggleSegmentPreviewEdit: ({ toggle }) => { - const labelmapTools = getLabelmapTools({ toolGroupService }); + let labelmapTools = getLabelmapTools({ toolGroupService }); + labelmapTools = labelmapTools.filter(tool => !tool.toolName.includes('Eraser')); labelmapTools.forEach(tool => { tool.configuration = { ...tool.configuration, @@ -1562,7 +1567,8 @@ function commandsModule({ }); }, toggleUseCenterSegmentIndex: ({ toggle }) => { - const labelmapTools = getLabelmapTools({ toolGroupService }); + let labelmapTools = getLabelmapTools({ toolGroupService }); + labelmapTools = labelmapTools.filter(tool => !tool.toolName.includes('Eraser')); labelmapTools.forEach(tool => { tool.configuration = { ...tool.configuration, @@ -1707,6 +1713,20 @@ function commandsModule({ const activeSegmentation = segmentationService.getActiveSegmentation(activeViewportId); segmentationService.addSegment(activeSegmentation.segmentationId); }, + loadSegmentationDisplaySetsForViewport: ({ viewportId, displaySetInstanceUIDs }) => { + const updatedViewports = getUpdatedViewportsForSegmentation({ + viewportId, + servicesManager, + displaySetInstanceUIDs, + }); + + updatedViewports.forEach(viewport => { + viewportGridService.setDisplaySetsForViewport({ + viewportId: viewport.viewportId, + displaySetInstanceUIDs: viewport.displaySetInstanceUIDs, + }); + }); + }, }; const definitions = { @@ -1985,6 +2005,7 @@ function commandsModule({ increaseBrushSize: actions.increaseBrushSize, decreaseBrushSize: actions.decreaseBrushSize, addNewSegment: actions.addNewSegment, + loadSegmentationDisplaySetsForViewport: actions.loadSegmentationDisplaySetsForViewport, }; return { diff --git a/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgress.tsx b/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgress.tsx index 7f58172f9..1b7fe89b8 100644 --- a/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgress.tsx +++ b/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgress.tsx @@ -303,7 +303,7 @@ function DicomUploadProgress({ diff --git a/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgressItem.tsx b/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgressItem.tsx index d1673d4a0..70b16ecdc 100644 --- a/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgressItem.tsx +++ b/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgressItem.tsx @@ -89,7 +89,7 @@ const DicomUploadProgressItem = memo( )}
diff --git a/extensions/cornerstone/src/index.tsx b/extensions/cornerstone/src/index.tsx index ee55b7234..fb29254af 100644 --- a/extensions/cornerstone/src/index.tsx +++ b/extensions/cornerstone/src/index.tsx @@ -82,6 +82,7 @@ const stackRetrieveOptions = { }, }; +const unsubscriptions = []; /** * */ @@ -142,6 +143,8 @@ const cornerstoneExtension: Types.Extensions.Extension = { useToggleOneUpViewportGridStore.getState().clearToggleOneUpViewportGridStore(); useSegmentationPresentationStore.getState().clearSegmentationPresentationStore(); segmentationService.removeAllSegmentations(); + + unsubscriptions.forEach(unsubscribe => unsubscribe()); }, /** @@ -149,7 +152,7 @@ const cornerstoneExtension: Types.Extensions.Extension = { * * @param configuration.csToolsConfig - Passed directly to `initCornerstoneTools` */ - preRegistration: function (props: Types.Extensions.ExtensionParams): Promise { + preRegistration: async function (props: Types.Extensions.ExtensionParams): Promise { const { servicesManager, serviceProvidersManager } = props; servicesManager.registerService(CornerstoneViewportService.REGISTRATION); servicesManager.registerService(ToolGroupService.REGISTRATION); @@ -167,7 +170,13 @@ const cornerstoneExtension: Types.Extensions.Extension = { const { syncGroupService } = servicesManager.services; syncGroupService.registerCustomSynchronizer('frameview', createFrameViewSynchronizer); - return init.call(this, props); + const initResult = await init.call(this, props); + + unsubscriptions.push(...initResult.unsubscriptions); + + return { + ...initResult, + }; }, getToolbarModule, getHangingProtocolModule, diff --git a/extensions/cornerstone/src/init.tsx b/extensions/cornerstone/src/init.tsx index d372c0651..e116c6134 100644 --- a/extensions/cornerstone/src/init.tsx +++ b/extensions/cornerstone/src/init.tsx @@ -193,13 +193,14 @@ export default async function init({ }); // Setup segmentation event handlers - setupSegmentationDataModifiedHandler({ - segmentationService, - customizationService, - commandsManager, - }); + const { unsubscribe: unsubscribeSegmentationDataModifiedHandler } = + setupSegmentationDataModifiedHandler({ + segmentationService, + customizationService, + commandsManager, + }); - setupSegmentationModifiedHandler({ + const { unsubscribe: unsubscribeSegmentationModifiedHandler } = setupSegmentationModifiedHandler({ segmentationService, }); @@ -299,6 +300,13 @@ export default async function init({ // Call this function when initializing initializeWebWorkerProgressHandler(servicesManager.services.uiNotificationService); + + const unsubscriptions = [ + unsubscribeSegmentationDataModifiedHandler, + unsubscribeSegmentationModifiedHandler, + ]; + + return { unsubscriptions }; } function initializeWebWorkerProgressHandler(uiNotificationService) { diff --git a/extensions/cornerstone/src/panels/PanelSegmentation.tsx b/extensions/cornerstone/src/panels/PanelSegmentation.tsx index cee8e5485..91ce81fe4 100644 --- a/extensions/cornerstone/src/panels/PanelSegmentation.tsx +++ b/extensions/cornerstone/src/panels/PanelSegmentation.tsx @@ -2,12 +2,10 @@ import React from 'react'; import { SegmentationTable } from '@ohif/ui-next'; import { useActiveViewportSegmentationRepresentations } from '../hooks/useActiveViewportSegmentationRepresentations'; import { metaData } from '@cornerstonejs/core'; +import { useSystem } from '@ohif/core/src'; -export default function PanelSegmentation({ - servicesManager, - commandsManager, - children, -}: withAppTypes) { +export default function PanelSegmentation({ children }: withAppTypes) { + const { commandsManager, servicesManager } = useSystem(); const { customizationService, displaySetService } = servicesManager.services; const { segmentationsWithRepresentations, disabled } = diff --git a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts index 2a7fa9257..bb0d9cad1 100644 --- a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts +++ b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts @@ -427,6 +427,8 @@ class SegmentationService extends PubSubService { ...metaData.get('instance', image.referencedImageId), })); + segDisplaySet.imageIds = derivedImageIds; + // We should parse the segmentation as separate slices to support overlapping segments. // This parsing should occur in the CornerstoneJS library adapters. // For now, we use the volume returned from the library and chop it here. @@ -513,7 +515,7 @@ class SegmentationService extends PubSubService { type: LABELMAP, data: { imageIds: derivedImageIds, - referencedVolumeId: this._getVolumeIdForDisplaySet(referencedDisplaySet), + // referencedVolumeId: this._getVolumeIdForDisplaySet(referencedDisplaySet), referencedImageIds: imageIds as string[], }, }, @@ -566,9 +568,10 @@ class SegmentationService extends PubSubService { const referencedImageIds = referencedDisplaySet.imageIds; // find the first image id that contains a referenced SOP instance UID - const firstSegmentedSliceImageId = referencedImageIds.find(imageId => - referencedImageIdsWithGeometry.some(referencedId => imageId.includes(referencedId)) - ); + const firstSegmentedSliceImageId = + referencedImageIds?.find(imageId => + referencedImageIdsWithGeometry.some(referencedId => imageId.includes(referencedId)) + ) || null; rtDisplaySet.firstSegmentedSliceImageId = firstSegmentedSliceImageId; // Map ROI contours to RT Struct Data diff --git a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts index 4952da070..890ef4bf2 100644 --- a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts +++ b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts @@ -742,6 +742,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi // If you call loadStudyMetadata and it's not in the DicomMetadataStore cache, it should fire // a request through the data source? // (This call may or may not create sub-requests for series metadata) + const { displaySetService } = this.servicesManager.services; const volumeInputArray = []; const displaySetOptionsArray = viewportInfo.getDisplaySetOptions(); const { hangingProtocolService } = this.servicesManager.services; @@ -750,7 +751,13 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi const displaySetInstanceUIDs = []; for (const [index, data] of viewportData.data.entries()) { - const { volume, imageIds, displaySetInstanceUID } = data; + const { imageIds, displaySetInstanceUID } = data; + let volume = data.volume; + + if (!volume) { + const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); + volume = csToolsUtils.getOrCreateImageVolume(displaySet.images.map(image => image.imageId)); + } displaySetInstanceUIDs.push(displaySetInstanceUID); diff --git a/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx b/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx index c343b85c9..2bbbda1dc 100644 --- a/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx +++ b/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import html2canvas from 'html2canvas'; import { getEnabledElement, StackViewport, BaseVolumeViewport } from '@cornerstonejs/core'; -import { ToolGroupManager } from '@cornerstonejs/tools'; +import { ToolGroupManager, segmentation, Enums } from '@cornerstonejs/tools'; import { getEnabledElement as OHIFgetEnabledElement } from '../state'; import { useSystem } from '@ohif/core/src'; @@ -103,6 +103,9 @@ const CornerstoneViewportDownloadForm = ({ return; } + const segmentationRepresentations = + segmentation.state.getViewportSegmentationRepresentations(activeViewportId); + const { viewport } = activeViewportEnabledElement; const downloadViewport = renderingEngine.getViewport(VIEWPORT_ID); @@ -113,20 +116,48 @@ const CornerstoneViewportDownloadForm = ({ await downloadViewport.setStack([imageId]); downloadViewport.setProperties(properties); - - return { - width: Math.min(width || DEFAULT_SIZE, MAX_TEXTURE_SIZE), - height: Math.min(height || DEFAULT_SIZE, MAX_TEXTURE_SIZE), - }; } else if (downloadViewport instanceof BaseVolumeViewport) { const volumeIds = viewport.getAllVolumeIds(); downloadViewport.setVolumes([{ volumeId: volumeIds[0] }]); - - return { - width: Math.min(width || DEFAULT_SIZE, MAX_TEXTURE_SIZE), - height: Math.min(height || DEFAULT_SIZE, MAX_TEXTURE_SIZE), - }; } + + if (segmentationRepresentations.length > 0) { + segmentationRepresentations.forEach(segRepresentation => { + const { segmentationId, colorLUTIndex, type } = segRepresentation; + if (type === Enums.SegmentationRepresentations.Labelmap) { + segmentation.addLabelmapRepresentationToViewportMap({ + [downloadViewport.id]: [ + { + segmentationId, + type: Enums.SegmentationRepresentations.Labelmap, + config: { + colorLUTOrIndex: colorLUTIndex, + }, + }, + ], + }); + } + + if (type === Enums.SegmentationRepresentations.Contour) { + segmentation.addContourRepresentationToViewportMap({ + [downloadViewport.id]: [ + { + segmentationId, + type: Enums.SegmentationRepresentations.Contour, + config: { + colorLUTOrIndex: colorLUTIndex, + }, + }, + ], + }); + } + }); + } + + return { + width: Math.min(width || DEFAULT_SIZE, MAX_TEXTURE_SIZE), + height: Math.min(height || DEFAULT_SIZE, MAX_TEXTURE_SIZE), + }; } catch (error) { console.error('Error loading image:', error); } @@ -167,6 +198,10 @@ const CornerstoneViewportDownloadForm = ({ setTimeout(() => { handleLoadImage(viewportDimensions.width, viewportDimensions.height); handleToggleAnnotations(showAnnotations); + // we need a resize here to make suer annotations world to canvas + // are properly calculated + renderingEngine.resize(); + renderingEngine.render(); }, 100); } }, [viewportDimensions, showAnnotations]); diff --git a/extensions/cornerstone/src/utils/hydrationUtils.ts b/extensions/cornerstone/src/utils/hydrationUtils.ts new file mode 100644 index 000000000..fd5f6120b --- /dev/null +++ b/extensions/cornerstone/src/utils/hydrationUtils.ts @@ -0,0 +1,31 @@ +function getUpdatedViewportsForSegmentation({ + viewportId, + servicesManager, + displaySetInstanceUIDs, +}: withAppTypes) { + const { hangingProtocolService, viewportGridService } = servicesManager.services; + + const { isHangingProtocolLayout } = viewportGridService.getState(); + + const viewport = getTargetViewport({ viewportId, viewportGridService }); + const targetViewportId = viewport.viewportOptions.viewportId; + + const updatedViewports = hangingProtocolService.getViewportsRequireUpdate( + targetViewportId, + displaySetInstanceUIDs[0], + isHangingProtocolLayout + ); + + return updatedViewports.filter(v => v.viewportOptions?.viewportType !== 'volume3d'); +} + +const getTargetViewport = ({ viewportId, viewportGridService }) => { + const { viewports, activeViewportId } = viewportGridService.getState(); + const targetViewportId = viewportId || activeViewportId; + + const viewport = viewports.get(targetViewportId); + + return viewport; +}; + +export { getUpdatedViewportsForSegmentation }; diff --git a/extensions/cornerstone/src/utils/segmentationHandlers.ts b/extensions/cornerstone/src/utils/segmentationHandlers.ts index f7e541115..8e233e9cc 100644 --- a/extensions/cornerstone/src/utils/segmentationHandlers.ts +++ b/extensions/cornerstone/src/utils/segmentationHandlers.ts @@ -9,10 +9,15 @@ export function setupSegmentationDataModifiedHandler({ customizationService, commandsManager, }) { - segmentationService.subscribeDebounced( + const { unsubscribe } = segmentationService.subscribeDebounced( segmentationService.EVENTS.SEGMENTATION_DATA_MODIFIED, async ({ segmentationId }) => { const segmentation = segmentationService.getSegmentation(segmentationId); + + if (!segmentation) { + return; + } + const readableText = customizationService.getCustomization('panelSegmentation.readableText'); // Check for segments with bidirectional measurements and update them @@ -46,17 +51,23 @@ export function setupSegmentationDataModifiedHandler({ }, 1000 ); + + return { unsubscribe }; } /** * Sets up the handler for segmentation modification events */ export function setupSegmentationModifiedHandler({ segmentationService }) { - segmentationService.subscribe( + const { unsubscribe } = segmentationService.subscribe( segmentationService.EVENTS.SEGMENTATION_MODIFIED, async ({ segmentationId }) => { const segmentation = segmentationService.getSegmentation(segmentationId); + if (!segmentation) { + return; + } + const annotationState = cornerstoneTools.annotation.state.getAllAnnotations(); const bidirectionalAnnotations = annotationState.filter( annotation => @@ -90,4 +101,6 @@ export function setupSegmentationModifiedHandler({ segmentationService }) { }); } ); + + return { unsubscribe }; } diff --git a/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx b/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx index 2517bac9c..53a676bbf 100644 --- a/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx +++ b/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx @@ -113,7 +113,7 @@ function DataSourceConfigurationModalComponent({ : 'border border-dashed border-secondary-light'; const getSelectedItemTextClasses = itemIndex => - itemIndex <= selectedItems.length ? 'text-primary-light' : 'text-primary-active'; + itemIndex <= selectedItems.length ? 'text-primary-light' : 'text-primary'; const getErrorComponent = (): ReactElement => { return ( diff --git a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowserHeader.tsx b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowserHeader.tsx index 0c55559ba..e5349b245 100644 --- a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowserHeader.tsx +++ b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowserHeader.tsx @@ -20,7 +20,7 @@ function PanelStudyBrowserHeader({
-
+
{actionIcons.map((icon: actionIcon, index) => React.createElement(Icons[icon.iconName] || Icons.MissingIcon, { key: index, diff --git a/extensions/default/src/ViewerLayout/HeaderPatientInfo/HeaderPatientInfo.tsx b/extensions/default/src/ViewerLayout/HeaderPatientInfo/HeaderPatientInfo.tsx index cac66d2f9..f6d858512 100644 --- a/extensions/default/src/ViewerLayout/HeaderPatientInfo/HeaderPatientInfo.tsx +++ b/extensions/default/src/ViewerLayout/HeaderPatientInfo/HeaderPatientInfo.tsx @@ -44,9 +44,9 @@ function HeaderPatientInfo({ servicesManager, appConfig }: withAppTypes) { onClick={handleOnClick} > {isMixedPatients ? ( - + ) : ( - + )}
{expanded ? ( @@ -61,12 +61,12 @@ function HeaderPatientInfo({ servicesManager, appConfig }: withAppTypes) {
) : ( -
+
{isMixedPatients ? 'Multiple Patients' : 'Patient'}
)}
- +
); } diff --git a/extensions/default/src/ViewerLayout/ViewerHeader.tsx b/extensions/default/src/ViewerLayout/ViewerHeader.tsx index e2dce512d..6e1afb651 100644 --- a/extensions/default/src/ViewerLayout/ViewerHeader.tsx +++ b/extensions/default/src/ViewerLayout/ViewerHeader.tsx @@ -95,7 +95,7 @@ function ViewerHeader({ appConfig }: withAppTypes<{ appConfig: AppTypes.Config } ) } UndoRedo={ -
+
-
- ); -}; diff --git a/extensions/tmtv/src/commandsModule.ts b/extensions/tmtv/src/commandsModule.ts index f8555e802..186390f71 100644 --- a/extensions/tmtv/src/commandsModule.ts +++ b/extensions/tmtv/src/commandsModule.ts @@ -8,7 +8,6 @@ import createAndDownloadTMTVReport from './utils/createAndDownloadTMTVReport'; import dicomRTAnnotationExport from './utils/dicomRTAnnotationExport/RTStructureSet'; -import { getWebWorkerManager } from '@cornerstonejs/core'; import { Enums } from '@cornerstonejs/tools'; import { utils } from '@ohif/core'; @@ -22,32 +21,6 @@ const ROI_THRESHOLD_MANUAL_TOOL_IDS = [ 'CircleROIStartEndThreshold', ]; -const workerManager = getWebWorkerManager(); - -// Register the task - -function getVolumesFromSegmentation(segmentationId) { - const csSegmentation = csTools.segmentation.state.getSegmentation(segmentationId); - const labelmapData = csSegmentation.representationData[ - SegmentationRepresentations.Labelmap - ] as csTools.Types.LabelmapToolOperationDataVolume; - - const { volumeId, referencedVolumeId } = labelmapData; - const labelmapVolume = cs.cache.getVolume(volumeId); - const referencedVolume = cs.cache.getVolume(referencedVolumeId); - - return { labelmapVolume, referencedVolume }; -} - -function getLabelmapVolumeFromSegmentation(segmentation) { - const { representationData } = segmentation; - const { volumeId } = representationData[ - SegmentationRepresentations.Labelmap - ] as csTools.Types.LabelmapToolOperationDataVolume; - - return cs.cache.getVolume(volumeId); -} - const commandsModule = ({ servicesManager, commandsManager, extensionManager }: withAppTypes) => { const { viewportGridService, @@ -265,8 +238,14 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }: segmentations, }); + let total_tlg = 0; + for (const segmentationId in segReport) { + const report = segReport[segmentationId]; + const tlg = report['namedStats_lesionGlycolysis']; + total_tlg += tlg.value; + } const additionalReportRows = [ - { key: 'Total Lesion Glycolysis', value: { tlg: tlg.toFixed(4) } }, + { key: 'Total Lesion Glycolysis', value: { tlg: total_tlg.toFixed(4) } }, { key: 'Threshold Configuration', value: { ...config } }, ]; @@ -382,9 +361,8 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }: continue; } - const referencedVolumeId = labelmapVolume.referencedVolumeId; - - const referencedVolume = cs.cache.getVolume(referencedVolumeId); + const referencedVolume = + csTools.utilities.segmentation.getReferenceVolumeForSegmentationVolume(segmentationId); if (!referencedVolume) { report[id] = segReport; @@ -476,9 +454,6 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }: thresholdSegmentationByRectangleROITool: { commandFn: actions.thresholdSegmentationByRectangleROITool, }, - getTotalLesionGlycolysis: { - commandFn: actions.getTotalLesionGlycolysis, - }, calculateTMTV: { commandFn: actions.calculateTMTV, }, diff --git a/extensions/tmtv/src/getPanelModule.tsx b/extensions/tmtv/src/getPanelModule.tsx index dbd6e04d0..9da4564f6 100644 --- a/extensions/tmtv/src/getPanelModule.tsx +++ b/extensions/tmtv/src/getPanelModule.tsx @@ -5,13 +5,7 @@ import PanelTMTV from './Panels/PanelTMTV'; function getPanelModule({ commandsManager, extensionManager, servicesManager }) { const wrappedPanelPetSuv = () => { - return ( - - ); + return ; }; const wrappedROIThresholdToolbox = () => { @@ -24,12 +18,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager }) }; const wrappedROIThresholdExport = () => { - return ( - - ); + return ; }; const wrappedPanelTMTV = () => { @@ -43,10 +32,6 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager }) commandsManager={commandsManager} servicesManager={servicesManager} /> - ); }; diff --git a/modes/longitudinal/src/toolbarButtons.ts b/modes/longitudinal/src/toolbarButtons.ts index 2cea2d3ec..48f38a2d3 100644 --- a/modes/longitudinal/src/toolbarButtons.ts +++ b/modes/longitudinal/src/toolbarButtons.ts @@ -3,10 +3,12 @@ import type { Button } from '@ohif/core/types'; import { EVENTS } from '@cornerstonejs/core'; import { ViewportGridService } from '@ohif/core'; -const ReferenceLinesListeners = [ +const callbacks = (toolName: string) => [ { - commandName: 'setSourceViewportForReferenceLinesTool', - context: 'CORNERSTONE', + commandName: 'setViewportForToolConfiguration', + commandOptions: { + toolName, + }, }, ]; @@ -118,8 +120,8 @@ const toolbarButtons: Button[] = [ tooltip: 'Show Reference Lines', commands: 'toggleEnabledDisabledToolbar', listeners: { - [ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: ReferenceLinesListeners, - [ViewportGridService.EVENTS.VIEWPORTS_READY]: ReferenceLinesListeners, + [ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('ReferenceLinesTool'), + [ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('ReferenceLinesTool'), }, evaluate: [ 'evaluate.cornerstoneTool.toggle', diff --git a/platform/app/package.json b/platform/app/package.json index c391bf89e..81aadd317 100644 --- a/platform/app/package.json +++ b/platform/app/package.json @@ -54,7 +54,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.4", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^3.8.4", + "@cornerstonejs/dicom-image-loader": "^3.8.5", "@emotion/serialize": "^1.1.3", "@ohif/core": "3.10.0-beta.148", "@ohif/extension-cornerstone": "3.10.0-beta.148", diff --git a/platform/app/src/routes/Debug.tsx b/platform/app/src/routes/Debug.tsx index 0933bd936..56985a9df 100644 --- a/platform/app/src/routes/Debug.tsx +++ b/platform/app/src/routes/Debug.tsx @@ -15,7 +15,7 @@ function Debug() { />
-

Debug Information

+

Debug Information

Cross Origin Isolated (COOP/COEP)

diff --git a/platform/core/package.json b/platform/core/package.json index 6a73138f3..a8d5e5a3c 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -37,7 +37,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.4", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^3.8.4", + "@cornerstonejs/dicom-image-loader": "^3.8.5", "@ohif/ui": "3.10.0-beta.148", "cornerstone-math": "0.1.9", "dicom-parser": "^1.8.21" diff --git a/platform/docs/src/pages/patterns/patterns-segmentation-raw.tsx b/platform/docs/src/pages/patterns/patterns-segmentation-raw.tsx new file mode 100644 index 000000000..ee7a1c95b --- /dev/null +++ b/platform/docs/src/pages/patterns/patterns-segmentation-raw.tsx @@ -0,0 +1,503 @@ +'use client'; + +import React, { useState } from 'react'; + +import { DataRow } from '../../../../ui-next/src/components/DataRow'; +import { Button } from '../../../../ui-next/src/components/Button'; +import { + Select, + SelectValue, + SelectTrigger, + SelectContent, + SelectItem, +} from '../../../../ui-next/src/components/Select'; +import { Icons } from '../../../../ui-next/src/components/Icons'; +import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuLabel, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, + DropdownMenuPortal, +} from '../../../../ui-next/src/components/DropdownMenu'; +import { + Accordion, + AccordionItem, + AccordionTrigger, + AccordionContent, +} from '../../../../ui-next/src/components/Accordion'; +import { Slider } from '../../../../ui-next/src/components/Slider'; +import { Switch } from '../../../../ui-next/src/components/Switch'; +import { Label } from '../../../../ui-next/src/components/Label'; +import { Input } from '../../../../ui-next/src/components/Input'; +import { Tabs, TabsList, TabsTrigger } from '../../../../ui-next/src/components/Tabs'; +import { actionOptionsMap, dataList } from '../../../../ui-next/assets/data'; +import { TooltipProvider } from '../../../../ui-next/src/components/Tooltip'; +import { + HoverCard, + HoverCardTrigger, + HoverCardContent, +} from '../../../../ui-next/src/components/HoverCard'; +import { DataItem, ListGroup } from '../../../../ui-next/assets/data'; +export default function SegmentationPanel() { + const [selectedRowId, setSelectedRowId] = useState(null); + const [selectedTab, setSelectedTab] = useState('Fill & Outline'); + const handleAction = (id: string, action: string) => { + console.log(`Action "${action}" triggered for item with id: ${id}`); + // Implement actual action logic here + }; + + // Handle row selection + const handleRowSelect = (id: string) => { + setSelectedRowId(prevSelectedId => (prevSelectedId === id ? null : id)); + }; + + const organSegmentationGroup = dataList.find( + (listGroup: any) => listGroup.type === 'Organ Segmentation' + ) as unknown as ListGroup; + + if (!organSegmentationGroup) { + return
Organ Segmentation data not found.
; + } + + // Create a state to track which item's statistics to show + + // Function to render statistics panel + const renderStatisticsPanel = (item: DataItem) => { + if (!item.statistics) { + return null; + } + + const stats = item.statistics; + return ( +
+
+
+

{item.title}

+
+ +
+
+
Centroid X
+
+ {stats.centroidX.value}{' '} + {stats.centroidX.unit} +
+
+ +
+
Centroid Y
+
+ {stats.centroidY.value}{' '} + {stats.centroidY.unit} +
+
+ +
+
Centroid Z
+
+ {stats.centroidZ.value}{' '} + {stats.centroidZ.unit} +
+
+ +
+
Frame Duration
+
+ {stats.frameDuration.value}{' '} + {stats.frameDuration.unit} +
+
+ +
+
Kurtosis
+
+ {stats.kurtosis.value}{' '} + {stats.kurtosis.unit} +
+
+ +
+
Max
+
+ {stats.max.value}{' '} + {stats.max.unit} +
+
+ +
+
Max Slice
+
+ {stats.maxSlice.value}{' '} + {stats.maxSlice.unit} +
+
+ +
+
Mean
+
+ {stats.mean.value}{' '} + {stats.mean.unit} +
+
+ +
+
Median
+
+ {stats.median.value}{' '} + {stats.median.unit} +
+
+ +
+
Min
+
+ {stats.min.value}{' '} + {stats.min.unit} +
+
+ +
+
Regions
+
+ {stats.regions.value}{' '} + {stats.regions.unit} +
+
+ +
+
Skewness
+
+ {stats.skewness.value}{' '} + {stats.skewness.unit} +
+
+ +
+
Sphere Diameter
+
+ {stats.sphereDiameter.value}{' '} + {stats.sphereDiameter.unit} +
+
+ +
+
Standard Deviation
+
+ {stats.standardDeviation.value}{' '} + {stats.standardDeviation.unit} +
+
+ +
+
SUV Peak
+
+ {stats.suvPeak.value}{' '} + {stats.suvPeak.unit} +
+
+ +
+
Total
+
+ {stats.total.value}{' '} + {stats.total.unit} +
+
+ +
+
Glycolysis
+
+ {stats.glycolysis.value}{' '} + {stats.glycolysis.unit} +
+
+ +
+
Volume
+
+ {stats.volume.value}{' '} + {stats.volume.unit} +
+
+ +
+
Voxel Count
+
+ {stats.voxelCount.value}{' '} + {stats.voxelCount.unit} +
+
+
+
+ ); + }; + + return ( +
+
+ + + {/* Segmentation Tools */} + + + Segmentation Tools + + +
+
+
+ + {/* Segmentation List */} + + + Segmentation List + + +
+ {/* Header Controls */} +
+ + + + + + + + Create New Segmentation + + + Manage Current Segmentation + + + Remove from Viewport + + + + Rename + + + + + Export & Download + + + + Export DICOM SEG + Download DICOM SEG + Download DICOM RTSTRUCT + + + + + + + Delete + + + + + +
+ + {/* Appearance Settings */} + + +
+ + Appearance Settings +
+
+ +
+
+ {/* Display Label with Selected Tab */} +
Show: {selectedTab}
+ {/* Tabs Controls */} + + + + + + + + + + + + + +
+ {/* Opacity Slider */} +
+ + + +
+ {/* Border Slider */} +
+ + + +
+ {/* Sync Changes Switch */} +
+ + +
+
+ {/* Display Inactive Segmentations Switch */} +
+ + +
+ {/* Additional Opacity Slider */} +
+ + + +
+
+
+
+ {/* Action Buttons */} +
+ + +
+
+ + {/* Data Rows */} +
+ {organSegmentationGroup.items.map((item, index) => { + const compositeId = `${organSegmentationGroup.type}-${item.id}-panel`; // Ensure unique composite ID + return ( + + +
+ handleAction(compositeId, action)} + isSelected={selectedRowId === compositeId} + onSelect={() => handleRowSelect(compositeId)} + isVisible={true} + isLocked={false} + onToggleVisibility={() => console.debug('Toggle visibility')} + onToggleLocked={() => console.debug('Toggle locked')} + onRename={() => console.debug('Rename')} + onDelete={() => console.debug('Delete')} + onColor={() => console.debug('Color')} + disableEditing={false} + /> +
+
+ + {renderStatisticsPanel(item)} + +
+ ); + })} +
+
+
+
+
+
+
+ ); +} diff --git a/platform/docs/src/pages/patterns/patterns-segmentation.tsx b/platform/docs/src/pages/patterns/patterns-segmentation.tsx index 3e9681aa5..1a44cb635 100644 --- a/platform/docs/src/pages/patterns/patterns-segmentation.tsx +++ b/platform/docs/src/pages/patterns/patterns-segmentation.tsx @@ -2,538 +2,72 @@ import React, { useState } from 'react'; -import { DataRow } from '../../../../ui-next/src/components/DataRow'; -import { Button } from '../../../../ui-next/src/components/Button'; -import { - Select, - SelectValue, - SelectTrigger, - SelectContent, - SelectItem, -} from '../../../../ui-next/src/components/Select'; -import { Icons } from '../../../../ui-next/src/components/Icons'; -import { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuLabel, - DropdownMenuSub, - DropdownMenuSubTrigger, - DropdownMenuSubContent, - DropdownMenuPortal, -} from '../../../../ui-next/src/components/DropdownMenu'; -import { - Accordion, - AccordionItem, - AccordionTrigger, - AccordionContent, -} from '../../../../ui-next/src/components/Accordion'; -import { Slider } from '../../../../ui-next/src/components/Slider'; -import { Switch } from '../../../../ui-next/src/components/Switch'; -import { Label } from '../../../../ui-next/src/components/Label'; -import { Input } from '../../../../ui-next/src/components/Input'; -import { Tabs, TabsList, TabsTrigger } from '../../../../ui-next/src/components/Tabs'; -import { actionOptionsMap, dataList } from '../../../../ui-next/assets/data'; +import { panelSegmentationData } from '../../../../ui-next/assets/data'; +import { SegmentationTable } from '../../../../ui-next/src/components/SegmentationTable'; import { TooltipProvider } from '../../../../ui-next/src/components/Tooltip'; -import { - HoverCard, - HoverCardTrigger, - HoverCardContent, -} from '../../../../ui-next/src/components/HoverCard'; - -interface DataItem { - id: number; - title: string; - description: string; - optionalField?: string; - colorHex?: string; - details?: { primary: string[]; secondary: string[] }; - series?: string; - statistics?: { - centroidX: string; - centroidY: string; - centroidZ: string; - frameDuration: string; - kurtosis: string; - max: string; - maxSlice: string; - mean: string; - median: string; - min: string; - regions: string; - skewness: string; - sphereDiameter: string; - standardDeviation: string; - suvPeak: string; - total: string; - glycolysis: string; - volume: string; - voxelCount: string; - }; -} - -interface ListGroup { - type: string; - items: DataItem[]; -} export default function SegmentationPanel() { - const [selectedRowId, setSelectedRowId] = useState(null); - const [selectedTab, setSelectedTab] = useState('Fill & Outline'); - const handleAction = (id: string, action: string) => { - console.log(`Action "${action}" triggered for item with id: ${id}`); - // Implement actual action logic here + const tableProps = panelSegmentationData; + tableProps.mode = 'expanded'; + + const renderSegments = () => { + return ( + + + + + ); }; - // Handle row selection - const handleRowSelect = (id: string) => { - setSelectedRowId(prevSelectedId => (prevSelectedId === id ? null : id)); - }; - - const organSegmentationGroup = dataList.find( - (listGroup: any) => listGroup.type === 'Organ Segmentation' - ) as unknown as ListGroup; - - if (!organSegmentationGroup) { - return
Organ Segmentation data not found.
; - } - - // Create a state to track which item's statistics to show - - // Function to render statistics panel - const renderStatisticsPanel = (item: DataItem) => { - if (!item.statistics) { - return null; + // Render content based on mode + const renderModeContent = () => { + if (tableProps.mode === 'collapsed') { + return ( + + + + {/* */} + + + + + + + {renderSegments()} + + + ); } - const stats = item.statistics; return ( -
-
-
-

{item.title}

-
+ <> + + + + {/* */} + + + + -
-
-
Centroid X
-
- {stats.centroidX.value}{' '} - {stats.centroidX.unit} -
-
- -
-
Centroid Y
-
- {stats.centroidY.value}{' '} - {stats.centroidY.unit} -
-
- -
-
Centroid Z
-
- {stats.centroidZ.value}{' '} - {stats.centroidZ.unit} -
-
- -
-
Frame Duration
-
- {stats.frameDuration.value}{' '} - {stats.frameDuration.unit} -
-
- -
-
Kurtosis
-
- {stats.kurtosis.value}{' '} - {stats.kurtosis.unit} -
-
- -
-
Max
-
- {stats.max.value}{' '} - {stats.max.unit} -
-
- -
-
Max Slice
-
- {stats.maxSlice.value}{' '} - {stats.maxSlice.unit} -
-
- -
-
Mean
-
- {stats.mean.value}{' '} - {stats.mean.unit} -
-
- -
-
Median
-
- {stats.median.value}{' '} - {stats.median.unit} -
-
- -
-
Min
-
- {stats.min.value}{' '} - {stats.min.unit} -
-
- -
-
Regions
-
- {stats.regions.value}{' '} - {stats.regions.unit} -
-
- -
-
Skewness
-
- {stats.skewness.value}{' '} - {stats.skewness.unit} -
-
- -
-
Sphere Diameter
-
- {stats.sphereDiameter.value}{' '} - {stats.sphereDiameter.unit} -
-
- -
-
Standard Deviation
-
- {stats.standardDeviation.value}{' '} - {stats.standardDeviation.unit} -
-
- -
-
SUV Peak
-
- {stats.suvPeak.value}{' '} - {stats.suvPeak.unit} -
-
- -
-
Total
-
- {stats.total.value}{' '} - {stats.total.unit} -
-
- -
-
Glycolysis
-
- {stats.glycolysis.value}{' '} - {stats.glycolysis.unit} -
-
- -
-
Volume
-
- {stats.volume.value}{' '} - {stats.volume.unit} -
-
- -
-
Voxel Count
-
- {stats.voxelCount.value}{' '} - {stats.voxelCount.unit} -
-
-
-
+ + + {renderSegments()} + + + ); }; return ( -
-
- - - {/* Segmentation Tools */} - - - Segmentation Tools - - -
-
-
- - {/* Segmentation List */} - - - Segmentation List - - -
- {/* Header Controls */} -
- - - - - - - - Create New Segmentation - - - Manage Current Segmentation - - - Remove from Viewport - - - - Rename - - - - - Export & Download - - - - Export DICOM SEG - Download DICOM SEG - Download DICOM RTSTRUCT - - - - - - - Delete - - - - - -
- - {/* Appearance Settings */} - - -
- - Appearance Settings -
-
- -
-
- {/* Display Label with Selected Tab */} -
Show: {selectedTab}
- {/* Tabs Controls */} - - - - - - - - - - - - - -
- {/* Opacity Slider */} -
- - - -
- {/* Border Slider */} -
- - - -
- {/* Sync Changes Switch */} -
- - -
-
- {/* Display Inactive Segmentations Switch */} -
- - -
- {/* Additional Opacity Slider */} -
- - - -
-
-
-
- {/* Action Buttons */} -
- - -
-
- - {/* Data Rows */} -
- {organSegmentationGroup.items.map((item, index) => { - const compositeId = `${organSegmentationGroup.type}-${item.id}-panel`; // Ensure unique composite ID - return ( - - -
- handleAction(compositeId, action)} - isSelected={selectedRowId === compositeId} - onSelect={() => handleRowSelect(compositeId)} - isVisible={true} - isLocked={false} - onToggleVisibility={() => console.debug('Toggle visibility')} - onToggleLocked={() => console.debug('Toggle locked')} - onRename={() => console.debug('Rename')} - onDelete={() => console.debug('Delete')} - onColor={() => console.debug('Color')} - disableEditing={false} - /> -
-
- - {renderStatisticsPanel(item)} - -
- ); - })} -
-
-
-
-
-
+
+ + + + + {renderModeContent()} + +
); } diff --git a/platform/ui-next/assets/data/index.ts b/platform/ui-next/assets/data/index.ts index 8eac4ec80..22701c0e8 100644 --- a/platform/ui-next/assets/data/index.ts +++ b/platform/ui-next/assets/data/index.ts @@ -1,3 +1,39 @@ +interface DataItem { + id: number; + title: string; + description: string; + optionalField?: string; + colorHex?: string; + details?: { primary: string[]; secondary: string[] }; + series?: string; + statistics?: { + centroidX: { value: number; unit: string }; + centroidY: { value: number; unit: string }; + centroidZ: { value: number; unit: string }; + frameDuration: { value: number; unit: string }; + kurtosis: { value: number; unit: string }; + max: { value: number; unit: string }; + maxSlice: { value: number; unit: string }; + mean: { value: number; unit: string }; + median: { value: number; unit: string }; + min: { value: number; unit: string }; + regions: { value: number; unit: string }; + skewness: { value: number; unit: string }; + sphereDiameter: { value: number; unit: string }; + standardDeviation: { value: number; unit: string }; + suvPeak: { value: number; unit: string }; + total: { value: number; unit: string }; + glycolysis: { value: number; unit: string }; + volume: { value: number; unit: string }; + voxelCount: { value: number; unit: string }; + }; +} + +interface ListGroup { + type: string; + items: DataItem[]; +} + const actionOptionsMap: { [key: string]: string[] } = { Measurement: ['Rename', 'Lock', 'Delete'], Segmentation: ['Rename', 'Lock', 'Export', 'Delete'], @@ -547,4 +583,146 @@ const dataList = [ }, ]; -export { actionOptionsMap, dataList }; +const segmentationData = { + representation: { + id: '791aaa59-1df6-60f8-3c28-6d870e6a4585-Labelmap-default', + segmentationId: '791aaa59-1df6-60f8-3c28-6d870e6a4585', + label: 'Segmentation', + active: true, + type: 'Labelmap', + visible: true, + segments: { + '1': { + color: [255, 0, 0, 255], + segmentIndex: 1, + opacity: 255, + visible: true, + }, + '2': { + color: [0, 255, 0, 255], + segmentIndex: 2, + opacity: 255, + visible: true, + }, + '3': { + color: [255, 255, 0, 255], + segmentIndex: 3, + opacity: 255, + visible: true, + }, + '4': { + color: [0, 255, 255, 255], + segmentIndex: 4, + opacity: 255, + visible: true, + }, + }, + viewportId: 'default', + colorLUTIndex: 0, + config: {}, + }, + segmentation: { + segmentationId: '791aaa59-1df6-60f8-3c28-6d870e6a4585', + label: 'Segmentation', + cachedStats: {}, + segments: { + '1': { + segmentIndex: 1, + label: 'Liver', + locked: false, + cachedStats: { + category: 'Anatomical Structure', + type: 'Liver', + algorithmType: 'SEMIAUTOMATIC', + algorithmName: 'Amira', + }, + active: true, + }, + '2': { + segmentIndex: 2, + label: 'Mass', + locked: false, + cachedStats: { + category: 'Morphologically Altered Structure', + type: 'Mass', + algorithmType: 'SEMIAUTOMATIC', + algorithmName: 'Amira', + }, + active: false, + }, + '3': { + segmentIndex: 3, + label: 'Portal vein', + locked: false, + cachedStats: { + category: 'Anatomical Structure', + type: 'Portal vein', + algorithmType: 'SEMIAUTOMATIC', + algorithmName: 'Amira', + }, + active: false, + }, + '4': { + segmentIndex: 4, + label: 'Abdominal aorta', + locked: false, + cachedStats: { + category: 'Anatomical Structure', + type: 'Abdominal aorta', + algorithmType: 'SEMIAUTOMATIC', + algorithmName: 'Amira', + }, + active: false, + }, + }, + representationData: { + Labelmap: { + imageIds: [], + referencedImageIds: [], + }, + }, + }, +}; + +const getSegmentationData = ({ + active = false, + segmentationId = '791aaa59-1df6-60f8-3c28-6d870e6a4585', +}) => { + return { + segmentation: { + ...segmentationData.segmentation, + active, + segmentationId, + }, + representation: { + ...segmentationData.representation, + active, + }, + }; +}; + +const panelSegmentationData = { + disabled: false, + data: [ + getSegmentationData({ active: true }), + getSegmentationData({ active: false, segmentationId: 'babas' }), + ], + mode: 'collapsed', + title: 'Segmentations', + exportOptions: [ + { + segmentationId: '791aaa59-1df6-60f8-3c28-6d870e6a4585', + isExportable: true, + }, + { + segmentationId: '791aaa59-1df6-60f8-3c28-6d870e6a4585', + isExportable: true, + }, + ], + disableEditing: false, + showAddSegment: true, + renderInactiveSegmentations: true, +}; + +export { actionOptionsMap, dataList, panelSegmentationData }; +export type { DataItem, ListGroup }; diff --git a/platform/ui-next/src/components/Header/Header.tsx b/platform/ui-next/src/components/Header/Header.tsx index 03a573836..5174b154f 100644 --- a/platform/ui-next/src/components/Header/Header.tsx +++ b/platform/ui-next/src/components/Header/Header.tsx @@ -65,7 +65,7 @@ function Header({ onClick={onClickReturn} data-cy="return-to-work-list" > - {isReturnEnabled && } + {isReturnEnabled && }
{WhiteLabeling?.createLogoComponentFn?.(React, props) || }
@@ -86,7 +86,7 @@ function Header({ diff --git a/platform/ui-next/src/components/SegmentationTable/AddSegmentationRow.tsx b/platform/ui-next/src/components/SegmentationTable/AddSegmentationRow.tsx index 63c9e65f3..ea6d213f0 100644 --- a/platform/ui-next/src/components/SegmentationTable/AddSegmentationRow.tsx +++ b/platform/ui-next/src/components/SegmentationTable/AddSegmentationRow.tsx @@ -27,7 +27,7 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({ onClick={() => !disabled && onSegmentationAdd('')} > {children} -
+
{disabled ? : }
diff --git a/platform/ui-next/src/components/SegmentationTable/SegmentationExpanded.tsx b/platform/ui-next/src/components/SegmentationTable/SegmentationExpanded.tsx index bc988602f..d57abda78 100644 --- a/platform/ui-next/src/components/SegmentationTable/SegmentationExpanded.tsx +++ b/platform/ui-next/src/components/SegmentationTable/SegmentationExpanded.tsx @@ -17,7 +17,7 @@ const SegmentationExpandedHeader = ({ children }: { children: React.ReactNode }) return ( { e.stopPropagation(); onSegmentationClick(segmentation.segmentationId); @@ -59,7 +59,7 @@ const SegmentationExpandedInfo = () => { const { segmentation } = useSegmentationExpanded('SegmentationExpandedInfo'); return ( -
+