feat(image capture): Enhance SEG and RTSS Support with Dependency Updates and Bug Fixes (#4924)
276
bun.lock
@ -33,7 +33,7 @@
|
|||||||
},
|
},
|
||||||
"addOns/externals/devDependencies": {
|
"addOns/externals/devDependencies": {
|
||||||
"name": "@externals/devDependencies",
|
"name": "@externals/devDependencies",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@kitware/vtk.js": "32.12.0",
|
"@kitware/vtk.js": "32.12.0",
|
||||||
@ -121,22 +121,22 @@
|
|||||||
},
|
},
|
||||||
"addOns/externals/dicom-microscopy-viewer": {
|
"addOns/externals/dicom-microscopy-viewer": {
|
||||||
"name": "@externals/dicom-microscopy-viewer",
|
"name": "@externals/dicom-microscopy-viewer",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dicom-microscopy-viewer": "^0.46.1",
|
"dicom-microscopy-viewer": "^0.46.1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"extensions/cornerstone": {
|
"extensions/cornerstone": {
|
||||||
"name": "@ohif/extension-cornerstone",
|
"name": "@ohif/extension-cornerstone",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^3.8.4",
|
"@cornerstonejs/adapters": "^3.8.5",
|
||||||
"@cornerstonejs/ai": "^3.8.4",
|
"@cornerstonejs/ai": "^3.8.5",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@cornerstonejs/labelmap-interpolation": "^3.8.4",
|
"@cornerstonejs/labelmap-interpolation": "^3.8.5",
|
||||||
"@cornerstonejs/polymorphic-segmentation": "^3.8.4",
|
"@cornerstonejs/polymorphic-segmentation": "^3.8.5",
|
||||||
"@cornerstonejs/tools": "^3.8.4",
|
"@cornerstonejs/tools": "^3.8.5",
|
||||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||||
"@kitware/vtk.js": "32.12.0",
|
"@kitware/vtk.js": "32.12.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
@ -153,9 +153,9 @@
|
|||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
"@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.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/ui": "3.10.0-beta.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"dcmjs": "*",
|
"dcmjs": "*",
|
||||||
"dicom-parser": "^1.8.21",
|
"dicom-parser": "^1.8.21",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
@ -167,19 +167,19 @@
|
|||||||
},
|
},
|
||||||
"extensions/cornerstone-dicom-pmap": {
|
"extensions/cornerstone-dicom-pmap": {
|
||||||
"name": "@ohif/extension-cornerstone-dicom-pmap",
|
"name": "@ohif/extension-cornerstone-dicom-pmap",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^3.8.4",
|
"@cornerstonejs/adapters": "^3.8.5",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@kitware/vtk.js": "32.12.0",
|
"@kitware/vtk.js": "32.12.0",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/i18n": "3.10.0-beta.144",
|
"@ohif/i18n": "3.10.0-beta.147",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@ -190,16 +190,16 @@
|
|||||||
},
|
},
|
||||||
"extensions/cornerstone-dicom-rt": {
|
"extensions/cornerstone-dicom-rt": {
|
||||||
"name": "@ohif/extension-cornerstone-dicom-rt",
|
"name": "@ohif/extension-cornerstone-dicom-rt",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/i18n": "3.10.0-beta.144",
|
"@ohif/i18n": "3.10.0-beta.147",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@ -210,19 +210,19 @@
|
|||||||
},
|
},
|
||||||
"extensions/cornerstone-dicom-seg": {
|
"extensions/cornerstone-dicom-seg": {
|
||||||
"name": "@ohif/extension-cornerstone-dicom-seg",
|
"name": "@ohif/extension-cornerstone-dicom-seg",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^3.8.4",
|
"@cornerstonejs/adapters": "^3.8.5",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@kitware/vtk.js": "32.12.0",
|
"@kitware/vtk.js": "32.12.0",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/i18n": "3.10.0-beta.144",
|
"@ohif/i18n": "3.10.0-beta.147",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@ -233,19 +233,19 @@
|
|||||||
},
|
},
|
||||||
"extensions/cornerstone-dicom-sr": {
|
"extensions/cornerstone-dicom-sr": {
|
||||||
"name": "@ohif/extension-cornerstone-dicom-sr",
|
"name": "@ohif/extension-cornerstone-dicom-sr",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^3.8.4",
|
"@cornerstonejs/adapters": "^3.8.5",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@cornerstonejs/tools": "^3.8.4",
|
"@cornerstonejs/tools": "^3.8.5",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone": "3.10.0-beta.147",
|
||||||
"@ohif/extension-measurement-tracking": "3.10.0-beta.144",
|
"@ohif/extension-measurement-tracking": "3.10.0-beta.147",
|
||||||
"@ohif/ui": "3.10.0-beta.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"dcmjs": "*",
|
"dcmjs": "*",
|
||||||
"dicom-parser": "^1.8.9",
|
"dicom-parser": "^1.8.9",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
@ -255,19 +255,19 @@
|
|||||||
},
|
},
|
||||||
"extensions/cornerstone-dynamic-volume": {
|
"extensions/cornerstone-dynamic-volume": {
|
||||||
"name": "@ohif/extension-cornerstone-dynamic-volume",
|
"name": "@ohif/extension-cornerstone-dynamic-volume",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@cornerstonejs/tools": "^3.8.4",
|
"@cornerstonejs/tools": "^3.8.5",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/i18n": "3.10.0-beta.144",
|
"@ohif/i18n": "3.10.0-beta.147",
|
||||||
"@ohif/ui": "3.10.0-beta.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"dcmjs": "*",
|
"dcmjs": "*",
|
||||||
"dicom-parser": "^1.8.21",
|
"dicom-parser": "^1.8.21",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
@ -277,7 +277,7 @@
|
|||||||
},
|
},
|
||||||
"extensions/default": {
|
"extensions/default": {
|
||||||
"name": "@ohif/extension-default",
|
"name": "@ohif/extension-default",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/calculate-suv": "^1.1.0",
|
"@cornerstonejs/calculate-suv": "^1.1.0",
|
||||||
@ -285,8 +285,8 @@
|
|||||||
"lodash.uniqby": "^4.7.0",
|
"lodash.uniqby": "^4.7.0",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/i18n": "3.10.0-beta.144",
|
"@ohif/i18n": "3.10.0-beta.147",
|
||||||
"dcmjs": "*",
|
"dcmjs": "*",
|
||||||
"dicomweb-client": "^0.10.4",
|
"dicomweb-client": "^0.10.4",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
@ -300,7 +300,7 @@
|
|||||||
},
|
},
|
||||||
"extensions/dicom-microscopy": {
|
"extensions/dicom-microscopy": {
|
||||||
"name": "@ohif/extension-dicom-microscopy",
|
"name": "@ohif/extension-dicom-microscopy",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/codec-charls": "^1.2.3",
|
"@cornerstonejs/codec-charls": "^1.2.3",
|
||||||
@ -311,10 +311,10 @@
|
|||||||
"mathjs": "^12.4.2",
|
"mathjs": "^12.4.2",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/i18n": "3.10.0-beta.144",
|
"@ohif/i18n": "3.10.0-beta.147",
|
||||||
"@ohif/ui": "3.10.0-beta.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@ -325,14 +325,14 @@
|
|||||||
},
|
},
|
||||||
"extensions/dicom-pdf": {
|
"extensions/dicom-pdf": {
|
||||||
"name": "@ohif/extension-dicom-pdf",
|
"name": "@ohif/extension-dicom-pdf",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/ui": "3.10.0-beta.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"dcmjs": "*",
|
"dcmjs": "*",
|
||||||
"dicom-parser": "^1.8.9",
|
"dicom-parser": "^1.8.9",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
@ -342,14 +342,14 @@
|
|||||||
},
|
},
|
||||||
"extensions/dicom-video": {
|
"extensions/dicom-video": {
|
||||||
"name": "@ohif/extension-dicom-video",
|
"name": "@ohif/extension-dicom-video",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/ui": "3.10.0-beta.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"dcmjs": "*",
|
"dcmjs": "*",
|
||||||
"dicom-parser": "^1.8.9",
|
"dicom-parser": "^1.8.9",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
@ -359,20 +359,20 @@
|
|||||||
},
|
},
|
||||||
"extensions/measurement-tracking": {
|
"extensions/measurement-tracking": {
|
||||||
"name": "@ohif/extension-measurement-tracking",
|
"name": "@ohif/extension-measurement-tracking",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@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/react": "^3.2.2",
|
||||||
"xstate": "^4.10.0",
|
"xstate": "^4.10.0",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@cornerstonejs/tools": "^3.8.4",
|
"@cornerstonejs/tools": "^3.8.5",
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/ui": "3.10.0-beta.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"dcmjs": "*",
|
"dcmjs": "*",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
@ -385,14 +385,14 @@
|
|||||||
},
|
},
|
||||||
"extensions/test-extension": {
|
"extensions/test-extension": {
|
||||||
"name": "@ohif/extension-test",
|
"name": "@ohif/extension-test",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/ui": "3.10.0-beta.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"dcmjs": "0.38.0",
|
"dcmjs": "0.38.0",
|
||||||
"dicom-parser": "^1.8.9",
|
"dicom-parser": "^1.8.9",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
@ -402,7 +402,7 @@
|
|||||||
},
|
},
|
||||||
"extensions/tmtv": {
|
"extensions/tmtv": {
|
||||||
"name": "@ohif/extension-tmtv",
|
"name": "@ohif/extension-tmtv",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
@ -419,7 +419,7 @@
|
|||||||
},
|
},
|
||||||
"modes/basic-dev-mode": {
|
"modes/basic-dev-mode": {
|
||||||
"name": "@ohif/mode-basic-dev-mode",
|
"name": "@ohif/mode-basic-dev-mode",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
@ -429,17 +429,17 @@
|
|||||||
"webpack-merge": "^5.7.3",
|
"webpack-merge": "^5.7.3",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-pdf": "3.10.0-beta.144",
|
"@ohif/extension-dicom-pdf": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-video": "3.10.0-beta.144",
|
"@ohif/extension-dicom-video": "3.10.0-beta.147",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"modes/basic-test-mode": {
|
"modes/basic-test-mode": {
|
||||||
"name": "@ohif/mode-test",
|
"name": "@ohif/mode-test",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
@ -449,19 +449,19 @@
|
|||||||
"webpack-merge": "^5.7.3",
|
"webpack-merge": "^5.7.3",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-pdf": "3.10.0-beta.144",
|
"@ohif/extension-dicom-pdf": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-video": "3.10.0-beta.144",
|
"@ohif/extension-dicom-video": "3.10.0-beta.147",
|
||||||
"@ohif/extension-measurement-tracking": "3.10.0-beta.144",
|
"@ohif/extension-measurement-tracking": "3.10.0-beta.147",
|
||||||
"@ohif/extension-test": "3.10.0-beta.144",
|
"@ohif/extension-test": "3.10.0-beta.147",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"modes/longitudinal": {
|
"modes/longitudinal": {
|
||||||
"name": "@ohif/mode-longitudinal",
|
"name": "@ohif/mode-longitudinal",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
@ -471,32 +471,32 @@
|
|||||||
"webpack-merge": "^5.7.3",
|
"webpack-merge": "^5.7.3",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-pdf": "3.10.0-beta.144",
|
"@ohif/extension-dicom-pdf": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-video": "3.10.0-beta.144",
|
"@ohif/extension-dicom-video": "3.10.0-beta.147",
|
||||||
"@ohif/extension-measurement-tracking": "3.10.0-beta.144",
|
"@ohif/extension-measurement-tracking": "3.10.0-beta.147",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"modes/microscopy": {
|
"modes/microscopy": {
|
||||||
"name": "@ohif/mode-microscopy",
|
"name": "@ohif/mode-microscopy",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-microscopy": "3.10.0-beta.144",
|
"@ohif/extension-dicom-microscopy": "3.10.0-beta.147",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"modes/preclinical-4d": {
|
"modes/preclinical-4d": {
|
||||||
"name": "@ohif/mode-preclinical-4d",
|
"name": "@ohif/mode-preclinical-4d",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
},
|
},
|
||||||
@ -515,7 +515,7 @@
|
|||||||
},
|
},
|
||||||
"modes/segmentation": {
|
"modes/segmentation": {
|
||||||
"name": "@ohif/mode-segmentation",
|
"name": "@ohif/mode-segmentation",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
@ -559,7 +559,7 @@
|
|||||||
},
|
},
|
||||||
"modes/tmtv": {
|
"modes/tmtv": {
|
||||||
"name": "@ohif/mode-tmtv",
|
"name": "@ohif/mode-tmtv",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
@ -580,32 +580,32 @@
|
|||||||
},
|
},
|
||||||
"platform/app": {
|
"platform/app": {
|
||||||
"name": "@ohif/app",
|
"name": "@ohif/app",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/codec-charls": "^1.2.3",
|
"@cornerstonejs/codec-charls": "^1.2.3",
|
||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
"@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",
|
"@emotion/serialize": "^1.1.3",
|
||||||
"@ohif/core": "3.10.0-beta.144",
|
"@ohif/core": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.147",
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.144",
|
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.147",
|
||||||
"@ohif/extension-default": "3.10.0-beta.144",
|
"@ohif/extension-default": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-microscopy": "3.10.0-beta.144",
|
"@ohif/extension-dicom-microscopy": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-pdf": "3.10.0-beta.144",
|
"@ohif/extension-dicom-pdf": "3.10.0-beta.147",
|
||||||
"@ohif/extension-dicom-video": "3.10.0-beta.144",
|
"@ohif/extension-dicom-video": "3.10.0-beta.147",
|
||||||
"@ohif/extension-test": "3.10.0-beta.144",
|
"@ohif/extension-test": "3.10.0-beta.147",
|
||||||
"@ohif/i18n": "3.10.0-beta.144",
|
"@ohif/i18n": "3.10.0-beta.147",
|
||||||
"@ohif/mode-basic-dev-mode": "3.10.0-beta.144",
|
"@ohif/mode-basic-dev-mode": "3.10.0-beta.147",
|
||||||
"@ohif/mode-longitudinal": "3.10.0-beta.144",
|
"@ohif/mode-longitudinal": "3.10.0-beta.147",
|
||||||
"@ohif/mode-microscopy": "3.10.0-beta.144",
|
"@ohif/mode-microscopy": "3.10.0-beta.147",
|
||||||
"@ohif/mode-test": "3.10.0-beta.144",
|
"@ohif/mode-test": "3.10.0-beta.147",
|
||||||
"@ohif/ui": "3.10.0-beta.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"@ohif/ui-next": "3.10.0-beta.144",
|
"@ohif/ui-next": "3.10.0-beta.147",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
@ -646,7 +646,7 @@
|
|||||||
},
|
},
|
||||||
"platform/cli": {
|
"platform/cli": {
|
||||||
"name": "@ohif/cli",
|
"name": "@ohif/cli",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"bin": {
|
"bin": {
|
||||||
"ohif-cli": "src/index.js",
|
"ohif-cli": "src/index.js",
|
||||||
},
|
},
|
||||||
@ -670,7 +670,7 @@
|
|||||||
},
|
},
|
||||||
"platform/core": {
|
"platform/core": {
|
||||||
"name": "@ohif/core",
|
"name": "@ohif/core",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"dcmjs": "*",
|
"dcmjs": "*",
|
||||||
@ -695,15 +695,15 @@
|
|||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
"@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.144",
|
"@ohif/ui": "3.10.0-beta.147",
|
||||||
"cornerstone-math": "0.1.9",
|
"cornerstone-math": "0.1.9",
|
||||||
"dicom-parser": "^1.8.21",
|
"dicom-parser": "^1.8.21",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"platform/i18n": {
|
"platform/i18n": {
|
||||||
"name": "@ohif/i18n",
|
"name": "@ohif/i18n",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"i18next-locize-backend": "^2.0.0",
|
"i18next-locize-backend": "^2.0.0",
|
||||||
@ -728,7 +728,7 @@
|
|||||||
},
|
},
|
||||||
"platform/ui": {
|
"platform/ui": {
|
||||||
"name": "@ohif/ui",
|
"name": "@ohif/ui",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@testing-library/react": "^13.1.0",
|
"@testing-library/react": "^13.1.0",
|
||||||
"browser-detect": "^0.2.28",
|
"browser-detect": "^0.2.28",
|
||||||
@ -788,7 +788,7 @@
|
|||||||
},
|
},
|
||||||
"platform/ui-next": {
|
"platform/ui-next": {
|
||||||
"name": "@ohif/ui-next",
|
"name": "@ohif/ui-next",
|
||||||
"version": "3.10.0-beta.144",
|
"version": "3.10.0-beta.147",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-accordion": "^1.2.0",
|
"@radix-ui/react-accordion": "^1.2.0",
|
||||||
"@radix-ui/react-checkbox": "^1.1.1",
|
"@radix-ui/react-checkbox": "^1.1.1",
|
||||||
@ -1099,9 +1099,9 @@
|
|||||||
|
|
||||||
"@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="],
|
"@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=="],
|
"@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/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=="],
|
"@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=="],
|
||||||
|
|
||||||
|
|||||||
@ -46,8 +46,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^3.8.4",
|
"@cornerstonejs/adapters": "^3.8.5",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@kitware/vtk.js": "32.12.0",
|
"@kitware/vtk.js": "32.12.0",
|
||||||
"react-color": "^2.19.3"
|
"react-color": "^2.19.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,8 +46,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^3.8.4",
|
"@cornerstonejs/adapters": "^3.8.5",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@kitware/vtk.js": "32.12.0",
|
"@kitware/vtk.js": "32.12.0",
|
||||||
"react-color": "^2.19.3"
|
"react-color": "^2.19.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -305,9 +305,6 @@ const commandsModule = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const definitions = {
|
const definitions = {
|
||||||
loadSegmentationDisplaySetsForViewport: {
|
|
||||||
commandFn: actions.loadSegmentationDisplaySetsForViewport,
|
|
||||||
},
|
|
||||||
loadSegmentationsForViewport: {
|
loadSegmentationsForViewport: {
|
||||||
commandFn: actions.loadSegmentationsForViewport,
|
commandFn: actions.loadSegmentationsForViewport,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -47,7 +47,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
|
|||||||
const [viewportGrid, viewportGridService] = useViewportGrid();
|
const [viewportGrid, viewportGridService] = useViewportGrid();
|
||||||
|
|
||||||
// States
|
// States
|
||||||
let selectedSegmentObjectIndex: number = 0;
|
const selectedSegmentObjectIndex: number = 0;
|
||||||
const { setPositionPresentation } = usePositionPresentationStore();
|
const { setPositionPresentation } = usePositionPresentationStore();
|
||||||
|
|
||||||
// Hydration means that the SEG is opened and segments are loaded into the
|
// Hydration means that the SEG is opened and segments are loaded into the
|
||||||
@ -160,7 +160,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
|
|||||||
displaySetInstanceUID: referencedDisplaySet.displaySetInstanceUID,
|
displaySetInstanceUID: referencedDisplaySet.displaySetInstanceUID,
|
||||||
});
|
});
|
||||||
|
|
||||||
viewportGridService.setDisplaySetsForViewport({
|
commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', {
|
||||||
viewportId,
|
viewportId,
|
||||||
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
|
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
|
||||||
});
|
});
|
||||||
|
|||||||
@ -46,9 +46,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^3.8.4",
|
"@cornerstonejs/adapters": "^3.8.5",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@cornerstonejs/tools": "^3.8.4",
|
"@cornerstonejs/tools": "^3.8.5",
|
||||||
"classnames": "^2.3.2"
|
"classnames": "^2.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,8 +42,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@cornerstonejs/tools": "^3.8.4",
|
"@cornerstonejs/tools": "^3.8.5",
|
||||||
"classnames": "^2.3.2"
|
"classnames": "^2.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
"@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/core": "3.10.0-beta.148",
|
||||||
"@ohif/ui": "3.10.0-beta.148",
|
"@ohif/ui": "3.10.0-beta.148",
|
||||||
"dcmjs": "*",
|
"dcmjs": "*",
|
||||||
@ -51,12 +51,12 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^3.8.4",
|
"@cornerstonejs/adapters": "^3.8.5",
|
||||||
"@cornerstonejs/ai": "^3.8.4",
|
"@cornerstonejs/ai": "^3.8.5",
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@cornerstonejs/labelmap-interpolation": "^3.8.4",
|
"@cornerstonejs/labelmap-interpolation": "^3.8.5",
|
||||||
"@cornerstonejs/polymorphic-segmentation": "^3.8.4",
|
"@cornerstonejs/polymorphic-segmentation": "^3.8.5",
|
||||||
"@cornerstonejs/tools": "^3.8.4",
|
"@cornerstonejs/tools": "^3.8.5",
|
||||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||||
"@kitware/vtk.js": "32.12.0",
|
"@kitware/vtk.js": "32.12.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import { toolNames } from './initCornerstoneTools';
|
|||||||
import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm';
|
import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm';
|
||||||
import { updateSegmentBidirectionalStats } from './utils/updateSegmentationStats';
|
import { updateSegmentBidirectionalStats } from './utils/updateSegmentationStats';
|
||||||
import { generateSegmentationCSVReport } from './utils/generateSegmentationCSVReport';
|
import { generateSegmentationCSVReport } from './utils/generateSegmentationCSVReport';
|
||||||
|
import { getUpdatedViewportsForSegmentation } from './utils/hydrationUtils';
|
||||||
|
|
||||||
const { DefaultHistoryMemo } = csUtils.HistoryMemo;
|
const { DefaultHistoryMemo } = csUtils.HistoryMemo;
|
||||||
const toggleSyncFunctions = {
|
const toggleSyncFunctions = {
|
||||||
@ -495,7 +496,6 @@ function commandsModule({
|
|||||||
|
|
||||||
downloadCSVSegmentationReport: ({ segmentationId }) => {
|
downloadCSVSegmentationReport: ({ segmentationId }) => {
|
||||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||||
const cachedStats = segmentation.cachedStats;
|
|
||||||
|
|
||||||
const { representationData } = segmentation;
|
const { representationData } = segmentation;
|
||||||
const { Labelmap } = representationData;
|
const { Labelmap } = representationData;
|
||||||
@ -1021,6 +1021,10 @@ function commandsModule({
|
|||||||
|
|
||||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||||
|
|
||||||
|
if (!toolGroup.hasTool(toolName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const prevConfig = toolGroup?.getToolConfiguration(toolName);
|
const prevConfig = toolGroup?.getToolConfiguration(toolName);
|
||||||
toolGroup?.setToolConfiguration(
|
toolGroup?.setToolConfiguration(
|
||||||
toolName,
|
toolName,
|
||||||
@ -1539,7 +1543,8 @@ function commandsModule({
|
|||||||
DefaultHistoryMemo.redo();
|
DefaultHistoryMemo.redo();
|
||||||
},
|
},
|
||||||
toggleSegmentPreviewEdit: ({ toggle }) => {
|
toggleSegmentPreviewEdit: ({ toggle }) => {
|
||||||
const labelmapTools = getLabelmapTools({ toolGroupService });
|
let labelmapTools = getLabelmapTools({ toolGroupService });
|
||||||
|
labelmapTools = labelmapTools.filter(tool => !tool.toolName.includes('Eraser'));
|
||||||
labelmapTools.forEach(tool => {
|
labelmapTools.forEach(tool => {
|
||||||
tool.configuration = {
|
tool.configuration = {
|
||||||
...tool.configuration,
|
...tool.configuration,
|
||||||
@ -1562,7 +1567,8 @@ function commandsModule({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
toggleUseCenterSegmentIndex: ({ toggle }) => {
|
toggleUseCenterSegmentIndex: ({ toggle }) => {
|
||||||
const labelmapTools = getLabelmapTools({ toolGroupService });
|
let labelmapTools = getLabelmapTools({ toolGroupService });
|
||||||
|
labelmapTools = labelmapTools.filter(tool => !tool.toolName.includes('Eraser'));
|
||||||
labelmapTools.forEach(tool => {
|
labelmapTools.forEach(tool => {
|
||||||
tool.configuration = {
|
tool.configuration = {
|
||||||
...tool.configuration,
|
...tool.configuration,
|
||||||
@ -1707,6 +1713,20 @@ function commandsModule({
|
|||||||
const activeSegmentation = segmentationService.getActiveSegmentation(activeViewportId);
|
const activeSegmentation = segmentationService.getActiveSegmentation(activeViewportId);
|
||||||
segmentationService.addSegment(activeSegmentation.segmentationId);
|
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 = {
|
const definitions = {
|
||||||
@ -1985,6 +2005,7 @@ function commandsModule({
|
|||||||
increaseBrushSize: actions.increaseBrushSize,
|
increaseBrushSize: actions.increaseBrushSize,
|
||||||
decreaseBrushSize: actions.decreaseBrushSize,
|
decreaseBrushSize: actions.decreaseBrushSize,
|
||||||
addNewSegment: actions.addNewSegment,
|
addNewSegment: actions.addNewSegment,
|
||||||
|
loadSegmentationDisplaySetsForViewport: actions.loadSegmentationDisplaySetsForViewport,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -303,7 +303,7 @@ function DicomUploadProgress({
|
|||||||
<span
|
<span
|
||||||
className={classNames(
|
className={classNames(
|
||||||
NO_WRAP_ELLIPSIS_CLASS_NAMES,
|
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}
|
onClick={cancelAllUploads}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -89,7 +89,7 @@ const DicomUploadProgressItem = memo(
|
|||||||
)}
|
)}
|
||||||
<div className="ml-auto flex cursor-pointer">
|
<div className="ml-auto flex cursor-pointer">
|
||||||
<Icons.Close
|
<Icons.Close
|
||||||
className="text-primary-active self-center"
|
className="text-primary self-center"
|
||||||
onClick={cancelUpload}
|
onClick={cancelUpload}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -82,6 +82,7 @@ const stackRetrieveOptions = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unsubscriptions = [];
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -142,6 +143,8 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
|||||||
useToggleOneUpViewportGridStore.getState().clearToggleOneUpViewportGridStore();
|
useToggleOneUpViewportGridStore.getState().clearToggleOneUpViewportGridStore();
|
||||||
useSegmentationPresentationStore.getState().clearSegmentationPresentationStore();
|
useSegmentationPresentationStore.getState().clearSegmentationPresentationStore();
|
||||||
segmentationService.removeAllSegmentations();
|
segmentationService.removeAllSegmentations();
|
||||||
|
|
||||||
|
unsubscriptions.forEach(unsubscribe => unsubscribe());
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,7 +152,7 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
|||||||
*
|
*
|
||||||
* @param configuration.csToolsConfig - Passed directly to `initCornerstoneTools`
|
* @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;
|
const { servicesManager, serviceProvidersManager } = props;
|
||||||
servicesManager.registerService(CornerstoneViewportService.REGISTRATION);
|
servicesManager.registerService(CornerstoneViewportService.REGISTRATION);
|
||||||
servicesManager.registerService(ToolGroupService.REGISTRATION);
|
servicesManager.registerService(ToolGroupService.REGISTRATION);
|
||||||
@ -167,7 +170,13 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
|||||||
const { syncGroupService } = servicesManager.services;
|
const { syncGroupService } = servicesManager.services;
|
||||||
syncGroupService.registerCustomSynchronizer('frameview', createFrameViewSynchronizer);
|
syncGroupService.registerCustomSynchronizer('frameview', createFrameViewSynchronizer);
|
||||||
|
|
||||||
return init.call(this, props);
|
const initResult = await init.call(this, props);
|
||||||
|
|
||||||
|
unsubscriptions.push(...initResult.unsubscriptions);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...initResult,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
getToolbarModule,
|
getToolbarModule,
|
||||||
getHangingProtocolModule,
|
getHangingProtocolModule,
|
||||||
|
|||||||
@ -193,13 +193,14 @@ export default async function init({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Setup segmentation event handlers
|
// Setup segmentation event handlers
|
||||||
setupSegmentationDataModifiedHandler({
|
const { unsubscribe: unsubscribeSegmentationDataModifiedHandler } =
|
||||||
segmentationService,
|
setupSegmentationDataModifiedHandler({
|
||||||
customizationService,
|
segmentationService,
|
||||||
commandsManager,
|
customizationService,
|
||||||
});
|
commandsManager,
|
||||||
|
});
|
||||||
|
|
||||||
setupSegmentationModifiedHandler({
|
const { unsubscribe: unsubscribeSegmentationModifiedHandler } = setupSegmentationModifiedHandler({
|
||||||
segmentationService,
|
segmentationService,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -299,6 +300,13 @@ export default async function init({
|
|||||||
|
|
||||||
// Call this function when initializing
|
// Call this function when initializing
|
||||||
initializeWebWorkerProgressHandler(servicesManager.services.uiNotificationService);
|
initializeWebWorkerProgressHandler(servicesManager.services.uiNotificationService);
|
||||||
|
|
||||||
|
const unsubscriptions = [
|
||||||
|
unsubscribeSegmentationDataModifiedHandler,
|
||||||
|
unsubscribeSegmentationModifiedHandler,
|
||||||
|
];
|
||||||
|
|
||||||
|
return { unsubscriptions };
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeWebWorkerProgressHandler(uiNotificationService) {
|
function initializeWebWorkerProgressHandler(uiNotificationService) {
|
||||||
|
|||||||
@ -2,12 +2,10 @@ import React from 'react';
|
|||||||
import { SegmentationTable } from '@ohif/ui-next';
|
import { SegmentationTable } from '@ohif/ui-next';
|
||||||
import { useActiveViewportSegmentationRepresentations } from '../hooks/useActiveViewportSegmentationRepresentations';
|
import { useActiveViewportSegmentationRepresentations } from '../hooks/useActiveViewportSegmentationRepresentations';
|
||||||
import { metaData } from '@cornerstonejs/core';
|
import { metaData } from '@cornerstonejs/core';
|
||||||
|
import { useSystem } from '@ohif/core/src';
|
||||||
|
|
||||||
export default function PanelSegmentation({
|
export default function PanelSegmentation({ children }: withAppTypes) {
|
||||||
servicesManager,
|
const { commandsManager, servicesManager } = useSystem();
|
||||||
commandsManager,
|
|
||||||
children,
|
|
||||||
}: withAppTypes) {
|
|
||||||
const { customizationService, displaySetService } = servicesManager.services;
|
const { customizationService, displaySetService } = servicesManager.services;
|
||||||
|
|
||||||
const { segmentationsWithRepresentations, disabled } =
|
const { segmentationsWithRepresentations, disabled } =
|
||||||
|
|||||||
@ -427,6 +427,8 @@ class SegmentationService extends PubSubService {
|
|||||||
...metaData.get('instance', image.referencedImageId),
|
...metaData.get('instance', image.referencedImageId),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
segDisplaySet.imageIds = derivedImageIds;
|
||||||
|
|
||||||
// We should parse the segmentation as separate slices to support overlapping segments.
|
// We should parse the segmentation as separate slices to support overlapping segments.
|
||||||
// This parsing should occur in the CornerstoneJS library adapters.
|
// This parsing should occur in the CornerstoneJS library adapters.
|
||||||
// For now, we use the volume returned from the library and chop it here.
|
// For now, we use the volume returned from the library and chop it here.
|
||||||
@ -513,7 +515,7 @@ class SegmentationService extends PubSubService {
|
|||||||
type: LABELMAP,
|
type: LABELMAP,
|
||||||
data: {
|
data: {
|
||||||
imageIds: derivedImageIds,
|
imageIds: derivedImageIds,
|
||||||
referencedVolumeId: this._getVolumeIdForDisplaySet(referencedDisplaySet),
|
// referencedVolumeId: this._getVolumeIdForDisplaySet(referencedDisplaySet),
|
||||||
referencedImageIds: imageIds as string[],
|
referencedImageIds: imageIds as string[],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -566,9 +568,10 @@ class SegmentationService extends PubSubService {
|
|||||||
|
|
||||||
const referencedImageIds = referencedDisplaySet.imageIds;
|
const referencedImageIds = referencedDisplaySet.imageIds;
|
||||||
// find the first image id that contains a referenced SOP instance UID
|
// find the first image id that contains a referenced SOP instance UID
|
||||||
const firstSegmentedSliceImageId = referencedImageIds.find(imageId =>
|
const firstSegmentedSliceImageId =
|
||||||
referencedImageIdsWithGeometry.some(referencedId => imageId.includes(referencedId))
|
referencedImageIds?.find(imageId =>
|
||||||
);
|
referencedImageIdsWithGeometry.some(referencedId => imageId.includes(referencedId))
|
||||||
|
) || null;
|
||||||
|
|
||||||
rtDisplaySet.firstSegmentedSliceImageId = firstSegmentedSliceImageId;
|
rtDisplaySet.firstSegmentedSliceImageId = firstSegmentedSliceImageId;
|
||||||
// Map ROI contours to RT Struct Data
|
// Map ROI contours to RT Struct Data
|
||||||
|
|||||||
@ -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
|
// If you call loadStudyMetadata and it's not in the DicomMetadataStore cache, it should fire
|
||||||
// a request through the data source?
|
// a request through the data source?
|
||||||
// (This call may or may not create sub-requests for series metadata)
|
// (This call may or may not create sub-requests for series metadata)
|
||||||
|
const { displaySetService } = this.servicesManager.services;
|
||||||
const volumeInputArray = [];
|
const volumeInputArray = [];
|
||||||
const displaySetOptionsArray = viewportInfo.getDisplaySetOptions();
|
const displaySetOptionsArray = viewportInfo.getDisplaySetOptions();
|
||||||
const { hangingProtocolService } = this.servicesManager.services;
|
const { hangingProtocolService } = this.servicesManager.services;
|
||||||
@ -750,7 +751,13 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
|||||||
const displaySetInstanceUIDs = [];
|
const displaySetInstanceUIDs = [];
|
||||||
|
|
||||||
for (const [index, data] of viewportData.data.entries()) {
|
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);
|
displaySetInstanceUIDs.push(displaySetInstanceUID);
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import html2canvas from 'html2canvas';
|
import html2canvas from 'html2canvas';
|
||||||
import { getEnabledElement, StackViewport, BaseVolumeViewport } from '@cornerstonejs/core';
|
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 { getEnabledElement as OHIFgetEnabledElement } from '../state';
|
||||||
import { useSystem } from '@ohif/core/src';
|
import { useSystem } from '@ohif/core/src';
|
||||||
|
|
||||||
@ -103,6 +103,9 @@ const CornerstoneViewportDownloadForm = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const segmentationRepresentations =
|
||||||
|
segmentation.state.getViewportSegmentationRepresentations(activeViewportId);
|
||||||
|
|
||||||
const { viewport } = activeViewportEnabledElement;
|
const { viewport } = activeViewportEnabledElement;
|
||||||
const downloadViewport = renderingEngine.getViewport(VIEWPORT_ID);
|
const downloadViewport = renderingEngine.getViewport(VIEWPORT_ID);
|
||||||
|
|
||||||
@ -113,20 +116,48 @@ const CornerstoneViewportDownloadForm = ({
|
|||||||
|
|
||||||
await downloadViewport.setStack([imageId]);
|
await downloadViewport.setStack([imageId]);
|
||||||
downloadViewport.setProperties(properties);
|
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) {
|
} else if (downloadViewport instanceof BaseVolumeViewport) {
|
||||||
const volumeIds = viewport.getAllVolumeIds();
|
const volumeIds = viewport.getAllVolumeIds();
|
||||||
downloadViewport.setVolumes([{ volumeId: volumeIds[0] }]);
|
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) {
|
} catch (error) {
|
||||||
console.error('Error loading image:', error);
|
console.error('Error loading image:', error);
|
||||||
}
|
}
|
||||||
@ -167,6 +198,10 @@ const CornerstoneViewportDownloadForm = ({
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
handleLoadImage(viewportDimensions.width, viewportDimensions.height);
|
handleLoadImage(viewportDimensions.width, viewportDimensions.height);
|
||||||
handleToggleAnnotations(showAnnotations);
|
handleToggleAnnotations(showAnnotations);
|
||||||
|
// we need a resize here to make suer annotations world to canvas
|
||||||
|
// are properly calculated
|
||||||
|
renderingEngine.resize();
|
||||||
|
renderingEngine.render();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
}, [viewportDimensions, showAnnotations]);
|
}, [viewportDimensions, showAnnotations]);
|
||||||
|
|||||||
31
extensions/cornerstone/src/utils/hydrationUtils.ts
Normal 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 };
|
||||||
@ -9,10 +9,15 @@ export function setupSegmentationDataModifiedHandler({
|
|||||||
customizationService,
|
customizationService,
|
||||||
commandsManager,
|
commandsManager,
|
||||||
}) {
|
}) {
|
||||||
segmentationService.subscribeDebounced(
|
const { unsubscribe } = segmentationService.subscribeDebounced(
|
||||||
segmentationService.EVENTS.SEGMENTATION_DATA_MODIFIED,
|
segmentationService.EVENTS.SEGMENTATION_DATA_MODIFIED,
|
||||||
async ({ segmentationId }) => {
|
async ({ segmentationId }) => {
|
||||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||||
|
|
||||||
|
if (!segmentation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const readableText = customizationService.getCustomization('panelSegmentation.readableText');
|
const readableText = customizationService.getCustomization('panelSegmentation.readableText');
|
||||||
|
|
||||||
// Check for segments with bidirectional measurements and update them
|
// Check for segments with bidirectional measurements and update them
|
||||||
@ -46,17 +51,23 @@ export function setupSegmentationDataModifiedHandler({
|
|||||||
},
|
},
|
||||||
1000
|
1000
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return { unsubscribe };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the handler for segmentation modification events
|
* Sets up the handler for segmentation modification events
|
||||||
*/
|
*/
|
||||||
export function setupSegmentationModifiedHandler({ segmentationService }) {
|
export function setupSegmentationModifiedHandler({ segmentationService }) {
|
||||||
segmentationService.subscribe(
|
const { unsubscribe } = segmentationService.subscribe(
|
||||||
segmentationService.EVENTS.SEGMENTATION_MODIFIED,
|
segmentationService.EVENTS.SEGMENTATION_MODIFIED,
|
||||||
async ({ segmentationId }) => {
|
async ({ segmentationId }) => {
|
||||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||||
|
|
||||||
|
if (!segmentation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const annotationState = cornerstoneTools.annotation.state.getAllAnnotations();
|
const annotationState = cornerstoneTools.annotation.state.getAllAnnotations();
|
||||||
const bidirectionalAnnotations = annotationState.filter(
|
const bidirectionalAnnotations = annotationState.filter(
|
||||||
annotation =>
|
annotation =>
|
||||||
@ -90,4 +101,6 @@ export function setupSegmentationModifiedHandler({ segmentationService }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return { unsubscribe };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,7 +113,7 @@ function DataSourceConfigurationModalComponent({
|
|||||||
: 'border border-dashed border-secondary-light';
|
: 'border border-dashed border-secondary-light';
|
||||||
|
|
||||||
const getSelectedItemTextClasses = itemIndex =>
|
const getSelectedItemTextClasses = itemIndex =>
|
||||||
itemIndex <= selectedItems.length ? 'text-primary-light' : 'text-primary-active';
|
itemIndex <= selectedItems.length ? 'text-primary-light' : 'text-primary';
|
||||||
|
|
||||||
const getErrorComponent = (): ReactElement => {
|
const getErrorComponent = (): ReactElement => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -20,7 +20,7 @@ function PanelStudyBrowserHeader({
|
|||||||
<div className={'flex h-[24px] w-full select-none justify-center self-center text-[14px]'}>
|
<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 w-full items-center gap-[10px]">
|
||||||
<div className="flex items-center justify-center">
|
<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) =>
|
{actionIcons.map((icon: actionIcon, index) =>
|
||||||
React.createElement(Icons[icon.iconName] || Icons.MissingIcon, {
|
React.createElement(Icons[icon.iconName] || Icons.MissingIcon, {
|
||||||
key: index,
|
key: index,
|
||||||
|
|||||||
@ -44,9 +44,9 @@ function HeaderPatientInfo({ servicesManager, appConfig }: withAppTypes) {
|
|||||||
onClick={handleOnClick}
|
onClick={handleOnClick}
|
||||||
>
|
>
|
||||||
{isMixedPatients ? (
|
{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">
|
<div className="flex flex-col justify-center">
|
||||||
{expanded ? (
|
{expanded ? (
|
||||||
@ -61,12 +61,12 @@ function HeaderPatientInfo({ servicesManager, appConfig }: withAppTypes) {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-primary-active self-center text-[13px]">
|
<div className="text-primary self-center text-[13px]">
|
||||||
{isMixedPatients ? 'Multiple Patients' : 'Patient'}
|
{isMixedPatients ? 'Multiple Patients' : 'Patient'}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Icons.ArrowLeft className={`text-primary-active ${expanded ? 'rotate-180' : ''}`} />
|
<Icons.ArrowLeft className={`text-primary ${expanded ? 'rotate-180' : ''}`} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,7 +95,7 @@ function ViewerHeader({ appConfig }: withAppTypes<{ appConfig: AppTypes.Config }
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
UndoRedo={
|
UndoRedo={
|
||||||
<div className="text-primary-active flex cursor-pointer items-center">
|
<div className="text-primary flex cursor-pointer items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="hover:bg-primary-dark"
|
className="hover:bg-primary-dark"
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export function Toolbox({ buttonSectionId, title }: { buttonSectionId: string; t
|
|||||||
{CustomConfigComponent && (
|
{CustomConfigComponent && (
|
||||||
<div className="ml-auto mr-2">
|
<div className="ml-auto mr-2">
|
||||||
<Icons.Settings
|
<Icons.Settings
|
||||||
className="text-primary-active h-4 w-4"
|
className="text-primary h-4 w-4"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setShowConfig(!showConfig);
|
setShowConfig(!showConfig);
|
||||||
|
|||||||
@ -32,8 +32,8 @@
|
|||||||
"start": "yarn run dev"
|
"start": "yarn run dev"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@cornerstonejs/core": "^3.8.4",
|
"@cornerstonejs/core": "^3.8.5",
|
||||||
"@cornerstonejs/tools": "^3.8.4",
|
"@cornerstonejs/tools": "^3.8.5",
|
||||||
"@ohif/core": "3.10.0-beta.148",
|
"@ohif/core": "3.10.0-beta.148",
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.148",
|
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.148",
|
||||||
"@ohif/extension-default": "3.10.0-beta.148",
|
"@ohif/extension-default": "3.10.0-beta.148",
|
||||||
|
|||||||
@ -2,7 +2,8 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { DicomMetadataStore } from '@ohif/core';
|
import { DicomMetadataStore } from '@ohif/core';
|
||||||
import { useTranslation } from 'react-i18next';
|
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 = {
|
const DEFAULT_MEATADATA = {
|
||||||
PatientWeight: null,
|
PatientWeight: null,
|
||||||
@ -37,7 +38,10 @@ const InputRow = ({ children, className, ...props }) => {
|
|||||||
|
|
||||||
// InputRow sub-components
|
// InputRow sub-components
|
||||||
InputRow.Label = ({ children, unit, className, ...props }) => (
|
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}
|
{children}
|
||||||
{unit && <span className="text-muted-foreground"> {unit}</span>}
|
{unit && <span className="text-muted-foreground"> {unit}</span>}
|
||||||
</Label>
|
</Label>
|
||||||
@ -54,7 +58,8 @@ InputRow.Input = ({ className, ...props }) => (
|
|||||||
InputRow.Label.displayName = 'InputRow.Label';
|
InputRow.Label.displayName = 'InputRow.Label';
|
||||||
InputRow.Input.displayName = 'InputRow.Input';
|
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 { t } = useTranslation('PanelSUV');
|
||||||
const { displaySetService, toolGroupService, toolbarService, hangingProtocolService } =
|
const { displaySetService, toolGroupService, toolbarService, hangingProtocolService } =
|
||||||
servicesManager.services;
|
servicesManager.services;
|
||||||
@ -192,7 +197,9 @@ export default function PanelPetSUV({ servicesManager, commandsManager }: withAp
|
|||||||
<InputRow>
|
<InputRow>
|
||||||
<InputRow.Label unit="bq">{t('Total Dose')}</InputRow.Label>
|
<InputRow.Label unit="bq">{t('Total Dose')}</InputRow.Label>
|
||||||
<InputRow.Input
|
<InputRow.Input
|
||||||
value={metadata.RadiopharmaceuticalInformationSequence.RadionuclideTotalDose || ''}
|
value={
|
||||||
|
metadata.RadiopharmaceuticalInformationSequence.RadionuclideTotalDose || ''
|
||||||
|
}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
handleMetadataChange({
|
handleMetadataChange({
|
||||||
RadiopharmaceuticalInformationSequence: {
|
RadiopharmaceuticalInformationSequence: {
|
||||||
@ -206,7 +213,9 @@ export default function PanelPetSUV({ servicesManager, commandsManager }: withAp
|
|||||||
<InputRow>
|
<InputRow>
|
||||||
<InputRow.Label unit="s">{t('Half Life')}</InputRow.Label>
|
<InputRow.Label unit="s">{t('Half Life')}</InputRow.Label>
|
||||||
<InputRow.Input
|
<InputRow.Input
|
||||||
value={metadata.RadiopharmaceuticalInformationSequence.RadionuclideHalfLife || ''}
|
value={
|
||||||
|
metadata.RadiopharmaceuticalInformationSequence.RadionuclideHalfLife || ''
|
||||||
|
}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
handleMetadataChange({
|
handleMetadataChange({
|
||||||
RadiopharmaceuticalInformationSequence: {
|
RadiopharmaceuticalInformationSequence: {
|
||||||
@ -221,7 +230,8 @@ export default function PanelPetSUV({ servicesManager, commandsManager }: withAp
|
|||||||
<InputRow.Label unit="s">{t('Injection Time')}</InputRow.Label>
|
<InputRow.Label unit="s">{t('Injection Time')}</InputRow.Label>
|
||||||
<InputRow.Input
|
<InputRow.Input
|
||||||
value={
|
value={
|
||||||
metadata.RadiopharmaceuticalInformationSequence.RadiopharmaceuticalStartTime || ''
|
metadata.RadiopharmaceuticalInformationSequence
|
||||||
|
.RadiopharmaceuticalStartTime || ''
|
||||||
}
|
}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
handleMetadataChange({
|
handleMetadataChange({
|
||||||
|
|||||||
@ -2,11 +2,10 @@ import React, { useEffect } from 'react';
|
|||||||
import { useActiveViewportSegmentationRepresentations } from '@ohif/extension-cornerstone';
|
import { useActiveViewportSegmentationRepresentations } from '@ohif/extension-cornerstone';
|
||||||
import { handleROIThresholding } from '../../utils/handleROIThresholding';
|
import { handleROIThresholding } from '../../utils/handleROIThresholding';
|
||||||
import { debounce } from '@ohif/core/src/utils';
|
import { debounce } from '@ohif/core/src/utils';
|
||||||
|
import { useSystem } from '@ohif/core/src';
|
||||||
|
|
||||||
export default function PanelRoiThresholdSegmentation({
|
export default function PanelRoiThresholdSegmentation() {
|
||||||
servicesManager,
|
const { commandsManager, servicesManager } = useSystem();
|
||||||
commandsManager,
|
|
||||||
}: withAppTypes) {
|
|
||||||
const { segmentationService } = servicesManager.services;
|
const { segmentationService } = servicesManager.services;
|
||||||
const { segmentationsWithRepresentations: segmentationsInfo } =
|
const { segmentationsWithRepresentations: segmentationsInfo } =
|
||||||
useActiveViewportSegmentationRepresentations({ servicesManager });
|
useActiveViewportSegmentationRepresentations({ servicesManager });
|
||||||
|
|||||||
@ -1,62 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { PanelSegmentation } from '@ohif/extension-cornerstone';
|
||||||
PanelSegmentation,
|
import PanelROIThresholdExport from './PanelROIThresholdSegmentation/PanelROIThresholdExport';
|
||||||
useActiveViewportSegmentationRepresentations,
|
|
||||||
} from '@ohif/extension-cornerstone';
|
|
||||||
import { Button, Icons } from '@ohif/ui-next';
|
|
||||||
|
|
||||||
export default function PanelTMTV({
|
export default function PanelTMTV({ configuration }: withAppTypes) {
|
||||||
servicesManager,
|
|
||||||
commandsManager,
|
|
||||||
extensionManager,
|
|
||||||
configuration,
|
|
||||||
}: withAppTypes) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PanelSegmentation
|
<PanelSegmentation configuration={configuration}>
|
||||||
servicesManager={servicesManager}
|
<PanelROIThresholdExport />
|
||||||
commandsManager={commandsManager}
|
|
||||||
extensionManager={extensionManager}
|
|
||||||
configuration={configuration}
|
|
||||||
>
|
|
||||||
<ExportCSV
|
|
||||||
servicesManager={servicesManager}
|
|
||||||
commandsManager={commandsManager}
|
|
||||||
/>
|
|
||||||
</PanelSegmentation>
|
</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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import createAndDownloadTMTVReport from './utils/createAndDownloadTMTVReport';
|
|||||||
|
|
||||||
import dicomRTAnnotationExport from './utils/dicomRTAnnotationExport/RTStructureSet';
|
import dicomRTAnnotationExport from './utils/dicomRTAnnotationExport/RTStructureSet';
|
||||||
|
|
||||||
import { getWebWorkerManager } from '@cornerstonejs/core';
|
|
||||||
import { Enums } from '@cornerstonejs/tools';
|
import { Enums } from '@cornerstonejs/tools';
|
||||||
import { utils } from '@ohif/core';
|
import { utils } from '@ohif/core';
|
||||||
|
|
||||||
@ -22,32 +21,6 @@ const ROI_THRESHOLD_MANUAL_TOOL_IDS = [
|
|||||||
'CircleROIStartEndThreshold',
|
'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 commandsModule = ({ servicesManager, commandsManager, extensionManager }: withAppTypes) => {
|
||||||
const {
|
const {
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
@ -265,8 +238,14 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }:
|
|||||||
segmentations,
|
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 = [
|
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 } },
|
{ key: 'Threshold Configuration', value: { ...config } },
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -382,9 +361,8 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }:
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const referencedVolumeId = labelmapVolume.referencedVolumeId;
|
const referencedVolume =
|
||||||
|
csTools.utilities.segmentation.getReferenceVolumeForSegmentationVolume(segmentationId);
|
||||||
const referencedVolume = cs.cache.getVolume(referencedVolumeId);
|
|
||||||
|
|
||||||
if (!referencedVolume) {
|
if (!referencedVolume) {
|
||||||
report[id] = segReport;
|
report[id] = segReport;
|
||||||
@ -476,9 +454,6 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }:
|
|||||||
thresholdSegmentationByRectangleROITool: {
|
thresholdSegmentationByRectangleROITool: {
|
||||||
commandFn: actions.thresholdSegmentationByRectangleROITool,
|
commandFn: actions.thresholdSegmentationByRectangleROITool,
|
||||||
},
|
},
|
||||||
getTotalLesionGlycolysis: {
|
|
||||||
commandFn: actions.getTotalLesionGlycolysis,
|
|
||||||
},
|
|
||||||
calculateTMTV: {
|
calculateTMTV: {
|
||||||
commandFn: actions.calculateTMTV,
|
commandFn: actions.calculateTMTV,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -5,13 +5,7 @@ import PanelTMTV from './Panels/PanelTMTV';
|
|||||||
|
|
||||||
function getPanelModule({ commandsManager, extensionManager, servicesManager }) {
|
function getPanelModule({ commandsManager, extensionManager, servicesManager }) {
|
||||||
const wrappedPanelPetSuv = () => {
|
const wrappedPanelPetSuv = () => {
|
||||||
return (
|
return <PanelPetSUV />;
|
||||||
<PanelPetSUV
|
|
||||||
commandsManager={commandsManager}
|
|
||||||
servicesManager={servicesManager}
|
|
||||||
extensionManager={extensionManager}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrappedROIThresholdToolbox = () => {
|
const wrappedROIThresholdToolbox = () => {
|
||||||
@ -24,12 +18,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager })
|
|||||||
};
|
};
|
||||||
|
|
||||||
const wrappedROIThresholdExport = () => {
|
const wrappedROIThresholdExport = () => {
|
||||||
return (
|
return <PanelROIThresholdExport />;
|
||||||
<PanelROIThresholdExport
|
|
||||||
commandsManager={commandsManager}
|
|
||||||
servicesManager={servicesManager}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrappedPanelTMTV = () => {
|
const wrappedPanelTMTV = () => {
|
||||||
@ -43,10 +32,6 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager })
|
|||||||
commandsManager={commandsManager}
|
commandsManager={commandsManager}
|
||||||
servicesManager={servicesManager}
|
servicesManager={servicesManager}
|
||||||
/>
|
/>
|
||||||
<PanelROIThresholdExport
|
|
||||||
commandsManager={commandsManager}
|
|
||||||
servicesManager={servicesManager}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,10 +3,12 @@ import type { Button } from '@ohif/core/types';
|
|||||||
import { EVENTS } from '@cornerstonejs/core';
|
import { EVENTS } from '@cornerstonejs/core';
|
||||||
import { ViewportGridService } from '@ohif/core';
|
import { ViewportGridService } from '@ohif/core';
|
||||||
|
|
||||||
const ReferenceLinesListeners = [
|
const callbacks = (toolName: string) => [
|
||||||
{
|
{
|
||||||
commandName: 'setSourceViewportForReferenceLinesTool',
|
commandName: 'setViewportForToolConfiguration',
|
||||||
context: 'CORNERSTONE',
|
commandOptions: {
|
||||||
|
toolName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -118,8 +120,8 @@ const toolbarButtons: Button[] = [
|
|||||||
tooltip: 'Show Reference Lines',
|
tooltip: 'Show Reference Lines',
|
||||||
commands: 'toggleEnabledDisabledToolbar',
|
commands: 'toggleEnabledDisabledToolbar',
|
||||||
listeners: {
|
listeners: {
|
||||||
[ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: ReferenceLinesListeners,
|
[ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('ReferenceLinesTool'),
|
||||||
[ViewportGridService.EVENTS.VIEWPORTS_READY]: ReferenceLinesListeners,
|
[ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('ReferenceLinesTool'),
|
||||||
},
|
},
|
||||||
evaluate: [
|
evaluate: [
|
||||||
'evaluate.cornerstoneTool.toggle',
|
'evaluate.cornerstoneTool.toggle',
|
||||||
|
|||||||
@ -54,7 +54,7 @@
|
|||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
"@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",
|
"@emotion/serialize": "^1.1.3",
|
||||||
"@ohif/core": "3.10.0-beta.148",
|
"@ohif/core": "3.10.0-beta.148",
|
||||||
"@ohif/extension-cornerstone": "3.10.0-beta.148",
|
"@ohif/extension-cornerstone": "3.10.0-beta.148",
|
||||||
|
|||||||
@ -15,7 +15,7 @@ function Debug() {
|
|||||||
/>
|
/>
|
||||||
<div className="space-y-2 pt-4 text-center">
|
<div className="space-y-2 pt-4 text-center">
|
||||||
<div className="flex flex-col items-center justify-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">
|
<div className="mt-4 flex items-center space-x-2">
|
||||||
<p className="text-md text-white">Cross Origin Isolated (COOP/COEP)</p>
|
<p className="text-md text-white">Cross Origin Isolated (COOP/COEP)</p>
|
||||||
<Icons.ByName
|
<Icons.ByName
|
||||||
|
|||||||
@ -343,7 +343,7 @@ function WorkList({
|
|||||||
<>
|
<>
|
||||||
<Icons.GroupLayers
|
<Icons.GroupLayers
|
||||||
className={classnames('mr-2 inline-flex w-4', {
|
className={classnames('mr-2 inline-flex w-4', {
|
||||||
'text-primary-active': isExpanded,
|
'text-primary': isExpanded,
|
||||||
'text-secondary-light': !isExpanded,
|
'text-secondary-light': !isExpanded,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
"@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",
|
"@ohif/ui": "3.10.0-beta.148",
|
||||||
"cornerstone-math": "0.1.9",
|
"cornerstone-math": "0.1.9",
|
||||||
"dicom-parser": "^1.8.21"
|
"dicom-parser": "^1.8.21"
|
||||||
|
|||||||
503
platform/docs/src/pages/patterns/patterns-segmentation-raw.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -2,538 +2,72 @@
|
|||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
import { DataRow } from '../../../../ui-next/src/components/DataRow';
|
import { panelSegmentationData } from '../../../../ui-next/assets/data';
|
||||||
import { Button } from '../../../../ui-next/src/components/Button';
|
import { SegmentationTable } from '../../../../ui-next/src/components/SegmentationTable';
|
||||||
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 { 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() {
|
export default function SegmentationPanel() {
|
||||||
const [selectedRowId, setSelectedRowId] = useState<string | null>(null);
|
const tableProps = panelSegmentationData;
|
||||||
const [selectedTab, setSelectedTab] = useState<string>('Fill & Outline');
|
tableProps.mode = 'expanded';
|
||||||
const handleAction = (id: string, action: string) => {
|
|
||||||
console.log(`Action "${action}" triggered for item with id: ${id}`);
|
const renderSegments = () => {
|
||||||
// Implement actual action logic here
|
return (
|
||||||
|
<SegmentationTable.Segments>
|
||||||
|
<SegmentationTable.SegmentStatistics.Header></SegmentationTable.SegmentStatistics.Header>
|
||||||
|
<SegmentationTable.SegmentStatistics.Body />
|
||||||
|
</SegmentationTable.Segments>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle row selection
|
// Render content based on mode
|
||||||
const handleRowSelect = (id: string) => {
|
const renderModeContent = () => {
|
||||||
setSelectedRowId(prevSelectedId => (prevSelectedId === id ? null : id));
|
if (tableProps.mode === 'collapsed') {
|
||||||
};
|
return (
|
||||||
|
<SegmentationTable.Collapsed>
|
||||||
const organSegmentationGroup = dataList.find(
|
<SegmentationTable.Collapsed.Header>
|
||||||
(listGroup: any) => listGroup.type === 'Organ Segmentation'
|
<SegmentationTable.Collapsed.DropdownMenu>
|
||||||
) as unknown as ListGroup;
|
{/* <CustomDropdownMenuContent /> */}
|
||||||
|
</SegmentationTable.Collapsed.DropdownMenu>
|
||||||
if (!organSegmentationGroup) {
|
<SegmentationTable.Collapsed.Selector />
|
||||||
return <div className="text-red-500">Organ Segmentation data not found.</div>;
|
<SegmentationTable.Collapsed.Info />
|
||||||
}
|
</SegmentationTable.Collapsed.Header>
|
||||||
|
<SegmentationTable.Collapsed.Content>
|
||||||
// Create a state to track which item's statistics to show
|
<SegmentationTable.AddSegmentRow />
|
||||||
|
{renderSegments()}
|
||||||
// Function to render statistics panel
|
</SegmentationTable.Collapsed.Content>
|
||||||
const renderStatisticsPanel = (item: DataItem) => {
|
</SegmentationTable.Collapsed>
|
||||||
if (!item.statistics) {
|
);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const stats = item.statistics;
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<>
|
||||||
<div className="mb-4 flex items-center space-x-2">
|
<SegmentationTable.Expanded>
|
||||||
<div
|
<SegmentationTable.Expanded.Header>
|
||||||
className="h-2.5 w-2.5 flex-shrink-0 rounded-full"
|
<SegmentationTable.Expanded.DropdownMenu>
|
||||||
style={{ backgroundColor: item.colorHex }}
|
{/* <CustomDropdownMenuContent /> */}
|
||||||
></div>
|
</SegmentationTable.Expanded.DropdownMenu>
|
||||||
<h3 className="text-muted-foreground break-words text-lg font-semibold">{item.title}</h3>
|
<SegmentationTable.Expanded.Label />
|
||||||
</div>
|
<SegmentationTable.Expanded.Info />
|
||||||
|
</SegmentationTable.Expanded.Header>
|
||||||
|
|
||||||
<div className="space-y-1">
|
<SegmentationTable.Expanded.Content>
|
||||||
<div className="flex justify-between">
|
<SegmentationTable.AddSegmentRow />
|
||||||
<div className="">Centroid X</div>
|
{renderSegments()}
|
||||||
<div>
|
</SegmentationTable.Expanded.Content>
|
||||||
<span className="text-white">{stats.centroidX.value}</span>{' '}
|
</SegmentationTable.Expanded>
|
||||||
<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 (
|
return (
|
||||||
<div className="px-auto flex min-h-screen w-full justify-center bg-black py-12">
|
<div className="px-auto flex min-h-screen w-full justify-center border-2 bg-black py-64">
|
||||||
<div className="w-64 space-y-0">
|
<TooltipProvider>
|
||||||
<TooltipProvider>
|
<SegmentationTable {...tableProps}>
|
||||||
<Accordion
|
<SegmentationTable.Config />
|
||||||
type="multiple"
|
<SegmentationTable.AddSegmentationRow />
|
||||||
defaultValue={['segmentation-tools', 'segmentation-list']}
|
{renderModeContent()}
|
||||||
>
|
</SegmentationTable>
|
||||||
{/* Segmentation Tools */}
|
</TooltipProvider>
|
||||||
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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[] } = {
|
const actionOptionsMap: { [key: string]: string[] } = {
|
||||||
Measurement: ['Rename', 'Lock', 'Delete'],
|
Measurement: ['Rename', 'Lock', 'Delete'],
|
||||||
Segmentation: ['Rename', 'Lock', 'Export', '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 };
|
||||||
|
|||||||
@ -65,7 +65,7 @@ function Header({
|
|||||||
onClick={onClickReturn}
|
onClick={onClickReturn}
|
||||||
data-cy="return-to-work-list"
|
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">
|
<div className="ml-1">
|
||||||
{WhiteLabeling?.createLogoComponentFn?.(React, props) || <Icons.OHIFLogo />}
|
{WhiteLabeling?.createLogoComponentFn?.(React, props) || <Icons.OHIFLogo />}
|
||||||
</div>
|
</div>
|
||||||
@ -86,7 +86,7 @@ function Header({
|
|||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
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 />
|
<Icons.GearSettings />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({
|
|||||||
onClick={() => !disabled && onSegmentationAdd('')}
|
onClick={() => !disabled && onSegmentationAdd('')}
|
||||||
>
|
>
|
||||||
{children}
|
{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">
|
<div className="grid h-[28px] w-[28px] place-items-center">
|
||||||
{disabled ? <Icons.Info /> : <Icons.Add />}
|
{disabled ? <Icons.Info /> : <Icons.Add />}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const SegmentationExpandedHeader = ({ children }: { children: React.ReactNode })
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelSection.Header
|
<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 => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onSegmentationClick(segmentation.segmentationId);
|
onSegmentationClick(segmentation.segmentationId);
|
||||||
@ -59,7 +59,7 @@ const SegmentationExpandedInfo = () => {
|
|||||||
const { segmentation } = useSegmentationExpanded('SegmentationExpandedInfo');
|
const { segmentation } = useSegmentationExpanded('SegmentationExpandedInfo');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ml-auto mr-1">
|
<div className="ml-auto mr-2">
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@ -79,8 +79,11 @@ const SegmentationExpandedInfo = () => {
|
|||||||
|
|
||||||
// Content component
|
// Content component
|
||||||
const SegmentationExpandedContent = ({ children }: { children: React.ReactNode }) => {
|
const SegmentationExpandedContent = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const { isActive } = useSegmentationExpanded('SegmentationExpandedContent');
|
||||||
return (
|
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>
|
<div className="segmentation-expanded-section">{children}</div>
|
||||||
</PanelSection.Content>
|
</PanelSection.Content>
|
||||||
);
|
);
|
||||||
@ -97,14 +100,14 @@ const SegmentationExpandedRoot = ({ children }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-0">
|
<div className={`space-y-0 pl-0.5`}>
|
||||||
{data.map(segmentationInfo => {
|
{data.map(segmentationInfo => {
|
||||||
const isActive = segmentationInfo.segmentation.segmentationId === activeSegmentationId;
|
const isActive = segmentationInfo.segmentation.segmentationId === activeSegmentationId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelSection
|
<PanelSection
|
||||||
key={segmentationInfo.segmentation.segmentationId}
|
key={segmentationInfo.segmentation.segmentationId}
|
||||||
className="mb-0"
|
className=""
|
||||||
>
|
>
|
||||||
<SegmentationExpandedProvider
|
<SegmentationExpandedProvider
|
||||||
segmentation={segmentationInfo.segmentation}
|
segmentation={segmentationInfo.segmentation}
|
||||||
|
|||||||
@ -65,42 +65,42 @@ export const SegmentationSegments = ({ children = null }: { children?: React.Rea
|
|||||||
const { locked, active, label, displayText } = segmentFromSegmentation;
|
const { locked, active, label, displayText } = segmentFromSegmentation;
|
||||||
const cssColor = `rgb(${color[0]},${color[1]},${color[2]})`;
|
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
|
<HoverCard
|
||||||
key={`hover-${segmentIndex}`}
|
key={`hover-${segmentIndex}`}
|
||||||
openDelay={300}
|
openDelay={300}
|
||||||
>
|
>
|
||||||
<HoverCardTrigger asChild>
|
<HoverCardTrigger asChild>
|
||||||
{/* The div here is not random, it is needed for making it work with the hover card */}
|
<div>{DataRowComponent}</div>
|
||||||
<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>
|
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent
|
<HoverCardContent
|
||||||
side="left"
|
side="left"
|
||||||
@ -126,6 +126,8 @@ export const SegmentationSegments = ({ children = null }: { children?: React.Rea
|
|||||||
</SegmentStatistics>
|
</SegmentStatistics>
|
||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
|
) : (
|
||||||
|
DataRowComponent
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|||||||
@ -112,7 +112,7 @@ export const SegmentationTableRoot = (props: SegmentationTableProps) => {
|
|||||||
{hasConfigComponent && (
|
{hasConfigComponent && (
|
||||||
<div className="ml-auto mr-2">
|
<div className="ml-auto mr-2">
|
||||||
<Icons.Settings
|
<Icons.Settings
|
||||||
className="text-primary-active h-4 w-4"
|
className="text-primary h-4 w-4"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
toggleShowConfig();
|
toggleShowConfig();
|
||||||
|
|||||||
@ -61,13 +61,13 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> =
|
|||||||
>
|
>
|
||||||
<TabsList>
|
<TabsList>
|
||||||
<TabsTrigger value="fill-and-outline">
|
<TabsTrigger value="fill-and-outline">
|
||||||
<Icons.FillAndOutline className="text-primary-active" />
|
<Icons.FillAndOutline className="text-primary" />
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="outline">
|
<TabsTrigger value="outline">
|
||||||
<Icons.OutlineOnly className="text-primary-active" />
|
<Icons.OutlineOnly className="text-primary" />
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="fill">
|
<TabsTrigger value="fill">
|
||||||
<Icons.FillOnly className="text-primary-active" />
|
<Icons.FillOnly className="text-primary" />
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|||||||
@ -112,7 +112,7 @@ const getTabClassNames = (
|
|||||||
isTabDisabled: boolean
|
isTabDisabled: boolean
|
||||||
) =>
|
) =>
|
||||||
classnames('h-[28px] mb-[2px] cursor-pointer text-white bg-black', {
|
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-l': tabIndex % numColumns === 0,
|
||||||
'rounded-r': (tabIndex + 1) % numColumns === 0 || tabIndex === numTabs - 1,
|
'rounded-r': (tabIndex + 1) % numColumns === 0 || tabIndex === numTabs - 1,
|
||||||
});
|
});
|
||||||
@ -294,7 +294,7 @@ const SidePanel = ({
|
|||||||
data-cy={`side-panel-header-${side}`}
|
data-cy={`side-panel-header-${side}`}
|
||||||
>
|
>
|
||||||
<Icons.NavigationPanelReveal
|
<Icons.NavigationPanelReveal
|
||||||
className={classnames('text-primary-active', side === 'left' && 'rotate-180 transform')}
|
className={classnames('text-primary', side === 'left' && 'rotate-180 transform')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames('mt-3 flex flex-col space-y-3')}>
|
<div className={classnames('mt-3 flex flex-col space-y-3')}>
|
||||||
@ -304,14 +304,14 @@ const SidePanel = ({
|
|||||||
<div
|
<div
|
||||||
id={`${childComponent.name}-btn`}
|
id={`${childComponent.name}-btn`}
|
||||||
data-cy={`${childComponent.name}-btn`}
|
data-cy={`${childComponent.name}-btn`}
|
||||||
className="text-primary-active hover:cursor-pointer"
|
className="text-primary hover:cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
return childComponent.disabled ? null : updateActiveTabIndex(index, true);
|
return childComponent.disabled ? null : updateActiveTabIndex(index, true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{React.createElement(Icons[childComponent.iconName] || Icons.MissingIcon, {
|
{React.createElement(Icons[childComponent.iconName] || Icons.MissingIcon, {
|
||||||
className: classnames({
|
className: classnames({
|
||||||
'text-primary-active': true,
|
'text-primary': true,
|
||||||
'ohif-disabled': childComponent.disabled,
|
'ohif-disabled': childComponent.disabled,
|
||||||
}),
|
}),
|
||||||
style: {
|
style: {
|
||||||
@ -352,7 +352,7 @@ const SidePanel = ({
|
|||||||
data-cy={`side-panel-header-${side}`}
|
data-cy={`side-panel-header-${side}`}
|
||||||
>
|
>
|
||||||
{React.createElement(Icons[openStateIconName[side]] || Icons.MissingIcon, {
|
{React.createElement(Icons[openStateIconName[side]] || Icons.MissingIcon, {
|
||||||
className: 'text-primary-active',
|
className: 'text-primary',
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -365,7 +365,7 @@ const SidePanel = ({
|
|||||||
<>
|
<>
|
||||||
{getCloseIcon()}
|
{getCloseIcon()}
|
||||||
<div className={classnames('flex grow justify-center')}>
|
<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) => {
|
{tabs.map((tab, tabIndex) => {
|
||||||
const { disabled } = tab;
|
const { disabled } = tab;
|
||||||
return (
|
return (
|
||||||
@ -401,7 +401,7 @@ const SidePanel = ({
|
|||||||
>
|
>
|
||||||
{React.createElement(Icons[tab.iconName] || Icons.MissingIcon, {
|
{React.createElement(Icons[tab.iconName] || Icons.MissingIcon, {
|
||||||
className: classnames({
|
className: classnames({
|
||||||
'text-primary-active': true,
|
'text-primary': true,
|
||||||
'ohif-disabled': disabled,
|
'ohif-disabled': disabled,
|
||||||
}),
|
}),
|
||||||
style: {
|
style: {
|
||||||
@ -429,7 +429,7 @@ const SidePanel = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames(
|
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`}
|
data-cy={`${tabs[0].name}-btn`}
|
||||||
onClick={() => updatePanelOpen(!panelOpen)}
|
onClick={() => updatePanelOpen(!panelOpen)}
|
||||||
|
|||||||
@ -111,7 +111,7 @@ const Thumbnail = ({
|
|||||||
<TooltipContent side="right">
|
<TooltipContent side="right">
|
||||||
<div className="flex flex-1 flex-row">
|
<div className="flex flex-1 flex-row">
|
||||||
<div className="flex-2 flex items-center justify-center pr-4">
|
<div className="flex-2 flex items-center justify-center pr-4">
|
||||||
<Icons.InfoLink className="text-primary-active" />
|
<Icons.InfoLink className="text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-1 flex-col">
|
<div className="flex flex-1 flex-col">
|
||||||
<span>
|
<span>
|
||||||
@ -226,7 +226,7 @@ const Thumbnail = ({
|
|||||||
<TooltipContent side="right">
|
<TooltipContent side="right">
|
||||||
<div className="flex flex-1 flex-row">
|
<div className="flex flex-1 flex-row">
|
||||||
<div className="flex-2 flex items-center justify-center pr-4">
|
<div className="flex-2 flex items-center justify-center pr-4">
|
||||||
<Icons.InfoLink className="text-primary-active" />
|
<Icons.InfoLink className="text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-1 flex-col">
|
<div className="flex flex-1 flex-col">
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
const getToggledClassName = isToggled => {
|
const getToggledClassName = isToggled => {
|
||||||
return isToggled
|
return isToggled
|
||||||
? '!text-primary-active'
|
? '!text-primary'
|
||||||
: '!text-common-bright hover:!bg-primary-dark hover:text-primary-light';
|
: '!text-common-bright hover:!bg-primary-dark hover:text-primary-light';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
42
tests/SEGHydrationFromMPR.spec.ts
Normal 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
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -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('study-browser-thumbnail-no-image').dblclick();
|
||||||
|
|
||||||
await page.getByTestId('yes-hydrate-btn').click();
|
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('Layout').click();
|
||||||
await page.getByTestId('Axial Primary').click();
|
await page.getByTestId('Axial Primary').click();
|
||||||
@ -20,6 +20,6 @@ test('should properly display MPR for MR', async ({ page }) => {
|
|||||||
await checkForScreenshot(
|
await checkForScreenshot(
|
||||||
page,
|
page,
|
||||||
page,
|
page,
|
||||||
screenShotPaths.segHydrationMPR.segPostHydrationMPRAxialPrimary
|
screenShotPaths.segHydrationThenMPR.segPostHydrationMPRAxialPrimary
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 239 KiB |
|
After Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 301 KiB |
@ -79,10 +79,16 @@ const screenShotPaths = {
|
|||||||
segPreHydration: 'segPreHydration.png',
|
segPreHydration: 'segPreHydration.png',
|
||||||
segJumpToSegment: 'segJumpToSegment.png',
|
segJumpToSegment: 'segJumpToSegment.png',
|
||||||
},
|
},
|
||||||
segHydrationMPR: {
|
segHydrationThenMPR: {
|
||||||
segPostHydration: 'segPostHydration.png',
|
segPostHydration: 'segPostHydration.png',
|
||||||
segPostHydrationMPRAxialPrimary: 'segPostHydrationMPRAxialPrimary.png',
|
segPostHydrationMPRAxialPrimary: 'segPostHydrationMPRAxialPrimary.png',
|
||||||
},
|
},
|
||||||
|
segHydrationFromMPR: {
|
||||||
|
mprBeforeSEG: 'mprBeforeSEG.png',
|
||||||
|
mprAfterSEG: 'mprAfterSEG.png',
|
||||||
|
mprAfterSegHydrated: 'mprAfterSegHydrated.png',
|
||||||
|
mprAfterSegHydratedAfterLayoutChange: 'mprAfterSegHydratedAfterLayoutChange.png',
|
||||||
|
},
|
||||||
rtHydration: {
|
rtHydration: {
|
||||||
rtPostHydration: 'rtPostHydration.png',
|
rtPostHydration: 'rtPostHydration.png',
|
||||||
rtPreHydration: 'rtPreHydration.png',
|
rtPreHydration: 'rtPreHydration.png',
|
||||||
|
|||||||
56
yarn.lock
@ -1393,10 +1393,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
||||||
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
|
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
|
||||||
|
|
||||||
"@cornerstonejs/adapters@^3.8.4":
|
"@cornerstonejs/adapters@^3.8.5":
|
||||||
version "3.8.4"
|
version "3.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-3.8.4.tgz#14e22bef3abaf14cde52ecf8b05b43a687b0dece"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-3.8.5.tgz#d516a8fba29d200b47d77fe0ef586ed8962506de"
|
||||||
integrity sha512-TFTvNZQBW54rEcyjDfyHZYYvIPHVGZoUCtjW3isHfpoXkjHz2cliwC55e0kcNJ0zbyI6H41c4VsYH8h2MTYOdA==
|
integrity sha512-dQlKKSCbV6V3QpivDdZZiI7N792l1VqsOj8H+e8iiarpy6MvwiEvck2u50/GBhto9D/U9QvRkZVs9TH0ewUs5w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime-corejs2" "^7.17.8"
|
"@babel/runtime-corejs2" "^7.17.8"
|
||||||
buffer "^6.0.3"
|
buffer "^6.0.3"
|
||||||
@ -1404,10 +1404,10 @@
|
|||||||
gl-matrix "^3.4.3"
|
gl-matrix "^3.4.3"
|
||||||
ndarray "^1.0.19"
|
ndarray "^1.0.19"
|
||||||
|
|
||||||
"@cornerstonejs/ai@^3.8.4":
|
"@cornerstonejs/ai@^3.8.5":
|
||||||
version "3.8.4"
|
version "3.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/ai/-/ai-3.8.4.tgz#c998cdfbd1cae01e3ea9ed61810c657d91112f75"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/ai/-/ai-3.8.5.tgz#8f6be57b7bc660afacd52b34ce56a71c696419ed"
|
||||||
integrity sha512-scc2srdL1BqNTSFEro4y7UlNBsPtBVSF31eT4SC1psxZQcr/iyaCKuOQg/hglk1yobFkdIKvjRAvvqvYmGduQw==
|
integrity sha512-9QR7PmTtkeXZOjDFh9WulLJGnraWLaM/g/qNB0ZaPKDMo1PLT7/tHn1F6wsyT2cmpEb3/G5towdxtEvhvlpm/w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime-corejs2" "^7.17.8"
|
"@babel/runtime-corejs2" "^7.17.8"
|
||||||
buffer "^6.0.3"
|
buffer "^6.0.3"
|
||||||
@ -1443,20 +1443,20 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0"
|
||||||
integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg==
|
integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg==
|
||||||
|
|
||||||
"@cornerstonejs/core@^3.8.4":
|
"@cornerstonejs/core@^3.8.5":
|
||||||
version "3.8.4"
|
version "3.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-3.8.4.tgz#bbc6863cb3f2e78f5f4c7a1a5a9287c091287228"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-3.8.5.tgz#577bd32f0a3da36f895bf89c821b02a4cf97e225"
|
||||||
integrity sha512-8spQvERZRn4I/0EhzTNZ5ruPBrVn5F4msb/q0ttBtiVEa9JaVwoWh3yEcO4q5NpK7t1BhpfSPMKwU98Dy02ugQ==
|
integrity sha512-x3LZwZUqhVldChpwtLqNwUWx8eXq9q1ndwrx5lVpnX4ZUT9qQIAro01Ih8DPiXlLb1NXbN3sG4e/x/TxuQ+5bw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@kitware/vtk.js" "32.12.1"
|
"@kitware/vtk.js" "32.12.1"
|
||||||
comlink "^4.4.1"
|
comlink "^4.4.1"
|
||||||
gl-matrix "^3.4.3"
|
gl-matrix "^3.4.3"
|
||||||
loglevel "^1.9.2"
|
loglevel "^1.9.2"
|
||||||
|
|
||||||
"@cornerstonejs/dicom-image-loader@^3.8.4":
|
"@cornerstonejs/dicom-image-loader@^3.8.5":
|
||||||
version "3.8.4"
|
version "3.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-3.8.4.tgz#c4f7af80a40a1f70deff37ddfc50447cc42422e7"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-3.8.5.tgz#963a3174af3603e4beb096d448671442a0d5f10a"
|
||||||
integrity sha512-N2D4U8JDhen+dCY1HrLOoK8W3CD2AAf/Cd+GiJMEGjtrihIuQ+Ha+hZTVQzaDMc7v2Ruvx2DpJ7i9HUJXoyWHw==
|
integrity sha512-yuLLU2sbtFWueNKVbeYBddPoJnfhrUQ+rRnXJb+y9iczwZMQLy4tYyk3Q5INo+/nIdwCVeZg7P9qORZERsBivw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cornerstonejs/codec-charls" "^1.2.3"
|
"@cornerstonejs/codec-charls" "^1.2.3"
|
||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
|
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
|
||||||
@ -1468,25 +1468,25 @@
|
|||||||
pako "^2.0.4"
|
pako "^2.0.4"
|
||||||
uuid "^9.0.0"
|
uuid "^9.0.0"
|
||||||
|
|
||||||
"@cornerstonejs/labelmap-interpolation@^3.8.4":
|
"@cornerstonejs/labelmap-interpolation@^3.8.5":
|
||||||
version "3.8.4"
|
version "3.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/labelmap-interpolation/-/labelmap-interpolation-3.8.4.tgz#5c18761a7bbb8e3e76bfbf22b2205c1c5e8bee6e"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/labelmap-interpolation/-/labelmap-interpolation-3.8.5.tgz#19e8142362d5b83540bcb764306cd6bdf277c9ce"
|
||||||
integrity sha512-KKDTeVJg1s0trplkaHglBm0foQ3XL9BhsTfatFCTzgIOCT0wolrKbr+xAKIeZuBQifiFazDWpYaeBHRbkWEEMA==
|
integrity sha512-u80OTJE8eju+tZ+S/N1b/HTIw2mPjEE2o5u3T/7/w90FX7OaxJJiMYjPPfnSZWXjE2HhRu0gEAKHOU3dr8pL4w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@itk-wasm/morphological-contour-interpolation" "1.1.0"
|
"@itk-wasm/morphological-contour-interpolation" "1.1.0"
|
||||||
itk-wasm "1.0.0-b.165"
|
itk-wasm "1.0.0-b.165"
|
||||||
|
|
||||||
"@cornerstonejs/polymorphic-segmentation@^3.8.4":
|
"@cornerstonejs/polymorphic-segmentation@^3.8.5":
|
||||||
version "3.8.4"
|
version "3.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/polymorphic-segmentation/-/polymorphic-segmentation-3.8.4.tgz#5c7578eedd4e7ac78250f06ccecb3950ddf5ba28"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/polymorphic-segmentation/-/polymorphic-segmentation-3.8.5.tgz#5289f1b9d7f8fdbf3164330c4d30dd03fffa16be"
|
||||||
integrity sha512-+3o280NOniUkG9GcWTeTi6x3qy8Rm7WpYFycWpxXIufJzVudJi0zAR+EIWDq/uvTQ3DH1SrBDe4wzYNqD+yirw==
|
integrity sha512-6L9a9f36CnUIuGsydA8kkkC0z1Gjid4KfclCQoYYaxcsLWLDpba5usFERmxCLIm9VyBsA3POqesIGDqWTgJmFg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@icr/polyseg-wasm" "0.4.0"
|
"@icr/polyseg-wasm" "0.4.0"
|
||||||
|
|
||||||
"@cornerstonejs/tools@^3.8.4":
|
"@cornerstonejs/tools@^3.8.5":
|
||||||
version "3.8.4"
|
version "3.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-3.8.4.tgz#4676ba9d7fcae22f236bab2314118b5d06cc973e"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-3.8.5.tgz#15fe20fa051dc12084141515c2a715a4f0a9a858"
|
||||||
integrity sha512-yr8RLtnxGh9BsPOFpX+zwT++UGJDqWMo7noWGzxkuISDriLr+HGZI8gL910Mw62obaF8r9qpeOSvSOzEHN3nSw==
|
integrity sha512-aTaziRHJakCCxvbHhbrSkEuiamURGakcK83KXGJPjSdStWC/2cVpkCzPxc8V7FZBIstsvo5pgciXSpNc6Yt7Og==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/offscreencanvas" "2019.7.3"
|
"@types/offscreencanvas" "2019.7.3"
|
||||||
comlink "^4.4.1"
|
comlink "^4.4.1"
|
||||||
|
|||||||