feat(image capture): Enhance SEG and RTSS Support with Dependency Updates and Bug Fixes (#4924)

This commit is contained in:
Alireza 2025-04-04 12:11:29 -04:00 committed by GitHub
parent 479e280fdf
commit fc33bd0d59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
57 changed files with 1247 additions and 935 deletions

276
bun.lock
View File

@ -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=="],

View File

@ -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"
}

View File

@ -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"
}

View File

@ -305,9 +305,6 @@ const commandsModule = ({
};
const definitions = {
loadSegmentationDisplaySetsForViewport: {
commandFn: actions.loadSegmentationDisplaySetsForViewport,
},
loadSegmentationsForViewport: {
commandFn: actions.loadSegmentationsForViewport,
},

View File

@ -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],
});

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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",

View File

@ -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 {

View File

@ -303,7 +303,7 @@ function DicomUploadProgress({
<span
className={classNames(
NO_WRAP_ELLIPSIS_CLASS_NAMES,
'text-primary-active hover:text-primary-light active:text-aqua-pale ml-auto cursor-pointer'
'text-primary hover:text-primary-light active:text-aqua-pale ml-auto cursor-pointer'
)}
onClick={cancelAllUploads}
>

View File

@ -89,7 +89,7 @@ const DicomUploadProgressItem = memo(
)}
<div className="ml-auto flex cursor-pointer">
<Icons.Close
className="text-primary-active self-center"
className="text-primary self-center"
onClick={cancelUpload}
/>
</div>

View File

@ -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<void> {
preRegistration: async function (props: Types.Extensions.ExtensionParams): Promise<void> {
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,

View File

@ -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) {

View File

@ -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 } =

View File

@ -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

View File

@ -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);

View File

@ -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]);

View File

@ -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 };

View File

@ -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 };
}

View File

@ -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 (

View File

@ -20,7 +20,7 @@ function PanelStudyBrowserHeader({
<div className={'flex h-[24px] w-full select-none justify-center self-center text-[14px]'}>
<div className="flex w-full items-center gap-[10px]">
<div className="flex items-center justify-center">
<div className="text-primary-active flex items-center space-x-1">
<div className="text-primary flex items-center space-x-1">
{actionIcons.map((icon: actionIcon, index) =>
React.createElement(Icons[icon.iconName] || Icons.MissingIcon, {
key: index,

View File

@ -44,9 +44,9 @@ function HeaderPatientInfo({ servicesManager, appConfig }: withAppTypes) {
onClick={handleOnClick}
>
{isMixedPatients ? (
<Icons.MultiplePatients className="text-primary-active" />
<Icons.MultiplePatients className="text-primary" />
) : (
<Icons.Patient className="text-primary-active" />
<Icons.Patient className="text-primary" />
)}
<div className="flex flex-col justify-center">
{expanded ? (
@ -61,12 +61,12 @@ function HeaderPatientInfo({ servicesManager, appConfig }: withAppTypes) {
</div>
</>
) : (
<div className="text-primary-active self-center text-[13px]">
<div className="text-primary self-center text-[13px]">
{isMixedPatients ? 'Multiple Patients' : 'Patient'}
</div>
)}
</div>
<Icons.ArrowLeft className={`text-primary-active ${expanded ? 'rotate-180' : ''}`} />
<Icons.ArrowLeft className={`text-primary ${expanded ? 'rotate-180' : ''}`} />
</div>
);
}

View File

@ -95,7 +95,7 @@ function ViewerHeader({ appConfig }: withAppTypes<{ appConfig: AppTypes.Config }
)
}
UndoRedo={
<div className="text-primary-active flex cursor-pointer items-center">
<div className="text-primary flex cursor-pointer items-center">
<Button
variant="ghost"
className="hover:bg-primary-dark"

View File

@ -85,7 +85,7 @@ export function Toolbox({ buttonSectionId, title }: { buttonSectionId: string; t
{CustomConfigComponent && (
<div className="ml-auto mr-2">
<Icons.Settings
className="text-primary-active h-4 w-4"
className="text-primary h-4 w-4"
onClick={e => {
e.stopPropagation();
setShowConfig(!showConfig);

View File

@ -32,8 +32,8 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@cornerstonejs/core": "^3.8.4",
"@cornerstonejs/tools": "^3.8.4",
"@cornerstonejs/core": "^3.8.5",
"@cornerstonejs/tools": "^3.8.5",
"@ohif/core": "3.10.0-beta.148",
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.148",
"@ohif/extension-default": "3.10.0-beta.148",

View File

@ -2,7 +2,8 @@ import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { DicomMetadataStore } from '@ohif/core';
import { useTranslation } from 'react-i18next';
import { Button, Input, Label, PanelSection, Separator } from '@ohif/ui-next';
import { Button, Input, Label, PanelSection } from '@ohif/ui-next';
import { useSystem } from '@ohif/core/src';
const DEFAULT_MEATADATA = {
PatientWeight: null,
@ -37,7 +38,10 @@ const InputRow = ({ children, className, ...props }) => {
// InputRow sub-components
InputRow.Label = ({ children, unit, className, ...props }) => (
<Label className={`min-w-32 flex-shrink-0 ${className || ''}`} {...props}>
<Label
className={`min-w-32 flex-shrink-0 ${className || ''}`}
{...props}
>
{children}
{unit && <span className="text-muted-foreground"> {unit}</span>}
</Label>
@ -54,7 +58,8 @@ InputRow.Input = ({ className, ...props }) => (
InputRow.Label.displayName = 'InputRow.Label';
InputRow.Input.displayName = 'InputRow.Input';
export default function PanelPetSUV({ servicesManager, commandsManager }: withAppTypes) {
export default function PanelPetSUV({ servicesManager }: withAppTypes) {
const { commandsManager } = useSystem();
const { t } = useTranslation('PanelSUV');
const { displaySetService, toolGroupService, toolbarService, hangingProtocolService } =
servicesManager.services;
@ -192,7 +197,9 @@ export default function PanelPetSUV({ servicesManager, commandsManager }: withAp
<InputRow>
<InputRow.Label unit="bq">{t('Total Dose')}</InputRow.Label>
<InputRow.Input
value={metadata.RadiopharmaceuticalInformationSequence.RadionuclideTotalDose || ''}
value={
metadata.RadiopharmaceuticalInformationSequence.RadionuclideTotalDose || ''
}
onChange={e => {
handleMetadataChange({
RadiopharmaceuticalInformationSequence: {
@ -206,7 +213,9 @@ export default function PanelPetSUV({ servicesManager, commandsManager }: withAp
<InputRow>
<InputRow.Label unit="s">{t('Half Life')}</InputRow.Label>
<InputRow.Input
value={metadata.RadiopharmaceuticalInformationSequence.RadionuclideHalfLife || ''}
value={
metadata.RadiopharmaceuticalInformationSequence.RadionuclideHalfLife || ''
}
onChange={e => {
handleMetadataChange({
RadiopharmaceuticalInformationSequence: {
@ -221,7 +230,8 @@ export default function PanelPetSUV({ servicesManager, commandsManager }: withAp
<InputRow.Label unit="s">{t('Injection Time')}</InputRow.Label>
<InputRow.Input
value={
metadata.RadiopharmaceuticalInformationSequence.RadiopharmaceuticalStartTime || ''
metadata.RadiopharmaceuticalInformationSequence
.RadiopharmaceuticalStartTime || ''
}
onChange={e => {
handleMetadataChange({
@ -269,4 +279,4 @@ PanelPetSUV.propTypes = {
}).isRequired,
}).isRequired,
}).isRequired,
};
};

View File

@ -2,11 +2,10 @@ import React, { useEffect } from 'react';
import { useActiveViewportSegmentationRepresentations } from '@ohif/extension-cornerstone';
import { handleROIThresholding } from '../../utils/handleROIThresholding';
import { debounce } from '@ohif/core/src/utils';
import { useSystem } from '@ohif/core/src';
export default function PanelRoiThresholdSegmentation({
servicesManager,
commandsManager,
}: withAppTypes) {
export default function PanelRoiThresholdSegmentation() {
const { commandsManager, servicesManager } = useSystem();
const { segmentationService } = servicesManager.services;
const { segmentationsWithRepresentations: segmentationsInfo } =
useActiveViewportSegmentationRepresentations({ servicesManager });

View File

@ -1,62 +1,13 @@
import React from 'react';
import {
PanelSegmentation,
useActiveViewportSegmentationRepresentations,
} from '@ohif/extension-cornerstone';
import { Button, Icons } from '@ohif/ui-next';
import { PanelSegmentation } from '@ohif/extension-cornerstone';
import PanelROIThresholdExport from './PanelROIThresholdSegmentation/PanelROIThresholdExport';
export default function PanelTMTV({
servicesManager,
commandsManager,
extensionManager,
configuration,
}: withAppTypes) {
export default function PanelTMTV({ configuration }: withAppTypes) {
return (
<>
<PanelSegmentation
servicesManager={servicesManager}
commandsManager={commandsManager}
extensionManager={extensionManager}
configuration={configuration}
>
<ExportCSV
servicesManager={servicesManager}
commandsManager={commandsManager}
/>
<PanelSegmentation configuration={configuration}>
<PanelROIThresholdExport />
</PanelSegmentation>
</>
);
}
const ExportCSV = ({ servicesManager, commandsManager }: withAppTypes) => {
const { segmentationsWithRepresentations: representations } =
useActiveViewportSegmentationRepresentations({ servicesManager });
const tmtv = representations[0]?.segmentation.cachedStats?.tmtv;
const segmentations = representations.map(representation => representation.segmentation);
if (!segmentations.length) {
return null;
}
return (
<div className="flex h-8 w-full items-center rounded pr-0.5">
<Button
size="sm"
variant="ghost"
className="pl-1.5"
onClick={() => {
commandsManager.runCommand('exportTMTVReportCSV', {
segmentations,
tmtv,
config: {},
});
}}
>
<Icons.Download />
<span className="pl-1">CSV</span>
</Button>
</div>
);
};

View File

@ -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,
},

View File

@ -5,13 +5,7 @@ import PanelTMTV from './Panels/PanelTMTV';
function getPanelModule({ commandsManager, extensionManager, servicesManager }) {
const wrappedPanelPetSuv = () => {
return (
<PanelPetSUV
commandsManager={commandsManager}
servicesManager={servicesManager}
extensionManager={extensionManager}
/>
);
return <PanelPetSUV />;
};
const wrappedROIThresholdToolbox = () => {
@ -24,12 +18,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager })
};
const wrappedROIThresholdExport = () => {
return (
<PanelROIThresholdExport
commandsManager={commandsManager}
servicesManager={servicesManager}
/>
);
return <PanelROIThresholdExport />;
};
const wrappedPanelTMTV = () => {
@ -43,10 +32,6 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager })
commandsManager={commandsManager}
servicesManager={servicesManager}
/>
<PanelROIThresholdExport
commandsManager={commandsManager}
servicesManager={servicesManager}
/>
</>
);
};

View File

@ -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',

View File

@ -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",

View File

@ -15,7 +15,7 @@ function Debug() {
/>
<div className="space-y-2 pt-4 text-center">
<div className="flex flex-col items-center justify-center">
<p className="text-primary-active mt-4 text-xl font-semibold">Debug Information</p>
<p className="text-primary mt-4 text-xl font-semibold">Debug Information</p>
<div className="mt-4 flex items-center space-x-2">
<p className="text-md text-white">Cross Origin Isolated (COOP/COEP)</p>
<Icons.ByName

View File

@ -343,7 +343,7 @@ function WorkList({
<>
<Icons.GroupLayers
className={classnames('mr-2 inline-flex w-4', {
'text-primary-active': isExpanded,
'text-primary': isExpanded,
'text-secondary-light': !isExpanded,
})}
/>

View File

@ -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"

View File

@ -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<string | null>(null);
const [selectedTab, setSelectedTab] = useState<string>('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 <div className="text-red-500">Organ Segmentation data not found.</div>;
}
// 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 (
<div className="w-full">
<div className="mb-4 flex items-center space-x-2">
<div
className="h-2.5 w-2.5 flex-shrink-0 rounded-full"
style={{ backgroundColor: item.colorHex }}
></div>
<h3 className="text-muted-foreground break-words text-lg font-semibold">{item.title}</h3>
</div>
<div className="space-y-1">
<div className="flex justify-between">
<div className="">Centroid X</div>
<div>
<span className="text-white">{stats.centroidX.value}</span>{' '}
<span className="">{stats.centroidX.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Centroid Y</div>
<div>
<span className="text-white">{stats.centroidY.value}</span>{' '}
<span className="">{stats.centroidY.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Centroid Z</div>
<div>
<span className="text-white">{stats.centroidZ.value}</span>{' '}
<span className="">{stats.centroidZ.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Frame Duration</div>
<div>
<span className="text-white">{stats.frameDuration.value}</span>{' '}
<span className="">{stats.frameDuration.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Kurtosis</div>
<div>
<span className="text-white">{stats.kurtosis.value}</span>{' '}
<span className="">{stats.kurtosis.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Max</div>
<div>
<span className="text-white">{stats.max.value}</span>{' '}
<span className="">{stats.max.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Max Slice</div>
<div>
<span className="text-white">{stats.maxSlice.value}</span>{' '}
<span className="">{stats.maxSlice.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Mean</div>
<div>
<span className="text-white">{stats.mean.value}</span>{' '}
<span className="">{stats.mean.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Median</div>
<div>
<span className="text-white">{stats.median.value}</span>{' '}
<span className="">{stats.median.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Min</div>
<div>
<span className="text-white">{stats.min.value}</span>{' '}
<span className="">{stats.min.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Regions</div>
<div>
<span className="text-white">{stats.regions.value}</span>{' '}
<span className="">{stats.regions.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Skewness</div>
<div>
<span className="text-white">{stats.skewness.value}</span>{' '}
<span className="">{stats.skewness.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Sphere Diameter</div>
<div>
<span className="text-white">{stats.sphereDiameter.value}</span>{' '}
<span className="">{stats.sphereDiameter.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Standard Deviation</div>
<div>
<span className="text-white">{stats.standardDeviation.value}</span>{' '}
<span className="">{stats.standardDeviation.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">SUV Peak</div>
<div>
<span className="text-white">{stats.suvPeak.value}</span>{' '}
<span className="">{stats.suvPeak.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Total</div>
<div>
<span className="text-white">{stats.total.value}</span>{' '}
<span className="">{stats.total.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Glycolysis</div>
<div>
<span className="text-white">{stats.glycolysis.value}</span>{' '}
<span className="">{stats.glycolysis.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Volume</div>
<div>
<span className="text-white">{stats.volume.value}</span>{' '}
<span className="">{stats.volume.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Voxel Count</div>
<div>
<span className="text-white">{stats.voxelCount.value}</span>{' '}
<span className="">{stats.voxelCount.unit}</span>
</div>
</div>
</div>
</div>
);
};
return (
<div className="px-auto flex min-h-screen w-full justify-center bg-black py-12">
<div className="w-64 space-y-0">
<TooltipProvider>
<Accordion
type="multiple"
defaultValue={['segmentation-tools', 'segmentation-list']}
>
{/* Segmentation Tools */}
<AccordionItem value="segmentation-tools">
<AccordionTrigger className="bg-popover hover:bg-accent text-muted-foreground my-0.5 flex h-7 w-full items-center justify-between rounded py-2 pr-1 pl-2 font-normal">
<span>Segmentation Tools</span>
</AccordionTrigger>
<AccordionContent>
<div className="bg-muted mb-0.5 h-32 rounded-b pb-3"></div>
</AccordionContent>
</AccordionItem>
{/* Segmentation List */}
<AccordionItem value="segmentation-list">
<AccordionTrigger className="bg-popover hover:bg-accent text-muted-foreground my-0.5 flex h-7 w-full items-center justify-between rounded py-2 pr-1 pl-2 font-normal">
<span>Segmentation List</span>
</AccordionTrigger>
<AccordionContent>
<div className="mb-0">
{/* Header Controls */}
<div className="bg-muted flex h-10 w-full items-center space-x-1 rounded-t px-1.5">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
>
<Icons.More className="h-6 w-6" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem>
<Icons.Add className="text-foreground" />
<span className="pl-2">Create New Segmentation</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel>Manage Current Segmentation</DropdownMenuLabel>
<DropdownMenuItem>
<Icons.Series className="text-foreground" />
<span className="pl-2">Remove from Viewport</span>
</DropdownMenuItem>
<DropdownMenuItem>
<Icons.Rename className="text-foreground" />
<span className="pl-2">Rename</span>
</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<Icons.Export className="text-foreground" />
<span className="pl-2">Export & Download</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuItem>Export DICOM SEG</DropdownMenuItem>
<DropdownMenuItem>Download DICOM SEG</DropdownMenuItem>
<DropdownMenuItem>Download DICOM RTSTRUCT</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Icons.Delete className="text-red-600" />
<span className="pl-2 text-red-600">Delete</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Select>
<SelectTrigger className="w-full overflow-hidden">
<SelectValue placeholder="Segmentation 1" />
</SelectTrigger>
<SelectContent>
<SelectItem value="seg1">Segmentation 1</SelectItem>
<SelectItem value="seg2">Segmentation 2</SelectItem>
<SelectItem value="seg3">Segmentation Long Name 123</SelectItem>
</SelectContent>
</Select>
<Button
variant="ghost"
size="icon"
>
<Icons.Info className="h-6 w-6" />
</Button>
</div>
{/* Appearance Settings */}
<AccordionItem value="segmentation-display">
<AccordionTrigger className="bg-muted hover:bg-accent mt-0.5 flex h-7 w-full items-center justify-between rounded-b pr-1 pl-2 font-normal text-white">
<div className="flex space-x-2">
<Icons.Controls className="text-primary" />
<span className="text-primary pr-1">Appearance Settings</span>
</div>
</AccordionTrigger>
<AccordionContent>
<div className="bg-muted mb-0.5 space-y-2 rounded-b px-1.5 pt-0.5 pb-3">
<div className="mx-1 mb-2.5 mt-1 flex items-center justify-between space-x-4">
{/* Display Label with Selected Tab */}
<div className="text-muted-foreground text-xs">Show: {selectedTab}</div>
{/* Tabs Controls */}
<Tabs
value={selectedTab}
onValueChange={setSelectedTab}
>
<TabsList>
<TabsTrigger value="Fill & Outline">
<Icons.DisplayFillAndOutline className="text-primary" />
</TabsTrigger>
<TabsTrigger value="Outline Only">
<Icons.DisplayOutlineOnly className="text-primary" />
</TabsTrigger>
<TabsTrigger value="Fill Only">
<Icons.DisplayFillOnly className="text-primary" />
</TabsTrigger>
</TabsList>
</Tabs>
</div>
{/* Opacity Slider */}
<div className="my-2 flex items-center">
<Label className="text-muted-foreground mx-1 w-14 flex-none whitespace-nowrap text-xs">
Opacity
</Label>
<Slider
className="mx-1 flex-1"
defaultValue={[85]}
max={100}
step={1}
/>
<Input
className="mx-1 w-10 flex-none"
placeholder="85"
/>
</div>
{/* Border Slider */}
<div className="my-2 flex items-center">
<Label className="text-muted-foreground mx-1 w-14 flex-none whitespace-nowrap text-xs">
Border
</Label>
<Slider
className="mx-1 flex-1"
defaultValue={[10]}
max={100}
step={1}
/>
<Input
className="mx-1 w-10 flex-none"
placeholder="2"
/>
</div>
{/* Sync Changes Switch */}
<div className="my-2 flex items-center pl-1 pb-1">
<Switch defaultChecked />
<Label className="text-muted-foreground mx-2 w-14 flex-none whitespace-nowrap text-xs">
Sync changes in all viewports
</Label>
</div>
<div className="border-input w-full border"></div>
{/* Display Inactive Segmentations Switch */}
<div className="my-2 flex items-center pl-1">
<Switch defaultChecked />
<Label className="text-muted-foreground mx-2 w-14 flex-none whitespace-nowrap text-xs">
Display inactive segmentations
</Label>
</div>
{/* Additional Opacity Slider */}
<div className="my-2 flex items-center">
<Label className="text-muted-foreground mx-1 w-14 flex-none whitespace-nowrap text-xs">
Opacity
</Label>
<Slider
className="mx-1 flex-1"
defaultValue={[65]}
max={100}
step={1}
/>
<Input
className="mx-1 w-10 flex-none"
placeholder="65"
/>
</div>
</div>
</AccordionContent>
</AccordionItem>
{/* Action Buttons */}
<div className="my-px flex h-9 w-full items-center justify-between rounded pl-0.5 pr-7">
<Button
size="sm"
variant="ghost"
className="pr pl-0.5"
>
<Icons.Add />
Add Segment
</Button>
<Button
size="icon"
variant="ghost"
>
<Icons.Hide className="h-6 w-6" />
</Button>
</div>
</div>
{/* Data Rows */}
<div className="space-y-px">
{organSegmentationGroup.items.map((item, index) => {
const compositeId = `${organSegmentationGroup.type}-${item.id}-panel`; // Ensure unique composite ID
return (
<HoverCard
key={`hover-${compositeId}`}
openDelay={300}
closeDelay={200}
// open={true}
>
<HoverCardTrigger asChild>
<div>
<DataRow
key={`panel-${compositeId}`} // Prefix to ensure uniqueness
number={index + 1}
title={item.title}
description={item.description}
colorHex={item.colorHex}
details={item.details || { primary: [], secondary: [] }}
actionOptions={
actionOptionsMap[organSegmentationGroup.type] || ['Action']
}
onAction={(action: string) => 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}
/>
</div>
</HoverCardTrigger>
<HoverCardContent
side="left"
align="start"
className="w-72 border"
>
{renderStatisticsPanel(item)}
</HoverCardContent>
</HoverCard>
);
})}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</TooltipProvider>
</div>
</div>
);
}

View File

@ -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<string | null>(null);
const [selectedTab, setSelectedTab] = useState<string>('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 (
<SegmentationTable.Segments>
<SegmentationTable.SegmentStatistics.Header></SegmentationTable.SegmentStatistics.Header>
<SegmentationTable.SegmentStatistics.Body />
</SegmentationTable.Segments>
);
};
// 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 <div className="text-red-500">Organ Segmentation data not found.</div>;
}
// 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 (
<SegmentationTable.Collapsed>
<SegmentationTable.Collapsed.Header>
<SegmentationTable.Collapsed.DropdownMenu>
{/* <CustomDropdownMenuContent /> */}
</SegmentationTable.Collapsed.DropdownMenu>
<SegmentationTable.Collapsed.Selector />
<SegmentationTable.Collapsed.Info />
</SegmentationTable.Collapsed.Header>
<SegmentationTable.Collapsed.Content>
<SegmentationTable.AddSegmentRow />
{renderSegments()}
</SegmentationTable.Collapsed.Content>
</SegmentationTable.Collapsed>
);
}
const stats = item.statistics;
return (
<div className="w-full">
<div className="mb-4 flex items-center space-x-2">
<div
className="h-2.5 w-2.5 flex-shrink-0 rounded-full"
style={{ backgroundColor: item.colorHex }}
></div>
<h3 className="text-muted-foreground break-words text-lg font-semibold">{item.title}</h3>
</div>
<>
<SegmentationTable.Expanded>
<SegmentationTable.Expanded.Header>
<SegmentationTable.Expanded.DropdownMenu>
{/* <CustomDropdownMenuContent /> */}
</SegmentationTable.Expanded.DropdownMenu>
<SegmentationTable.Expanded.Label />
<SegmentationTable.Expanded.Info />
</SegmentationTable.Expanded.Header>
<div className="space-y-1">
<div className="flex justify-between">
<div className="">Centroid X</div>
<div>
<span className="text-white">{stats.centroidX.value}</span>{' '}
<span className="">{stats.centroidX.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Centroid Y</div>
<div>
<span className="text-white">{stats.centroidY.value}</span>{' '}
<span className="">{stats.centroidY.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Centroid Z</div>
<div>
<span className="text-white">{stats.centroidZ.value}</span>{' '}
<span className="">{stats.centroidZ.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Frame Duration</div>
<div>
<span className="text-white">{stats.frameDuration.value}</span>{' '}
<span className="">{stats.frameDuration.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Kurtosis</div>
<div>
<span className="text-white">{stats.kurtosis.value}</span>{' '}
<span className="">{stats.kurtosis.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Max</div>
<div>
<span className="text-white">{stats.max.value}</span>{' '}
<span className="">{stats.max.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Max Slice</div>
<div>
<span className="text-white">{stats.maxSlice.value}</span>{' '}
<span className="">{stats.maxSlice.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Mean</div>
<div>
<span className="text-white">{stats.mean.value}</span>{' '}
<span className="">{stats.mean.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Median</div>
<div>
<span className="text-white">{stats.median.value}</span>{' '}
<span className="">{stats.median.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Min</div>
<div>
<span className="text-white">{stats.min.value}</span>{' '}
<span className="">{stats.min.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Regions</div>
<div>
<span className="text-white">{stats.regions.value}</span>{' '}
<span className="">{stats.regions.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Skewness</div>
<div>
<span className="text-white">{stats.skewness.value}</span>{' '}
<span className="">{stats.skewness.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Sphere Diameter</div>
<div>
<span className="text-white">{stats.sphereDiameter.value}</span>{' '}
<span className="">{stats.sphereDiameter.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Standard Deviation</div>
<div>
<span className="text-white">{stats.standardDeviation.value}</span>{' '}
<span className="">{stats.standardDeviation.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">SUV Peak</div>
<div>
<span className="text-white">{stats.suvPeak.value}</span>{' '}
<span className="">{stats.suvPeak.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Total</div>
<div>
<span className="text-white">{stats.total.value}</span>{' '}
<span className="">{stats.total.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Glycolysis</div>
<div>
<span className="text-white">{stats.glycolysis.value}</span>{' '}
<span className="">{stats.glycolysis.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Volume</div>
<div>
<span className="text-white">{stats.volume.value}</span>{' '}
<span className="">{stats.volume.unit}</span>
</div>
</div>
<div className="flex justify-between">
<div className="">Voxel Count</div>
<div>
<span className="text-white">{stats.voxelCount.value}</span>{' '}
<span className="">{stats.voxelCount.unit}</span>
</div>
</div>
</div>
</div>
<SegmentationTable.Expanded.Content>
<SegmentationTable.AddSegmentRow />
{renderSegments()}
</SegmentationTable.Expanded.Content>
</SegmentationTable.Expanded>
</>
);
};
return (
<div className="px-auto flex min-h-screen w-full justify-center bg-black py-12">
<div className="w-64 space-y-0">
<TooltipProvider>
<Accordion
type="multiple"
defaultValue={['segmentation-tools', 'segmentation-list']}
>
{/* Segmentation Tools */}
<AccordionItem value="segmentation-tools">
<AccordionTrigger className="bg-popover hover:bg-accent text-muted-foreground my-0.5 flex h-7 w-full items-center justify-between rounded py-2 pr-1 pl-2 font-normal">
<span>Segmentation Tools</span>
</AccordionTrigger>
<AccordionContent>
<div className="bg-muted mb-0.5 h-32 rounded-b pb-3"></div>
</AccordionContent>
</AccordionItem>
{/* Segmentation List */}
<AccordionItem value="segmentation-list">
<AccordionTrigger className="bg-popover hover:bg-accent text-muted-foreground my-0.5 flex h-7 w-full items-center justify-between rounded py-2 pr-1 pl-2 font-normal">
<span>Segmentation List</span>
</AccordionTrigger>
<AccordionContent>
<div className="mb-0">
{/* Header Controls */}
<div className="bg-muted flex h-10 w-full items-center space-x-1 rounded-t px-1.5">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
>
<Icons.More className="h-6 w-6" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem>
<Icons.Add className="text-foreground" />
<span className="pl-2">Create New Segmentation</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel>Manage Current Segmentation</DropdownMenuLabel>
<DropdownMenuItem>
<Icons.Series className="text-foreground" />
<span className="pl-2">Remove from Viewport</span>
</DropdownMenuItem>
<DropdownMenuItem>
<Icons.Rename className="text-foreground" />
<span className="pl-2">Rename</span>
</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<Icons.Export className="text-foreground" />
<span className="pl-2">Export & Download</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuItem>Export DICOM SEG</DropdownMenuItem>
<DropdownMenuItem>Download DICOM SEG</DropdownMenuItem>
<DropdownMenuItem>Download DICOM RTSTRUCT</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Icons.Delete className="text-red-600" />
<span className="pl-2 text-red-600">Delete</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Select>
<SelectTrigger className="w-full overflow-hidden">
<SelectValue placeholder="Segmentation 1" />
</SelectTrigger>
<SelectContent>
<SelectItem value="seg1">Segmentation 1</SelectItem>
<SelectItem value="seg2">Segmentation 2</SelectItem>
<SelectItem value="seg3">Segmentation Long Name 123</SelectItem>
</SelectContent>
</Select>
<Button
variant="ghost"
size="icon"
>
<Icons.Info className="h-6 w-6" />
</Button>
</div>
{/* Appearance Settings */}
<AccordionItem value="segmentation-display">
<AccordionTrigger className="bg-muted hover:bg-accent mt-0.5 flex h-7 w-full items-center justify-between rounded-b pr-1 pl-2 font-normal text-white">
<div className="flex space-x-2">
<Icons.Controls className="text-primary" />
<span className="text-primary pr-1">Appearance Settings</span>
</div>
</AccordionTrigger>
<AccordionContent>
<div className="bg-muted mb-0.5 space-y-2 rounded-b px-1.5 pt-0.5 pb-3">
<div className="mx-1 mb-2.5 mt-1 flex items-center justify-between space-x-4">
{/* Display Label with Selected Tab */}
<div className="text-muted-foreground text-xs">Show: {selectedTab}</div>
{/* Tabs Controls */}
<Tabs
value={selectedTab}
onValueChange={setSelectedTab}
>
<TabsList>
<TabsTrigger value="Fill & Outline">
<Icons.DisplayFillAndOutline className="text-primary" />
</TabsTrigger>
<TabsTrigger value="Outline Only">
<Icons.DisplayOutlineOnly className="text-primary" />
</TabsTrigger>
<TabsTrigger value="Fill Only">
<Icons.DisplayFillOnly className="text-primary" />
</TabsTrigger>
</TabsList>
</Tabs>
</div>
{/* Opacity Slider */}
<div className="my-2 flex items-center">
<Label className="text-muted-foreground mx-1 w-14 flex-none whitespace-nowrap text-xs">
Opacity
</Label>
<Slider
className="mx-1 flex-1"
defaultValue={[85]}
max={100}
step={1}
/>
<Input
className="mx-1 w-10 flex-none"
placeholder="85"
/>
</div>
{/* Border Slider */}
<div className="my-2 flex items-center">
<Label className="text-muted-foreground mx-1 w-14 flex-none whitespace-nowrap text-xs">
Border
</Label>
<Slider
className="mx-1 flex-1"
defaultValue={[10]}
max={100}
step={1}
/>
<Input
className="mx-1 w-10 flex-none"
placeholder="2"
/>
</div>
{/* Sync Changes Switch */}
<div className="my-2 flex items-center pl-1 pb-1">
<Switch defaultChecked />
<Label className="text-muted-foreground mx-2 w-14 flex-none whitespace-nowrap text-xs">
Sync changes in all viewports
</Label>
</div>
<div className="border-input w-full border"></div>
{/* Display Inactive Segmentations Switch */}
<div className="my-2 flex items-center pl-1">
<Switch defaultChecked />
<Label className="text-muted-foreground mx-2 w-14 flex-none whitespace-nowrap text-xs">
Display inactive segmentations
</Label>
</div>
{/* Additional Opacity Slider */}
<div className="my-2 flex items-center">
<Label className="text-muted-foreground mx-1 w-14 flex-none whitespace-nowrap text-xs">
Opacity
</Label>
<Slider
className="mx-1 flex-1"
defaultValue={[65]}
max={100}
step={1}
/>
<Input
className="mx-1 w-10 flex-none"
placeholder="65"
/>
</div>
</div>
</AccordionContent>
</AccordionItem>
{/* Action Buttons */}
<div className="my-px flex h-9 w-full items-center justify-between rounded pl-0.5 pr-7">
<Button
size="sm"
variant="ghost"
className="pr pl-0.5"
>
<Icons.Add />
Add Segment
</Button>
<Button
size="icon"
variant="ghost"
>
<Icons.Hide className="h-6 w-6" />
</Button>
</div>
</div>
{/* Data Rows */}
<div className="space-y-px">
{organSegmentationGroup.items.map((item, index) => {
const compositeId = `${organSegmentationGroup.type}-${item.id}-panel`; // Ensure unique composite ID
return (
<HoverCard
key={`hover-${compositeId}`}
openDelay={300}
closeDelay={200}
// open={true}
>
<HoverCardTrigger asChild>
<div>
<DataRow
key={`panel-${compositeId}`} // Prefix to ensure uniqueness
number={index + 1}
title={item.title}
description={item.description}
colorHex={item.colorHex}
details={item.details || { primary: [], secondary: [] }}
actionOptions={
actionOptionsMap[organSegmentationGroup.type] || ['Action']
}
onAction={(action: string) => 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}
/>
</div>
</HoverCardTrigger>
<HoverCardContent
side="left"
align="start"
className="w-72 border"
>
{renderStatisticsPanel(item)}
</HoverCardContent>
</HoverCard>
);
})}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</TooltipProvider>
</div>
<div className="px-auto flex min-h-screen w-full justify-center border-2 bg-black py-64">
<TooltipProvider>
<SegmentationTable {...tableProps}>
<SegmentationTable.Config />
<SegmentationTable.AddSegmentationRow />
{renderModeContent()}
</SegmentationTable>
</TooltipProvider>
</div>
);
}

View File

@ -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 };

View File

@ -65,7 +65,7 @@ function Header({
onClick={onClickReturn}
data-cy="return-to-work-list"
>
{isReturnEnabled && <Icons.ArrowLeft className="text-primary-active w-8" />}
{isReturnEnabled && <Icons.ArrowLeft className="text-primary w-8" />}
<div className="ml-1">
{WhiteLabeling?.createLogoComponentFn?.(React, props) || <Icons.OHIFLogo />}
</div>
@ -86,7 +86,7 @@ function Header({
<Button
variant="ghost"
size="icon"
className="text-primary-active hover:bg-primary-dark mt-2 h-full w-full"
className="text-primary hover:bg-primary-dark mt-2 h-full w-full"
>
<Icons.GearSettings />
</Button>

View File

@ -27,7 +27,7 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({
onClick={() => !disabled && onSegmentationAdd('')}
>
{children}
<div className="text-primary-active group-hover:bg-secondary-dark flex items-center rounded-[4px] pl-1 group-hover:cursor-pointer">
<div className="text-primary group-hover:bg-secondary-dark flex items-center rounded-[4px] pl-1 group-hover:cursor-pointer">
<div className="grid h-[28px] w-[28px] place-items-center">
{disabled ? <Icons.Info /> : <Icons.Add />}
</div>

View File

@ -17,7 +17,7 @@ const SegmentationExpandedHeader = ({ children }: { children: React.ReactNode })
return (
<PanelSection.Header
className={`border-input border-t-2 bg-transparent pl-1 ${isActive ? 'border-primary' : ''} py-0`}
className={`bg-muted my-0 rounded-none border-l-[2px] pl-0 ${isActive ? 'border-primary/70' : 'border-primary/35'}`}
onClick={e => {
e.stopPropagation();
onSegmentationClick(segmentation.segmentationId);
@ -59,7 +59,7 @@ const SegmentationExpandedInfo = () => {
const { segmentation } = useSegmentationExpanded('SegmentationExpandedInfo');
return (
<div className="ml-auto mr-1">
<div className="ml-auto mr-2">
<Tooltip>
<TooltipTrigger asChild>
<Button
@ -79,8 +79,11 @@ const SegmentationExpandedInfo = () => {
// Content component
const SegmentationExpandedContent = ({ children }: { children: React.ReactNode }) => {
const { isActive } = useSegmentationExpanded('SegmentationExpandedContent');
return (
<PanelSection.Content className="py-0">
<PanelSection.Content
className={`border-l-[2px] py-0 pb-6 pl-[8px] ${isActive ? 'border-primary/70' : 'border-primary/35'}`}
>
<div className="segmentation-expanded-section">{children}</div>
</PanelSection.Content>
);
@ -97,14 +100,14 @@ const SegmentationExpandedRoot = ({ children }) => {
}
return (
<div className="space-y-0">
<div className={`space-y-0 pl-0.5`}>
{data.map(segmentationInfo => {
const isActive = segmentationInfo.segmentation.segmentationId === activeSegmentationId;
return (
<PanelSection
key={segmentationInfo.segmentation.segmentationId}
className="mb-0"
className=""
>
<SegmentationExpandedProvider
segmentation={segmentationInfo.segmentation}

View File

@ -65,42 +65,42 @@ export const SegmentationSegments = ({ children = null }: { children?: React.Rea
const { locked, active, label, displayText } = segmentFromSegmentation;
const cssColor = `rgb(${color[0]},${color[1]},${color[2]})`;
return (
const hasStats = segmentFromSegmentation.cachedStats?.namedStats;
const DataRowComponent = (
<DataRow
key={segmentIndex}
number={segmentIndex}
title={label}
details={displayText}
description={displayText}
colorHex={cssColor}
isSelected={active}
isVisible={visible}
isLocked={locked}
disableEditing={disableEditing}
className={!isActiveSegmentation ? 'opacity-80' : ''}
onColor={() => onSegmentColorClick(segmentation.segmentationId, segmentIndex)}
onToggleVisibility={() =>
onToggleSegmentVisibility(
segmentation.segmentationId,
segmentIndex,
representation.type
)
}
onToggleLocked={() => onToggleSegmentLock(segmentation.segmentationId, segmentIndex)}
onSelect={() => onSegmentClick(segmentation.segmentationId, segmentIndex)}
onRename={() => onSegmentEdit(segmentation.segmentationId, segmentIndex)}
onDelete={() => onSegmentDelete(segmentation.segmentationId, segmentIndex)}
/>
);
return hasStats ? (
<HoverCard
key={`hover-${segmentIndex}`}
openDelay={300}
>
<HoverCardTrigger asChild>
{/* The div here is not random, it is needed for making it work with the hover card */}
<div>
<DataRow
key={segmentIndex}
number={segmentIndex}
title={label}
details={displayText}
description={displayText}
colorHex={cssColor}
isSelected={active}
isVisible={visible}
isLocked={locked}
disableEditing={disableEditing}
className={!isActiveSegmentation ? 'opacity-80' : ''}
onColor={() => onSegmentColorClick(segmentation.segmentationId, segmentIndex)}
onToggleVisibility={() =>
onToggleSegmentVisibility(
segmentation.segmentationId,
segmentIndex,
representation.type
)
}
onToggleLocked={() =>
onToggleSegmentLock(segmentation.segmentationId, segmentIndex)
}
onSelect={() => onSegmentClick(segmentation.segmentationId, segmentIndex)}
onRename={() => onSegmentEdit(segmentation.segmentationId, segmentIndex)}
onDelete={() => onSegmentDelete(segmentation.segmentationId, segmentIndex)}
/>
</div>
<div>{DataRowComponent}</div>
</HoverCardTrigger>
<HoverCardContent
side="left"
@ -126,6 +126,8 @@ export const SegmentationSegments = ({ children = null }: { children?: React.Rea
</SegmentStatistics>
</HoverCardContent>
</HoverCard>
) : (
DataRowComponent
);
})}
</ScrollArea>

View File

@ -112,7 +112,7 @@ export const SegmentationTableRoot = (props: SegmentationTableProps) => {
{hasConfigComponent && (
<div className="ml-auto mr-2">
<Icons.Settings
className="text-primary-active h-4 w-4"
className="text-primary h-4 w-4"
onClick={e => {
e.stopPropagation();
toggleShowConfig();

View File

@ -61,13 +61,13 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
>
<TabsList>
<TabsTrigger value="fill-and-outline">
<Icons.FillAndOutline className="text-primary-active" />
<Icons.FillAndOutline className="text-primary" />
</TabsTrigger>
<TabsTrigger value="outline">
<Icons.OutlineOnly className="text-primary-active" />
<Icons.OutlineOnly className="text-primary" />
</TabsTrigger>
<TabsTrigger value="fill">
<Icons.FillOnly className="text-primary-active" />
<Icons.FillOnly className="text-primary" />
</TabsTrigger>
</TabsList>
</Tabs>

View File

@ -112,7 +112,7 @@ const getTabClassNames = (
isTabDisabled: boolean
) =>
classnames('h-[28px] mb-[2px] cursor-pointer text-white bg-black', {
'hover:text-primary-active': !isActiveTab && !isTabDisabled,
'hover:text-primary': !isActiveTab && !isTabDisabled,
'rounded-l': tabIndex % numColumns === 0,
'rounded-r': (tabIndex + 1) % numColumns === 0 || tabIndex === numTabs - 1,
});
@ -294,7 +294,7 @@ const SidePanel = ({
data-cy={`side-panel-header-${side}`}
>
<Icons.NavigationPanelReveal
className={classnames('text-primary-active', side === 'left' && 'rotate-180 transform')}
className={classnames('text-primary', side === 'left' && 'rotate-180 transform')}
/>
</div>
<div className={classnames('mt-3 flex flex-col space-y-3')}>
@ -304,14 +304,14 @@ const SidePanel = ({
<div
id={`${childComponent.name}-btn`}
data-cy={`${childComponent.name}-btn`}
className="text-primary-active hover:cursor-pointer"
className="text-primary hover:cursor-pointer"
onClick={() => {
return childComponent.disabled ? null : updateActiveTabIndex(index, true);
}}
>
{React.createElement(Icons[childComponent.iconName] || Icons.MissingIcon, {
className: classnames({
'text-primary-active': true,
'text-primary': true,
'ohif-disabled': childComponent.disabled,
}),
style: {
@ -352,7 +352,7 @@ const SidePanel = ({
data-cy={`side-panel-header-${side}`}
>
{React.createElement(Icons[openStateIconName[side]] || Icons.MissingIcon, {
className: 'text-primary-active',
className: 'text-primary',
})}
</div>
);
@ -365,7 +365,7 @@ const SidePanel = ({
<>
{getCloseIcon()}
<div className={classnames('flex grow justify-center')}>
<div className={classnames('bg-primary-dark text-primary-active flex flex-wrap')}>
<div className={classnames('bg-primary-dark text-primary flex flex-wrap')}>
{tabs.map((tab, tabIndex) => {
const { disabled } = tab;
return (
@ -401,7 +401,7 @@ const SidePanel = ({
>
{React.createElement(Icons[tab.iconName] || Icons.MissingIcon, {
className: classnames({
'text-primary-active': true,
'text-primary': true,
'ohif-disabled': disabled,
}),
style: {
@ -429,7 +429,7 @@ const SidePanel = ({
return (
<div
className={classnames(
'text-primary-active flex grow cursor-pointer select-none justify-center self-center text-[13px]'
'text-primary flex grow cursor-pointer select-none justify-center self-center text-[13px]'
)}
data-cy={`${tabs[0].name}-btn`}
onClick={() => updatePanelOpen(!panelOpen)}

View File

@ -111,7 +111,7 @@ const Thumbnail = ({
<TooltipContent side="right">
<div className="flex flex-1 flex-row">
<div className="flex-2 flex items-center justify-center pr-4">
<Icons.InfoLink className="text-primary-active" />
<Icons.InfoLink className="text-primary" />
</div>
<div className="flex flex-1 flex-col">
<span>
@ -226,7 +226,7 @@ const Thumbnail = ({
<TooltipContent side="right">
<div className="flex flex-1 flex-row">
<div className="flex-2 flex items-center justify-center pr-4">
<Icons.InfoLink className="text-primary-active" />
<Icons.InfoLink className="text-primary" />
</div>
<div className="flex flex-1 flex-col">
<span>

View File

@ -1,6 +1,6 @@
const getToggledClassName = isToggled => {
return isToggled
? '!text-primary-active'
? '!text-primary'
: '!text-common-bright hover:!bg-primary-dark hover:text-primary-light';
};

View File

@ -0,0 +1,42 @@
import { test } from '@playwright/test';
import { visitStudy, checkForScreenshot, screenShotPaths } from './utils';
test.beforeEach(async ({ page }) => {
const studyInstanceUID = '1.3.12.2.1107.5.2.32.35162.30000015050317233592200000046';
const mode = 'viewer';
await visitStudy(page, studyInstanceUID, mode, 2000);
});
test('should properly display MPR for MR', async ({ page }) => {
await page.getByTestId('side-panel-header-right').click();
await page.getByTestId('Layout').click();
await page.getByTestId('MPR').click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydrationFromMPR.mprBeforeSEG);
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydrationFromMPR.mprAfterSEG);
await page.getByTestId('yes-hydrate-btn').click();
await page.waitForTimeout(5000);
await checkForScreenshot(page, page, screenShotPaths.segHydrationFromMPR.mprAfterSegHydrated);
await page.getByTestId('Layout').click();
await page.getByTestId('Axial Primary').click();
await page.waitForTimeout(5000);
await checkForScreenshot(
page,
page,
screenShotPaths.segHydrationFromMPR.mprAfterSegHydratedAfterLayoutChange
);
});

View File

@ -12,7 +12,7 @@ test('should properly display MPR for MR', async ({ page }) => {
await page.getByTestId('study-browser-thumbnail-no-image').dblclick();
await page.getByTestId('yes-hydrate-btn').click();
await checkForScreenshot(page, page, screenShotPaths.segHydrationMPR.segPostHydration);
await checkForScreenshot(page, page, screenShotPaths.segHydrationThenMPR.segPostHydration);
await page.getByTestId('Layout').click();
await page.getByTestId('Axial Primary').click();
@ -20,6 +20,6 @@ test('should properly display MPR for MR', async ({ page }) => {
await checkForScreenshot(
page,
page,
screenShotPaths.segHydrationMPR.segPostHydrationMPRAxialPrimary
screenShotPaths.segHydrationThenMPR.segPostHydrationMPRAxialPrimary
);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

View File

@ -79,10 +79,16 @@ const screenShotPaths = {
segPreHydration: 'segPreHydration.png',
segJumpToSegment: 'segJumpToSegment.png',
},
segHydrationMPR: {
segHydrationThenMPR: {
segPostHydration: 'segPostHydration.png',
segPostHydrationMPRAxialPrimary: 'segPostHydrationMPRAxialPrimary.png',
},
segHydrationFromMPR: {
mprBeforeSEG: 'mprBeforeSEG.png',
mprAfterSEG: 'mprAfterSEG.png',
mprAfterSegHydrated: 'mprAfterSegHydrated.png',
mprAfterSegHydratedAfterLayoutChange: 'mprAfterSegHydratedAfterLayoutChange.png',
},
rtHydration: {
rtPostHydration: 'rtPostHydration.png',
rtPreHydration: 'rtPreHydration.png',

View File

@ -1393,10 +1393,10 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
"@cornerstonejs/adapters@^3.8.4":
version "3.8.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-3.8.4.tgz#14e22bef3abaf14cde52ecf8b05b43a687b0dece"
integrity sha512-TFTvNZQBW54rEcyjDfyHZYYvIPHVGZoUCtjW3isHfpoXkjHz2cliwC55e0kcNJ0zbyI6H41c4VsYH8h2MTYOdA==
"@cornerstonejs/adapters@^3.8.5":
version "3.8.5"
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-3.8.5.tgz#d516a8fba29d200b47d77fe0ef586ed8962506de"
integrity sha512-dQlKKSCbV6V3QpivDdZZiI7N792l1VqsOj8H+e8iiarpy6MvwiEvck2u50/GBhto9D/U9QvRkZVs9TH0ewUs5w==
dependencies:
"@babel/runtime-corejs2" "^7.17.8"
buffer "^6.0.3"
@ -1404,10 +1404,10 @@
gl-matrix "^3.4.3"
ndarray "^1.0.19"
"@cornerstonejs/ai@^3.8.4":
version "3.8.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/ai/-/ai-3.8.4.tgz#c998cdfbd1cae01e3ea9ed61810c657d91112f75"
integrity sha512-scc2srdL1BqNTSFEro4y7UlNBsPtBVSF31eT4SC1psxZQcr/iyaCKuOQg/hglk1yobFkdIKvjRAvvqvYmGduQw==
"@cornerstonejs/ai@^3.8.5":
version "3.8.5"
resolved "https://registry.yarnpkg.com/@cornerstonejs/ai/-/ai-3.8.5.tgz#8f6be57b7bc660afacd52b34ce56a71c696419ed"
integrity sha512-9QR7PmTtkeXZOjDFh9WulLJGnraWLaM/g/qNB0ZaPKDMo1PLT7/tHn1F6wsyT2cmpEb3/G5towdxtEvhvlpm/w==
dependencies:
"@babel/runtime-corejs2" "^7.17.8"
buffer "^6.0.3"
@ -1443,20 +1443,20 @@
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0"
integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg==
"@cornerstonejs/core@^3.8.4":
version "3.8.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-3.8.4.tgz#bbc6863cb3f2e78f5f4c7a1a5a9287c091287228"
integrity sha512-8spQvERZRn4I/0EhzTNZ5ruPBrVn5F4msb/q0ttBtiVEa9JaVwoWh3yEcO4q5NpK7t1BhpfSPMKwU98Dy02ugQ==
"@cornerstonejs/core@^3.8.5":
version "3.8.5"
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-3.8.5.tgz#577bd32f0a3da36f895bf89c821b02a4cf97e225"
integrity sha512-x3LZwZUqhVldChpwtLqNwUWx8eXq9q1ndwrx5lVpnX4ZUT9qQIAro01Ih8DPiXlLb1NXbN3sG4e/x/TxuQ+5bw==
dependencies:
"@kitware/vtk.js" "32.12.1"
comlink "^4.4.1"
gl-matrix "^3.4.3"
loglevel "^1.9.2"
"@cornerstonejs/dicom-image-loader@^3.8.4":
version "3.8.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-3.8.4.tgz#c4f7af80a40a1f70deff37ddfc50447cc42422e7"
integrity sha512-N2D4U8JDhen+dCY1HrLOoK8W3CD2AAf/Cd+GiJMEGjtrihIuQ+Ha+hZTVQzaDMc7v2Ruvx2DpJ7i9HUJXoyWHw==
"@cornerstonejs/dicom-image-loader@^3.8.5":
version "3.8.5"
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-3.8.5.tgz#963a3174af3603e4beb096d448671442a0d5f10a"
integrity sha512-yuLLU2sbtFWueNKVbeYBddPoJnfhrUQ+rRnXJb+y9iczwZMQLy4tYyk3Q5INo+/nIdwCVeZg7P9qORZERsBivw==
dependencies:
"@cornerstonejs/codec-charls" "^1.2.3"
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
@ -1468,25 +1468,25 @@
pako "^2.0.4"
uuid "^9.0.0"
"@cornerstonejs/labelmap-interpolation@^3.8.4":
version "3.8.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/labelmap-interpolation/-/labelmap-interpolation-3.8.4.tgz#5c18761a7bbb8e3e76bfbf22b2205c1c5e8bee6e"
integrity sha512-KKDTeVJg1s0trplkaHglBm0foQ3XL9BhsTfatFCTzgIOCT0wolrKbr+xAKIeZuBQifiFazDWpYaeBHRbkWEEMA==
"@cornerstonejs/labelmap-interpolation@^3.8.5":
version "3.8.5"
resolved "https://registry.yarnpkg.com/@cornerstonejs/labelmap-interpolation/-/labelmap-interpolation-3.8.5.tgz#19e8142362d5b83540bcb764306cd6bdf277c9ce"
integrity sha512-u80OTJE8eju+tZ+S/N1b/HTIw2mPjEE2o5u3T/7/w90FX7OaxJJiMYjPPfnSZWXjE2HhRu0gEAKHOU3dr8pL4w==
dependencies:
"@itk-wasm/morphological-contour-interpolation" "1.1.0"
itk-wasm "1.0.0-b.165"
"@cornerstonejs/polymorphic-segmentation@^3.8.4":
version "3.8.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/polymorphic-segmentation/-/polymorphic-segmentation-3.8.4.tgz#5c7578eedd4e7ac78250f06ccecb3950ddf5ba28"
integrity sha512-+3o280NOniUkG9GcWTeTi6x3qy8Rm7WpYFycWpxXIufJzVudJi0zAR+EIWDq/uvTQ3DH1SrBDe4wzYNqD+yirw==
"@cornerstonejs/polymorphic-segmentation@^3.8.5":
version "3.8.5"
resolved "https://registry.yarnpkg.com/@cornerstonejs/polymorphic-segmentation/-/polymorphic-segmentation-3.8.5.tgz#5289f1b9d7f8fdbf3164330c4d30dd03fffa16be"
integrity sha512-6L9a9f36CnUIuGsydA8kkkC0z1Gjid4KfclCQoYYaxcsLWLDpba5usFERmxCLIm9VyBsA3POqesIGDqWTgJmFg==
dependencies:
"@icr/polyseg-wasm" "0.4.0"
"@cornerstonejs/tools@^3.8.4":
version "3.8.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-3.8.4.tgz#4676ba9d7fcae22f236bab2314118b5d06cc973e"
integrity sha512-yr8RLtnxGh9BsPOFpX+zwT++UGJDqWMo7noWGzxkuISDriLr+HGZI8gL910Mw62obaF8r9qpeOSvSOzEHN3nSw==
"@cornerstonejs/tools@^3.8.5":
version "3.8.5"
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-3.8.5.tgz#15fe20fa051dc12084141515c2a715a4f0a9a858"
integrity sha512-aTaziRHJakCCxvbHhbrSkEuiamURGakcK83KXGJPjSdStWC/2cVpkCzPxc8V7FZBIstsvo5pgciXSpNc6Yt7Og==
dependencies:
"@types/offscreencanvas" "2019.7.3"
comlink "^4.4.1"