Compare commits
33
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f412dc254b | ||
|
|
c0ed128fb7 | ||
|
|
c829270c80 | ||
|
|
a16d612a8a | ||
|
|
5f94ebe81a | ||
|
|
8218a9d034 | ||
|
|
4b94f18f7c | ||
|
|
8e7e9ca02f | ||
|
|
cd4e6408bc | ||
|
|
c1fced0573 | ||
|
|
e41019f2b3 | ||
|
|
383f45823c | ||
|
|
6337d2e9cb | ||
|
|
e987710908 | ||
|
|
7dbe5e37b0 | ||
|
|
ff974e2a03 | ||
|
|
cccf74d6ea | ||
|
|
4be8495335 | ||
|
|
1cb8d328a4 | ||
|
|
2c8dda01ae | ||
|
|
4e185e4b21 | ||
|
|
4732e8093e | ||
|
|
984471e972 | ||
|
|
25cb71b3f6 | ||
|
|
0e75f6d62c | ||
|
|
d75b3e25b6 | ||
|
|
0bb9b17636 | ||
|
|
b21ee9bc5b | ||
|
|
7ed0f6d53b | ||
|
|
98f8187dfe | ||
|
|
4643d6dc40 | ||
|
|
258b353269 | ||
|
|
0f39496290 |
No files matched your search
@@ -58,3 +58,5 @@ tests/test-results/
|
||||
tests/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
**/.claude/settings.local.json
|
||||
Vendored
+3
-1
@@ -109,6 +109,8 @@
|
||||
"wado",
|
||||
"wadors",
|
||||
"wadouri",
|
||||
"workerpool"
|
||||
"workerpool",
|
||||
"Colorbar",
|
||||
"Colorbars"
|
||||
]
|
||||
}
|
||||
+106
@@ -3,6 +3,112 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add locale files for Russian language, translated texts in locale json files to Russian language. ([#4984](https://github.com/OHIF/Viewers/issues/4984)) ([c829270](https://github.com/OHIF/Viewers/commit/c829270c80c0899132aff0294b8e7386475dc71c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **i18n:** add Arabic translations ([#5013](https://github.com/OHIF/Viewers/issues/5013)) ([8218a9d](https://github.com/OHIF/Viewers/commit/8218a9d0346fe9a5473420e26a0b8ab389f4ef58))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improve menu modals customizations ([#5011](https://github.com/OHIF/Viewers/issues/5011)) ([7dbe5e3](https://github.com/OHIF/Viewers/commit/7dbe5e37b06056fdd5262a131417013d112c3d30))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **referencelines:** and bring back the notification for worker updates but disallow duplication ([#5005](https://github.com/OHIF/Viewers/issues/5005)) ([98f8187](https://github.com/OHIF/Viewers/commit/98f8187dfeb8aec6eca145da75c855aec0da4e1d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **template:** Update mode template to match current implementation (OHI-1833) ([#4998](https://github.com/OHIF/Viewers/issues/4998)) ([0f39496](https://github.com/OHIF/Viewers/commit/0f394962904808a140221dbae063ca5dcf0df36b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
|
||||
|
||||
+88
@@ -3,6 +3,94 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@externals/devDependencies",
|
||||
"description": "External dev dependencies - put dev build dependencies here",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"engines": {
|
||||
|
||||
@@ -3,6 +3,94 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@externals/dicom-microscopy-viewer",
|
||||
"description": "External reference to dicom-microscopy-viewer",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dicom-microscopy-viewer": "^0.46.1"
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
},
|
||||
"addOns/externals/devDependencies": {
|
||||
"name": "@externals/devDependencies",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
@@ -127,22 +127,22 @@
|
||||
},
|
||||
"addOns/externals/dicom-microscopy-viewer": {
|
||||
"name": "@externals/dicom-microscopy-viewer",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"dicom-microscopy-viewer": "^0.46.1",
|
||||
},
|
||||
},
|
||||
"extensions/cornerstone": {
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.9.5",
|
||||
"@cornerstonejs/ai": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/labelmap-interpolation": "^3.9.5",
|
||||
"@cornerstonejs/polymorphic-segmentation": "^3.9.5",
|
||||
"@cornerstonejs/tools": "^3.9.5",
|
||||
"@cornerstonejs/adapters": "^3.11.6",
|
||||
"@cornerstonejs/ai": "^3.11.6",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@cornerstonejs/labelmap-interpolation": "^3.11.6",
|
||||
"@cornerstonejs/polymorphic-segmentation": "^3.11.6",
|
||||
"@cornerstonejs/tools": "^3.11.6",
|
||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
@@ -159,9 +159,9 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.9.5",
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.11.6",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.21",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -173,19 +173,19 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-pmap": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-pmap",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/adapters": "^3.11.6",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "^2.19.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/i18n": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/i18n": "3.11.0-beta.23",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -196,16 +196,16 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-rt": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-rt",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"react-color": "^2.19.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/i18n": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/i18n": "3.11.0-beta.23",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -216,19 +216,19 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-seg": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-seg",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/adapters": "^3.11.6",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "^2.19.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/i18n": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/i18n": "3.11.0-beta.23",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -239,19 +239,19 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-sr": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-sr",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/tools": "^3.9.5",
|
||||
"@cornerstonejs/adapters": "^3.11.6",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@cornerstonejs/tools": "^3.11.6",
|
||||
"classnames": "^2.3.2",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.158",
|
||||
"@ohif/extension-measurement-tracking": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.23",
|
||||
"@ohif/extension-measurement-tracking": "3.11.0-beta.23",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -261,19 +261,19 @@
|
||||
},
|
||||
"extensions/cornerstone-dynamic-volume": {
|
||||
"name": "@ohif/extension-cornerstone-dynamic-volume",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/tools": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@cornerstonejs/tools": "^3.11.6",
|
||||
"classnames": "^2.3.2",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/i18n": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/i18n": "3.11.0-beta.23",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.21",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -283,7 +283,7 @@
|
||||
},
|
||||
"extensions/default": {
|
||||
"name": "@ohif/extension-default",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/calculate-suv": "^1.1.0",
|
||||
@@ -291,8 +291,8 @@
|
||||
"lodash.uniqby": "^4.7.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/i18n": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/i18n": "3.11.0-beta.23",
|
||||
"dcmjs": "*",
|
||||
"dicomweb-client": "^0.10.4",
|
||||
"prop-types": "^15.6.2",
|
||||
@@ -306,7 +306,7 @@
|
||||
},
|
||||
"extensions/dicom-microscopy": {
|
||||
"name": "@ohif/extension-dicom-microscopy",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/codec-charls": "^1.2.3",
|
||||
@@ -317,10 +317,10 @@
|
||||
"mathjs": "^12.4.2",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/i18n": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/i18n": "3.11.0-beta.23",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -331,14 +331,14 @@
|
||||
},
|
||||
"extensions/dicom-pdf": {
|
||||
"name": "@ohif/extension-dicom-pdf",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"classnames": "^2.3.2",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -348,14 +348,14 @@
|
||||
},
|
||||
"extensions/dicom-video": {
|
||||
"name": "@ohif/extension-dicom-video",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"classnames": "^2.3.2",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -365,20 +365,20 @@
|
||||
},
|
||||
"extensions/measurement-tracking": {
|
||||
"name": "@ohif/extension-measurement-tracking",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"@xstate/react": "^3.2.2",
|
||||
"xstate": "^4.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/tools": "^3.9.5",
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@cornerstonejs/tools": "^3.11.6",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"classnames": "^2.3.2",
|
||||
"dcmjs": "*",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
@@ -391,14 +391,14 @@
|
||||
},
|
||||
"extensions/test-extension": {
|
||||
"name": "@ohif/extension-test",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"classnames": "^2.3.2",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"dcmjs": "0.38.0",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -408,7 +408,7 @@
|
||||
},
|
||||
"extensions/tmtv": {
|
||||
"name": "@ohif/extension-tmtv",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"classnames": "^2.3.2",
|
||||
@@ -425,7 +425,7 @@
|
||||
},
|
||||
"modes/basic-dev-mode": {
|
||||
"name": "@ohif/mode-basic-dev-mode",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"i18next": "^17.0.3",
|
||||
@@ -435,17 +435,17 @@
|
||||
"webpack-merge": "^5.7.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-pdf": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-video": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-pdf": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-video": "3.11.0-beta.23",
|
||||
},
|
||||
},
|
||||
"modes/basic-test-mode": {
|
||||
"name": "@ohif/mode-test",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"i18next": "^17.0.3",
|
||||
@@ -455,19 +455,19 @@
|
||||
"webpack-merge": "^5.7.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-pdf": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-video": "3.10.0-beta.158",
|
||||
"@ohif/extension-measurement-tracking": "3.10.0-beta.158",
|
||||
"@ohif/extension-test": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-pdf": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-video": "3.11.0-beta.23",
|
||||
"@ohif/extension-measurement-tracking": "3.11.0-beta.23",
|
||||
"@ohif/extension-test": "3.11.0-beta.23",
|
||||
},
|
||||
},
|
||||
"modes/longitudinal": {
|
||||
"name": "@ohif/mode-longitudinal",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"i18next": "^17.0.3",
|
||||
@@ -477,32 +477,32 @@
|
||||
"webpack-merge": "^5.7.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-pdf": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-video": "3.10.0-beta.158",
|
||||
"@ohif/extension-measurement-tracking": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-pdf": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-video": "3.11.0-beta.23",
|
||||
"@ohif/extension-measurement-tracking": "3.11.0-beta.23",
|
||||
},
|
||||
},
|
||||
"modes/microscopy": {
|
||||
"name": "@ohif/mode-microscopy",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"i18next": "^17.0.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-microscopy": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-microscopy": "3.11.0-beta.23",
|
||||
},
|
||||
},
|
||||
"modes/preclinical-4d": {
|
||||
"name": "@ohif/mode-preclinical-4d",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
},
|
||||
@@ -521,7 +521,7 @@
|
||||
},
|
||||
"modes/segmentation": {
|
||||
"name": "@ohif/mode-segmentation",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"i18next": "^17.0.3",
|
||||
@@ -565,7 +565,7 @@
|
||||
},
|
||||
"modes/tmtv": {
|
||||
"name": "@ohif/mode-tmtv",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"i18next": "^17.0.3",
|
||||
@@ -586,32 +586,32 @@
|
||||
},
|
||||
"platform/app": {
|
||||
"name": "@ohif/app",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/codec-charls": "^1.2.3",
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.9.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.11.6",
|
||||
"@emotion/serialize": "^1.1.3",
|
||||
"@ohif/core": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.10.0-beta.158",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.158",
|
||||
"@ohif/extension-default": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-microscopy": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-pdf": "3.10.0-beta.158",
|
||||
"@ohif/extension-dicom-video": "3.10.0-beta.158",
|
||||
"@ohif/extension-test": "3.10.0-beta.158",
|
||||
"@ohif/i18n": "3.10.0-beta.158",
|
||||
"@ohif/mode-basic-dev-mode": "3.10.0-beta.158",
|
||||
"@ohif/mode-longitudinal": "3.10.0-beta.158",
|
||||
"@ohif/mode-microscopy": "3.10.0-beta.158",
|
||||
"@ohif/mode-test": "3.10.0-beta.158",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@ohif/ui-next": "3.10.0-beta.158",
|
||||
"@ohif/core": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.11.0-beta.23",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.11.0-beta.23",
|
||||
"@ohif/extension-default": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-microscopy": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-pdf": "3.11.0-beta.23",
|
||||
"@ohif/extension-dicom-video": "3.11.0-beta.23",
|
||||
"@ohif/extension-test": "3.11.0-beta.23",
|
||||
"@ohif/i18n": "3.11.0-beta.23",
|
||||
"@ohif/mode-basic-dev-mode": "3.11.0-beta.23",
|
||||
"@ohif/mode-longitudinal": "3.11.0-beta.23",
|
||||
"@ohif/mode-microscopy": "3.11.0-beta.23",
|
||||
"@ohif/mode-test": "3.11.0-beta.23",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"@ohif/ui-next": "3.11.0-beta.23",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"classnames": "^2.3.2",
|
||||
@@ -652,7 +652,7 @@
|
||||
},
|
||||
"platform/cli": {
|
||||
"name": "@ohif/cli",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"bin": {
|
||||
"ohif-cli": "src/index.js",
|
||||
},
|
||||
@@ -676,7 +676,7 @@
|
||||
},
|
||||
"platform/core": {
|
||||
"name": "@ohif/core",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"dcmjs": "*",
|
||||
@@ -701,16 +701,16 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.9.5",
|
||||
"@ohif/ui": "3.10.0-beta.158",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.11.6",
|
||||
"@ohif/ui": "3.11.0-beta.23",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"dicom-parser": "^1.8.21",
|
||||
},
|
||||
},
|
||||
"platform/i18n": {
|
||||
"name": "@ohif/i18n",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"i18next-locize-backend": "^2.0.0",
|
||||
@@ -735,7 +735,7 @@
|
||||
},
|
||||
"platform/ui": {
|
||||
"name": "@ohif/ui",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@testing-library/react": "^13.1.0",
|
||||
"browser-detect": "^0.2.28",
|
||||
@@ -795,7 +795,7 @@
|
||||
},
|
||||
"platform/ui-next": {
|
||||
"name": "@ohif/ui-next",
|
||||
"version": "3.11.0-beta.5",
|
||||
"version": "3.11.0-beta.23",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "^1.2.0",
|
||||
"@radix-ui/react-checkbox": "^1.1.1",
|
||||
@@ -1106,9 +1106,9 @@
|
||||
|
||||
"@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="],
|
||||
|
||||
"@cornerstonejs/adapters": ["@cornerstonejs/adapters@3.9.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.9.5", "@cornerstonejs/tools": "^3.9.5" } }, "sha512-I8nPFmwNBqBwrH26w06gZfsJaHyZPN4nZC8GO7IerkVSkyYXgFEFC5iRdVuU+Kr94nWm8zPKyLqHx7hAeNudgw=="],
|
||||
"@cornerstonejs/adapters": ["@cornerstonejs/adapters@3.11.6", "", { "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.11.6", "@cornerstonejs/tools": "^3.11.6" } }, "sha512-EYLkBXfXF6UcmcZPitIkDNe02HdKykiFDnG9CtXPJkTSqFfCZnkF4Mqe4WN/husc9wia6BUMlESQRBYEgBithA=="],
|
||||
|
||||
"@cornerstonejs/ai": ["@cornerstonejs/ai@3.9.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.9.5", "@cornerstonejs/tools": "^3.9.5" } }, "sha512-AgVPLVoYJeefPcp6tf5UNfS8qoEJc8VxrqZab6f+LybhSa2se52h+57rCB5Ihd7SziNvWyIdux7v1Iy3GiZm+g=="],
|
||||
"@cornerstonejs/ai": ["@cornerstonejs/ai@3.11.6", "", { "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.11.6", "@cornerstonejs/tools": "^3.11.6" } }, "sha512-F17IDzbn1PC2Zt1S2RS4OaLqO5ccC71Mmk2/9tU3wQS7g7qmyh8JzyB3B6t3iHkSlv20p3HNh8MqSug7zHOcrg=="],
|
||||
|
||||
"@cornerstonejs/calculate-suv": ["@cornerstonejs/calculate-suv@1.1.0", "", {}, "sha512-Q9XraiDJif9aMFArD2iEuDO/HXbcRVCqB7KfaHgDrdTTjgDFovS91Psbdim7crypRSvE6dh/+HKeFNHdvNkA6w=="],
|
||||
|
||||
@@ -1120,15 +1120,15 @@
|
||||
|
||||
"@cornerstonejs/codec-openjph": ["@cornerstonejs/codec-openjph@2.4.7", "", {}, "sha512-qvP4q4JDib7mi9r7LqKOwqz7YZ8gjtDX4ZCezeYf8+eb7MBXCz5uXAMeVF3yz9Axw4XiIMdB/pqXkm8tqCl13w=="],
|
||||
|
||||
"@cornerstonejs/core": ["@cornerstonejs/core@3.9.5", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "^4.4.1", "gl-matrix": "^3.4.3", "loglevel": "^1.9.2" } }, "sha512-EwXeTNSlL7UnXmj8o1DigNZOsVbhtNDKT2Bbi66jhnzZQm+rTPyYeTva+FnnqvpxBb/6QqwDEJ8gwlXwV0Rk5w=="],
|
||||
"@cornerstonejs/core": ["@cornerstonejs/core@3.11.6", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "^4.4.1", "gl-matrix": "^3.4.3", "loglevel": "^1.9.2" } }, "sha512-8IlnDj7MZV/pfbtdF5gdpL53hLkriBgqcTY4hBIY4iW1A1plmwADsbVXzgyXl3Bi63aovmJhiQZ1VtQx0TzZGQ=="],
|
||||
|
||||
"@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@3.9.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.9.5" } }, "sha512-g0GZNkZ6PBRCl/dvi6NoQlmpmZhuLLeI4cdiFwV0Ug81HPkHdn8FQaqgy1XPOMknLSL6sJGZsJN2l2nW5COQSA=="],
|
||||
"@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@3.11.6", "", { "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.11.6" } }, "sha512-SJZ/LAw2gIyxzyQTEFWqBtyZELakqjcHOJnFxhkEmmpuJdpX4tgtV4k+6fnbKTg9OhybyP3prouMVPj5myjCZA=="],
|
||||
|
||||
"@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@3.9.5", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "^3.9.5", "@cornerstonejs/tools": "^3.9.5", "@kitware/vtk.js": "32.12.1" } }, "sha512-U7XnaqrrEHGvH3kKdTwGiwLYaydxp5GVo0aqVvZaRDXMXfP3tbOpCfMQcqD+bv4Sh+Rr0719O5aDJ0+ENpA/MA=="],
|
||||
"@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@3.11.6", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "^3.11.6", "@cornerstonejs/tools": "^3.11.6", "@kitware/vtk.js": "32.12.1" } }, "sha512-N55MxFxlRNXHVKpsJgeO8Z2aEURINxdmyPvGODGgYjZSOXZ+iR12Aly/iZHW9d/crq4GU440d5B6EmEZ6iuW8w=="],
|
||||
|
||||
"@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@3.9.5", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.9.5", "@cornerstonejs/tools": "^3.9.5", "@kitware/vtk.js": "32.12.1" } }, "sha512-6ucJkb0X5CBBx00g3CzJGuLd6x4T99bPyKXa+y9Bek3jC+RyKI4vySS5RRKtQ4N59yuealZkBvV7dT0FseE8og=="],
|
||||
"@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@3.11.6", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.11.6", "@cornerstonejs/tools": "^3.11.6", "@kitware/vtk.js": "32.12.1" } }, "sha512-dk+G12MqqmKSd2Rz8gHo1piEDAeijBcz1EAdF+50UQlUEds1fczHwFkMrh9mlHc9treyHi5+QEIr2MKRmwaJIw=="],
|
||||
|
||||
"@cornerstonejs/tools": ["@cornerstonejs/tools@3.9.5", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "^4.4.1", "lodash.get": "^4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "^3.9.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-vdPsm4szolXmyBdDNTB1gyRCP5EEicsf+TOsTU5P6QYWR7cB+nyio7GeGK5zFQrUi0hb2a2qkVjjLQLQH9inAw=="],
|
||||
"@cornerstonejs/tools": ["@cornerstonejs/tools@3.11.6", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "^4.4.1", "lodash.get": "^4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "^3.11.6", "@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-Sp1QkEaA952GgZikTNzyj125FzpYtVAYbeqCOn0E3WS7K1yw2XPuRbSCTRkRcV/J66ZoeKl4KgZVrhDlxJGAnw=="],
|
||||
|
||||
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
09ac43f297fa81101d3a3eaa9b5cfef8e01ea214
|
||||
c829270c80c0899132aff0294b8e7386475dc71c
|
||||
@@ -3,6 +3,97 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-pmap",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "DICOM Parametric Map read workflow",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -33,10 +33,10 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.15",
|
||||
"@ohif/extension-default": "3.11.0-beta.15",
|
||||
"@ohif/i18n": "3.11.0-beta.15",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.26",
|
||||
"@ohif/extension-default": "3.11.0-beta.26",
|
||||
"@ohif/i18n": "3.11.0-beta.26",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/adapters": "^3.11.6",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { utils } from '@ohif/core';
|
||||
import { utils, Types as OhifTypes } from '@ohif/core';
|
||||
import i18n from '@ohif/i18n';
|
||||
import { metaData, cache, utilities as csUtils, volumeLoader } from '@cornerstonejs/core';
|
||||
import { adaptersPMAP } from '@cornerstonejs/adapters';
|
||||
import { SOPClassHandlerId } from './id';
|
||||
import { dicomLoaderService } from '@ohif/extension-cornerstone';
|
||||
|
||||
const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
|
||||
import { dicomLoaderService, VOLUME_LOADER_SCHEME } from '@ohif/extension-cornerstone';
|
||||
const sopClassUids = ['1.2.840.10008.5.1.4.1.1.30'];
|
||||
|
||||
function _getDisplaySetsFromSeries(
|
||||
@@ -57,7 +56,8 @@ function _getDisplaySetsFromSeries(
|
||||
wadoRoot,
|
||||
wadoUriRoot,
|
||||
wadoUri,
|
||||
isOverlayDisplaySet: true,
|
||||
supportsWindowLevel: true,
|
||||
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('PMAP')}`,
|
||||
};
|
||||
|
||||
const referencedSeriesSequence = instance.ReferencedSeriesSequence;
|
||||
@@ -227,7 +227,8 @@ async function _loadParametricMap({ displaySet, headers }: withAppTypes) {
|
||||
return derivedVolume;
|
||||
}
|
||||
|
||||
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
||||
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
|
||||
const { servicesManager, extensionManager } = params;
|
||||
const getDisplaySetsFromSeries = instances => {
|
||||
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,97 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-rt",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "DICOM RT read workflow",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -33,10 +33,10 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.15",
|
||||
"@ohif/extension-default": "3.11.0-beta.15",
|
||||
"@ohif/i18n": "3.11.0-beta.15",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.26",
|
||||
"@ohif/extension-default": "3.11.0-beta.26",
|
||||
"@ohif/i18n": "3.11.0-beta.26",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
|
||||
@@ -27,7 +27,7 @@ const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
|
||||
// it will be in the correct position zoom and pan
|
||||
commandsManager.runCommand('updateStoredPositionPresentation', {
|
||||
viewportId,
|
||||
displaySetInstanceUID: referencedDisplaySet.displaySetInstanceUID,
|
||||
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
|
||||
});
|
||||
|
||||
viewportGridService.setDisplaySetsForViewport({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { utils } from '@ohif/core';
|
||||
import { utils, Types as OhifTypes } from '@ohif/core';
|
||||
import i18n from '@ohif/i18n';
|
||||
|
||||
import { SOPClassHandlerId } from './id';
|
||||
import loadRTStruct from './loadRTStruct';
|
||||
@@ -53,6 +54,7 @@ function _getDisplaySetsFromSeries(
|
||||
wadoUriRoot,
|
||||
wadoUri,
|
||||
isOverlayDisplaySet: true,
|
||||
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('RTSTRUCT')}`,
|
||||
};
|
||||
|
||||
let referencedSeriesSequence = instance.ReferencedSeriesSequence;
|
||||
@@ -94,12 +96,19 @@ function _getDisplaySetsFromSeries(
|
||||
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
|
||||
}
|
||||
|
||||
displaySet.load = ({ headers }) => _load(displaySet, servicesManager, extensionManager, headers);
|
||||
displaySet.load = ({ headers, createSegmentation = true }) =>
|
||||
_load(displaySet, servicesManager, extensionManager, headers, createSegmentation);
|
||||
|
||||
return [displaySet];
|
||||
}
|
||||
|
||||
function _load(rtDisplaySet, servicesManager: AppTypes.ServicesManager, extensionManager, headers) {
|
||||
function _load(
|
||||
rtDisplaySet,
|
||||
servicesManager: AppTypes.ServicesManager,
|
||||
extensionManager,
|
||||
headers,
|
||||
createSegmentation = true
|
||||
) {
|
||||
const { SOPInstanceUID } = rtDisplaySet;
|
||||
const { segmentationService } = servicesManager.services;
|
||||
if (
|
||||
@@ -121,16 +130,21 @@ function _load(rtDisplaySet, servicesManager: AppTypes.ServicesManager, extensio
|
||||
rtDisplaySet.structureSet = structureSet;
|
||||
}
|
||||
|
||||
segmentationService
|
||||
.createSegmentationForRTDisplaySet(rtDisplaySet)
|
||||
.then(() => {
|
||||
rtDisplaySet.loading = false;
|
||||
resolve();
|
||||
})
|
||||
.catch(error => {
|
||||
rtDisplaySet.loading = false;
|
||||
reject(error);
|
||||
});
|
||||
if (createSegmentation) {
|
||||
segmentationService
|
||||
.createSegmentationForRTDisplaySet(rtDisplaySet)
|
||||
.then(() => {
|
||||
rtDisplaySet.loading = false;
|
||||
resolve();
|
||||
})
|
||||
.catch(error => {
|
||||
rtDisplaySet.loading = false;
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
rtDisplaySet.loading = false;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
return loadPromises[SOPInstanceUID];
|
||||
@@ -171,20 +185,14 @@ function _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
|
||||
return ReferencedSeriesSequence;
|
||||
}
|
||||
|
||||
function _segmentationExistsInCache(
|
||||
rtDisplaySet,
|
||||
segmentationService: AppTypes.SegmentationService
|
||||
) {
|
||||
function _segmentationExistsInCache() {
|
||||
// Todo: fix this
|
||||
return false;
|
||||
// This should be abstracted with the CornerstoneCacheService
|
||||
const rtContourId = rtDisplaySet.displaySetInstanceUID;
|
||||
const contour = segmentationService.getContour(rtContourId);
|
||||
|
||||
return contour !== undefined;
|
||||
}
|
||||
|
||||
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
||||
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
|
||||
const { servicesManager, extensionManager } = params;
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'dicom-rt',
|
||||
|
||||
@@ -323,7 +323,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
|
||||
onStatusClick,
|
||||
}),
|
||||
indexPriority: -100,
|
||||
location: viewportActionCornersService.LOCATIONS.topLeft,
|
||||
location: viewportActionCornersService.LOCATIONS.topRight,
|
||||
},
|
||||
{
|
||||
viewportId,
|
||||
|
||||
@@ -3,6 +3,97 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-seg",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "DICOM SEG read workflow",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -33,10 +33,10 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.15",
|
||||
"@ohif/extension-default": "3.11.0-beta.15",
|
||||
"@ohif/i18n": "3.11.0-beta.15",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.26",
|
||||
"@ohif/extension-default": "3.11.0-beta.26",
|
||||
"@ohif/i18n": "3.11.0-beta.26",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/adapters": "^3.11.6",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { utils } from '@ohif/core';
|
||||
import { metaData, triggerEvent, eventTarget } from '@cornerstonejs/core';
|
||||
import { utils, Types as OhifTypes } from '@ohif/core';
|
||||
import i18n from '@ohif/i18n';
|
||||
import { metaData, eventTarget } from '@cornerstonejs/core';
|
||||
import { CONSTANTS, segmentation as cstSegmentation } from '@cornerstonejs/tools';
|
||||
import { adaptersSEG, Enums } from '@cornerstonejs/adapters';
|
||||
|
||||
@@ -57,6 +58,7 @@ function _getDisplaySetsFromSeries(
|
||||
wadoUriRoot,
|
||||
wadoUri,
|
||||
isOverlayDisplaySet: true,
|
||||
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SEG')}`,
|
||||
};
|
||||
|
||||
const referencedSeriesSequence = instance.ReferencedSeriesSequence;
|
||||
@@ -232,7 +234,8 @@ function _segmentationExists(segDisplaySet) {
|
||||
return cstSegmentation.state.getSegmentation(segDisplaySet.displaySetInstanceUID);
|
||||
}
|
||||
|
||||
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
||||
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
|
||||
const { servicesManager, extensionManager } = params;
|
||||
const getDisplaySetsFromSeries = instances => {
|
||||
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
|
||||
};
|
||||
|
||||
@@ -157,7 +157,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
|
||||
// it will be in the correct position zoom and pan
|
||||
commandsManager.runCommand('updateStoredPositionPresentation', {
|
||||
viewportId,
|
||||
displaySetInstanceUID: referencedDisplaySet.displaySetInstanceUID,
|
||||
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
|
||||
});
|
||||
|
||||
commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', {
|
||||
@@ -304,7 +304,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
|
||||
onStatusClick,
|
||||
}),
|
||||
indexPriority: -100,
|
||||
location: viewportActionCornersService.LOCATIONS.topLeft,
|
||||
location: viewportActionCornersService.LOCATIONS.topRight,
|
||||
},
|
||||
{
|
||||
viewportId,
|
||||
|
||||
@@ -3,6 +3,97 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-sr",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "OHIF extension for an SR Cornerstone Viewport",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -34,10 +34,10 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.15",
|
||||
"@ohif/extension-measurement-tracking": "3.11.0-beta.15",
|
||||
"@ohif/ui": "3.11.0-beta.15",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.26",
|
||||
"@ohif/extension-measurement-tracking": "3.11.0-beta.26",
|
||||
"@ohif/ui": "3.11.0-beta.26",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/tools": "^3.9.5",
|
||||
"@cornerstonejs/adapters": "^3.11.6",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@cornerstonejs/tools": "^3.11.6",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -273,7 +273,7 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) {
|
||||
servicesManager,
|
||||
}),
|
||||
indexPriority: -100,
|
||||
location: viewportActionCornersService.LOCATIONS.topLeft,
|
||||
location: viewportActionCornersService.LOCATIONS.topRight,
|
||||
},
|
||||
{
|
||||
viewportId,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { utils, classes, DisplaySetService, Types } from '@ohif/core';
|
||||
import { utils, classes, DisplaySetService, Types as OhifTypes } from '@ohif/core';
|
||||
import i18n from '@ohif/i18n';
|
||||
import { Enums as CSExtensionEnums } from '@ohif/extension-cornerstone';
|
||||
import { adaptersSR } from '@cornerstonejs/adapters';
|
||||
|
||||
@@ -122,6 +123,7 @@ function _getDisplaySetsFromSeries(
|
||||
sopClassUids,
|
||||
instance,
|
||||
addInstances,
|
||||
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SR')}`,
|
||||
};
|
||||
|
||||
displaySet.load = () => _load(displaySet, servicesManager, extensionManager);
|
||||
@@ -354,7 +356,8 @@ function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frame
|
||||
* @param {Object} options.extensionManager - The extension manager.
|
||||
* @returns {Array} An array containing the SOP class handler module.
|
||||
*/
|
||||
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
||||
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
|
||||
const { servicesManager, extensionManager } = params;
|
||||
const getDisplaySetsFromSeries = instances => {
|
||||
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,97 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dynamic-volume",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "OHIF extension for 4D volumes data",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -29,11 +29,11 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.15",
|
||||
"@ohif/extension-default": "3.11.0-beta.15",
|
||||
"@ohif/i18n": "3.11.0-beta.15",
|
||||
"@ohif/ui": "3.11.0-beta.15",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/extension-cornerstone": "3.11.0-beta.26",
|
||||
"@ohif/extension-default": "3.11.0-beta.26",
|
||||
"@ohif/i18n": "3.11.0-beta.26",
|
||||
"@ohif/ui": "3.11.0-beta.26",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.21",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/tools": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@cornerstonejs/tools": "^3.11.6",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,100 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **referencelines:** and bring back the notification for worker updates but disallow duplication ([#5005](https://github.com/OHIF/Viewers/issues/5005)) ([98f8187](https://github.com/OHIF/Viewers/commit/98f8187dfeb8aec6eca145da75c855aec0da4e1d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -38,9 +38,9 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.9.5",
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/ui": "3.11.0-beta.15",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.11.6",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/ui": "3.11.0-beta.26",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.21",
|
||||
"hammerjs": "^2.0.8",
|
||||
@@ -51,12 +51,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.9.5",
|
||||
"@cornerstonejs/ai": "^3.9.5",
|
||||
"@cornerstonejs/core": "^3.9.5",
|
||||
"@cornerstonejs/labelmap-interpolation": "^3.9.5",
|
||||
"@cornerstonejs/polymorphic-segmentation": "^3.9.5",
|
||||
"@cornerstonejs/tools": "^3.9.5",
|
||||
"@cornerstonejs/adapters": "^3.11.6",
|
||||
"@cornerstonejs/ai": "^3.11.6",
|
||||
"@cornerstonejs/core": "^3.11.6",
|
||||
"@cornerstonejs/labelmap-interpolation": "^3.11.6",
|
||||
"@cornerstonejs/polymorphic-segmentation": "^3.11.6",
|
||||
"@cornerstonejs/tools": "^3.11.6",
|
||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import React, { useEffect, useRef, useCallback, useState } from 'react';
|
||||
import { useResizeDetector } from 'react-resize-detector';
|
||||
import * as cs3DTools from '@cornerstonejs/tools';
|
||||
import { Enums, eventTarget, getEnabledElement } from '@cornerstonejs/core';
|
||||
import { MeasurementService } from '@ohif/core';
|
||||
import { AllInOneMenu } from '@ohif/ui-next';
|
||||
import { useViewportDialog } from '@ohif/ui-next';
|
||||
import type { Types as csTypes } from '@cornerstonejs/core';
|
||||
|
||||
@@ -15,12 +13,11 @@ import CinePlayer from '../components/CinePlayer';
|
||||
import type { Types } from '@ohif/core';
|
||||
|
||||
import OHIFViewportActionCorners from '../components/OHIFViewportActionCorners';
|
||||
import { getWindowLevelActionMenu } from '../components/WindowLevelActionMenu/getWindowLevelActionMenu';
|
||||
import { getViewportDataOverlaySettingsMenu } from '../components/ViewportDataOverlaySettingMenu';
|
||||
import { getViewportPresentations } from '../utils/presentations/getViewportPresentations';
|
||||
import { useSynchronizersStore } from '../stores/useSynchronizersStore';
|
||||
import ActiveViewportBehavior from '../utils/ActiveViewportBehavior';
|
||||
import { WITH_NAVIGATION } from '../services/ViewportService/CornerstoneViewportService';
|
||||
import { useViewportActionCorners } from '../hooks';
|
||||
|
||||
const STACK = 'stack';
|
||||
|
||||
@@ -110,7 +107,23 @@ const OHIFCornerstoneViewport = React.memo(
|
||||
cornerstoneViewportService.resize();
|
||||
setImageScrollBarHeight();
|
||||
}
|
||||
}, [elementRef]);
|
||||
}, [elementRef, cornerstoneViewportService, setImageScrollBarHeight]);
|
||||
|
||||
useEffect(() => {
|
||||
const element = elementRef.current;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resizeObserver = new ResizeObserver(onResize);
|
||||
resizeObserver.observe(element);
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
resizeObserver.unobserve(element);
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, [onResize]);
|
||||
|
||||
const cleanUpServices = useCallback(
|
||||
viewportInfo => {
|
||||
@@ -306,52 +319,14 @@ const OHIFCornerstoneViewport = React.memo(
|
||||
};
|
||||
}, [displaySets, elementRef, viewportId, isJumpToMeasurementDisabled, servicesManager]);
|
||||
|
||||
// Set up the window level action menu in the viewport action corners.
|
||||
useEffect(() => {
|
||||
const windowLevelActionMenu = customizationService.getCustomization(
|
||||
'viewportActionMenu.windowLevelActionMenu'
|
||||
);
|
||||
const segmentationOverlay = customizationService.getCustomization(
|
||||
'viewportActionMenu.segmentationOverlay'
|
||||
);
|
||||
|
||||
if (windowLevelActionMenu?.enabled) {
|
||||
viewportActionCornersService.addComponent({
|
||||
viewportId,
|
||||
id: 'windowLevelActionMenu',
|
||||
component: getWindowLevelActionMenu({
|
||||
viewportId,
|
||||
element: elementRef.current,
|
||||
displaySets,
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
location: windowLevelActionMenu.location,
|
||||
verticalDirection: AllInOneMenu.VerticalDirection.TopToBottom,
|
||||
horizontalDirection: AllInOneMenu.HorizontalDirection.RightToLeft,
|
||||
}),
|
||||
location: windowLevelActionMenu.location,
|
||||
});
|
||||
}
|
||||
|
||||
if (segmentationOverlay?.enabled) {
|
||||
viewportActionCornersService.addComponent({
|
||||
viewportId,
|
||||
id: 'segmentation',
|
||||
component: getViewportDataOverlaySettingsMenu({
|
||||
viewportId,
|
||||
element: elementRef.current,
|
||||
displaySets,
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
location: segmentationOverlay.location,
|
||||
}),
|
||||
location: segmentationOverlay.location,
|
||||
});
|
||||
}
|
||||
}, [displaySets, viewportId, viewportActionCornersService, servicesManager, commandsManager]);
|
||||
|
||||
const { ref: resizeRef } = useResizeDetector({
|
||||
onResize,
|
||||
// Set up the window level action menu in the viewport action corners using the custom hook
|
||||
useViewportActionCorners({
|
||||
viewportId,
|
||||
elementRef,
|
||||
displaySets,
|
||||
viewportActionCornersService,
|
||||
customizationService,
|
||||
commandsManager,
|
||||
});
|
||||
|
||||
const Notification = customizationService.getCustomization('ui.notificationComponent');
|
||||
@@ -364,10 +339,7 @@ const OHIFCornerstoneViewport = React.memo(
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
onContextMenu={e => e.preventDefault()}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
ref={el => {
|
||||
resizeRef.current = el;
|
||||
elementRef.current = el;
|
||||
}}
|
||||
ref={elementRef}
|
||||
></div>
|
||||
<CornerstoneOverlays
|
||||
viewportId={viewportId}
|
||||
@@ -516,22 +488,18 @@ OHIFCornerstoneViewport.displayName = 'OHIFCornerstoneViewport';
|
||||
|
||||
function areEqual(prevProps, nextProps) {
|
||||
if (nextProps.needsRerendering) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: needsRerendering');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prevProps.displaySets.length !== nextProps.displaySets.length) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: displaySets length change');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prevProps.viewportOptions.orientation !== nextProps.viewportOptions.orientation) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: orientation change');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prevProps.viewportOptions.toolGroupId !== nextProps.viewportOptions.toolGroupId) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: toolGroupId change');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -539,12 +507,10 @@ function areEqual(prevProps, nextProps) {
|
||||
nextProps.viewportOptions.viewportType &&
|
||||
prevProps.viewportOptions.viewportType !== nextProps.viewportOptions.viewportType
|
||||
) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: viewportType change');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nextProps.viewportOptions.needsRerendering) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: viewportOptions.needsRerendering');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -552,7 +518,6 @@ function areEqual(prevProps, nextProps) {
|
||||
const nextDisplaySets = nextProps.displaySets;
|
||||
|
||||
if (prevDisplaySets.length !== nextDisplaySets.length) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: displaySets length mismatch');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -565,13 +530,11 @@ function areEqual(prevProps, nextProps) {
|
||||
);
|
||||
|
||||
if (!foundDisplaySet) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: displaySet not found');
|
||||
return false;
|
||||
}
|
||||
|
||||
// check they contain the same image
|
||||
if (foundDisplaySet.images?.length !== prevDisplaySet.images?.length) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: images length mismatch');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -579,7 +542,6 @@ function areEqual(prevProps, nextProps) {
|
||||
if (foundDisplaySet.images?.length) {
|
||||
for (let j = 0; j < foundDisplaySet.images.length; j++) {
|
||||
if (foundDisplaySet.images[j].imageId !== prevDisplaySet.images[j].imageId) {
|
||||
console.debug('OHIFCornerstoneViewport: Rerender caused by: imageId mismatch');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ function CornerstoneImageScrollbar({
|
||||
}
|
||||
const { viewportType } = viewportData;
|
||||
const eventId =
|
||||
(viewportType === Enums.ViewportType.STACK && Enums.Events.STACK_VIEWPORT_SCROLL) ||
|
||||
(viewportType === Enums.ViewportType.STACK && Enums.Events.STACK_NEW_IMAGE) ||
|
||||
(viewportType === Enums.ViewportType.ORTHOGRAPHIC && Enums.Events.VOLUME_NEW_IMAGE) ||
|
||||
Enums.Events.IMAGE_RENDERED;
|
||||
|
||||
@@ -72,11 +72,11 @@ function CornerstoneImageScrollbar({
|
||||
if (!viewport || viewport instanceof VolumeViewport3D) {
|
||||
return;
|
||||
}
|
||||
const { imageIndex, newImageIdIndex = imageIndex } = event.detail;
|
||||
const { imageIndex, newImageIdIndex = imageIndex, imageIdIndex } = event.detail;
|
||||
const numberOfSlices = viewport.getNumberOfSlices();
|
||||
// find the index of imageId in the imageIds
|
||||
setImageSliceData({
|
||||
imageIndex: newImageIdIndex,
|
||||
imageIndex: newImageIdIndex ?? imageIdIndex,
|
||||
numberOfSlices,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
StackViewport,
|
||||
VolumeViewport,
|
||||
utilities as csUtils,
|
||||
Enums as CoreEnums,
|
||||
Types as CoreTypes,
|
||||
BaseVolumeViewport,
|
||||
} from '@cornerstonejs/core';
|
||||
@@ -248,7 +249,7 @@ function commandsModule({
|
||||
},
|
||||
updateStoredPositionPresentation: ({
|
||||
viewportId,
|
||||
displaySetInstanceUID,
|
||||
displaySetInstanceUIDs,
|
||||
referencedImageId,
|
||||
options,
|
||||
}) => {
|
||||
@@ -256,11 +257,24 @@ function commandsModule({
|
||||
const { positionPresentationStore, setPositionPresentation, getPositionPresentationId } =
|
||||
usePositionPresentationStore.getState();
|
||||
|
||||
// Look inside positionPresentationStore and find the key that includes the displaySetInstanceUID
|
||||
// Look inside positionPresentationStore and find the key that includes ALL the displaySetInstanceUIDs
|
||||
// and the value has viewportId as activeViewportId.
|
||||
const previousReferencedDisplaySetStoreKey = Object.entries(positionPresentationStore).find(
|
||||
([key, value]) => key.includes(displaySetInstanceUID) && value.viewportId === viewportId
|
||||
)?.[0];
|
||||
let previousReferencedDisplaySetStoreKey;
|
||||
|
||||
if (
|
||||
displaySetInstanceUIDs &&
|
||||
Array.isArray(displaySetInstanceUIDs) &&
|
||||
displaySetInstanceUIDs.length > 0
|
||||
) {
|
||||
previousReferencedDisplaySetStoreKey = Object.entries(positionPresentationStore).find(
|
||||
([key, value]) => {
|
||||
return (
|
||||
displaySetInstanceUIDs.every(uid => key.includes(uid)) &&
|
||||
value.viewportId === viewportId
|
||||
);
|
||||
}
|
||||
)?.[0];
|
||||
}
|
||||
|
||||
// Create presentation data with referencedImageId and options if provided
|
||||
const presentationData = referencedImageId
|
||||
@@ -282,7 +296,7 @@ function commandsModule({
|
||||
// so we need to grab the positionPresentationId directly from the store,
|
||||
// Todo: this is really hacky, we should have a better way for this
|
||||
const positionPresentationId = getPositionPresentationId({
|
||||
displaySetInstanceUIDs: [displaySetInstanceUID],
|
||||
displaySetInstanceUIDs,
|
||||
viewportId,
|
||||
});
|
||||
|
||||
@@ -719,7 +733,7 @@ function commandsModule({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toolGroup.hasTool(toolName)) {
|
||||
if (!toolGroup?.hasTool(toolName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1019,7 +1033,7 @@ function commandsModule({
|
||||
|
||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||
|
||||
if (!toolGroup.hasTool(toolName)) {
|
||||
if (!toolGroup?.hasTool(toolName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1189,6 +1203,9 @@ function commandsModule({
|
||||
},
|
||||
/**
|
||||
* Creates a labelmap for the active viewport
|
||||
*
|
||||
* The created labelmap will be registered as a display set and also added
|
||||
* as a segmentation representation to the viewport.
|
||||
*/
|
||||
createLabelmapForViewport: async ({ viewportId, options = {} }) => {
|
||||
const { viewportGridService, displaySetService, segmentationService } =
|
||||
@@ -1209,6 +1226,7 @@ function commandsModule({
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
|
||||
// This will create the segmentation and register it as a display set
|
||||
const generatedSegmentationId = await segmentationService.createLabelmapForDisplaySet(
|
||||
displaySet,
|
||||
{
|
||||
@@ -1225,6 +1243,7 @@ function commandsModule({
|
||||
}
|
||||
);
|
||||
|
||||
// Also add the segmentation representation to the viewport
|
||||
await segmentationService.addSegmentationRepresentation(viewportId, {
|
||||
segmentationId,
|
||||
type: Enums.SegmentationRepresentations.Labelmap,
|
||||
@@ -1741,6 +1760,30 @@ function commandsModule({
|
||||
});
|
||||
});
|
||||
},
|
||||
setViewportOrientation: ({ viewportId, orientation }) => {
|
||||
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
|
||||
if (!viewport || viewport.type !== CoreEnums.ViewportType.ORTHOGRAPHIC) {
|
||||
console.warn('Orientation can only be set on volume viewports');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get display sets for this viewport to verify at least one is reconstructable
|
||||
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
|
||||
const displaySets = displaySetUIDs.map(uid => displaySetService.getDisplaySetByUID(uid));
|
||||
|
||||
if (!displaySets.some(ds => ds.isReconstructable)) {
|
||||
console.warn('Cannot change orientation: No reconstructable display sets in viewport');
|
||||
return;
|
||||
}
|
||||
|
||||
viewport.setOrientation(orientation);
|
||||
viewport.render();
|
||||
|
||||
// update the orientation in the viewport info
|
||||
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
|
||||
viewportInfo.setOrientation(orientation);
|
||||
},
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
@@ -2021,6 +2064,7 @@ function commandsModule({
|
||||
decreaseBrushSize: actions.decreaseBrushSize,
|
||||
addNewSegment: actions.addNewSegment,
|
||||
loadSegmentationDisplaySetsForViewport: actions.loadSegmentationDisplaySetsForViewport,
|
||||
setViewportOrientation: actions.setViewportOrientation,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { useViewportActionCornersContext } from '../contextProviders/ViewportActionCornersProvider';
|
||||
import { useSystem } from '@ohif/core';
|
||||
import { useViewportGrid } from '@ohif/ui-next';
|
||||
|
||||
export type OHIFViewportActionCornersProps = {
|
||||
viewportId: string;
|
||||
@@ -9,8 +10,13 @@ export type OHIFViewportActionCornersProps = {
|
||||
function OHIFViewportActionCorners({ viewportId }: OHIFViewportActionCornersProps) {
|
||||
const { servicesManager } = useSystem();
|
||||
const [viewportActionCornersState] = useViewportActionCornersContext();
|
||||
|
||||
const [viewportGrid] = useViewportGrid();
|
||||
const isActiveViewport = viewportGrid.activeViewportId === viewportId;
|
||||
|
||||
const ViewportActionCorners =
|
||||
servicesManager.services.customizationService.getCustomization('ui.viewportActionCorner');
|
||||
|
||||
if (!viewportActionCornersState[viewportId]) {
|
||||
return null;
|
||||
}
|
||||
@@ -18,7 +24,8 @@ function OHIFViewportActionCorners({ viewportId }: OHIFViewportActionCornersProp
|
||||
return (
|
||||
<ViewportActionCorners
|
||||
cornerComponents={viewportActionCornersState[viewportId]}
|
||||
></ViewportActionCorners>
|
||||
isActiveViewport={isActiveViewport}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Icons,
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Switch,
|
||||
} from '@ohif/ui-next';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
import { useViewportDisplaySets } from '../../hooks/useViewportDisplaySets';
|
||||
|
||||
function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: string }>) {
|
||||
const { commandsManager, servicesManager } = useSystem();
|
||||
const [pendingForegrounds, setPendingForegrounds] = useState<string[]>([]);
|
||||
const [pendingSegmentations, setPendingSegmentations] = useState<string[]>([]);
|
||||
const [thresholdOpacityEnabled, setThresholdOpacityEnabled] = useState(false);
|
||||
|
||||
const { hangingProtocolService } = servicesManager.services;
|
||||
|
||||
const {
|
||||
backgroundDisplaySet,
|
||||
potentialOverlayDisplaySets,
|
||||
potentialForegroundDisplaySets,
|
||||
potentialBackgroundDisplaySets,
|
||||
overlayDisplaySets,
|
||||
foregroundDisplaySets,
|
||||
} = useViewportDisplaySets(viewportId);
|
||||
|
||||
/**
|
||||
* Change the background display set
|
||||
*/
|
||||
const handleBackgroundSelection = (newBackgroundDisplaySet: AppTypes.DisplaySet) => {
|
||||
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||
viewportId,
|
||||
newBackgroundDisplaySet.displaySetInstanceUID
|
||||
);
|
||||
|
||||
commandsManager.run('setDisplaySetsForViewports', {
|
||||
viewportsToUpdate: updatedViewports,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle threshold and opacity toggle
|
||||
*/
|
||||
const handleThresholdOpacityToggle = (checked: boolean) => {
|
||||
setThresholdOpacityEnabled(checked);
|
||||
|
||||
if (foregroundDisplaySets.length > 0) {
|
||||
// Example implementation of threshold/opacity adjustment
|
||||
commandsManager.runCommand('setForegroundThresholdOpacity', {
|
||||
viewportId,
|
||||
enabled: checked,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace a display set layer with a new one
|
||||
*/
|
||||
const handleReplaceDisplaySetLayer = (
|
||||
currentDisplaySetInstanceUID: string,
|
||||
newDisplaySetInstanceUID: string
|
||||
) => {
|
||||
// Remove current display set
|
||||
commandsManager.runCommand('removeDisplaySetLayer', {
|
||||
viewportId,
|
||||
displaySetInstanceUID: currentDisplaySetInstanceUID,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
commandsManager.runCommand('addDisplaySetAsLayer', {
|
||||
viewportId,
|
||||
displaySetInstanceUID: newDisplaySetInstanceUID,
|
||||
});
|
||||
}, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a display set layer
|
||||
*/
|
||||
const handleRemoveDisplaySetLayer = (displaySetInstanceUID: string) => {
|
||||
commandsManager.runCommand('removeDisplaySetLayer', {
|
||||
viewportId,
|
||||
displaySetInstanceUID,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a display set as a layer
|
||||
*/
|
||||
const handleAddDisplaySetAsLayer = (displaySetInstanceUID: string) => {
|
||||
commandsManager.runCommand('addDisplaySetAsLayer', {
|
||||
viewportId,
|
||||
displaySetInstanceUID,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle overlay display set selection change
|
||||
*/
|
||||
const handleOverlaySelectionChange = (
|
||||
currentDisplaySet: AppTypes.DisplaySet,
|
||||
newDisplaySetInstanceUID: string
|
||||
) => {
|
||||
if (newDisplaySetInstanceUID === currentDisplaySet.displaySetInstanceUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the selected display set
|
||||
const selectedDisplaySet = potentialOverlayDisplaySets.find(
|
||||
ds => ds.displaySetInstanceUID === newDisplaySetInstanceUID
|
||||
);
|
||||
|
||||
if (selectedDisplaySet) {
|
||||
handleReplaceDisplaySetLayer(
|
||||
currentDisplaySet.displaySetInstanceUID,
|
||||
selectedDisplaySet.displaySetInstanceUID
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle foreground display set selection change
|
||||
*/
|
||||
const handleForegroundSelectionChange = (
|
||||
currentDisplaySet: AppTypes.DisplaySet,
|
||||
newDisplaySetInstanceUID: string
|
||||
) => {
|
||||
if (newDisplaySetInstanceUID === currentDisplaySet.displaySetInstanceUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the selected display set
|
||||
const selectedDisplaySet = potentialForegroundDisplaySets.find(
|
||||
ds => ds.displaySetInstanceUID === newDisplaySetInstanceUID
|
||||
);
|
||||
|
||||
if (selectedDisplaySet) {
|
||||
handleReplaceDisplaySetLayer(
|
||||
currentDisplaySet.displaySetInstanceUID,
|
||||
selectedDisplaySet.displaySetInstanceUID
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle pending segmentation selection
|
||||
*/
|
||||
const handlePendingSegmentationSelection = (pendingId: string, displaySetInstanceUID: string) => {
|
||||
const selectedDisplaySet = potentialOverlayDisplaySets.find(
|
||||
ds => ds.displaySetInstanceUID === displaySetInstanceUID
|
||||
);
|
||||
|
||||
if (selectedDisplaySet) {
|
||||
handleAddDisplaySetAsLayer(selectedDisplaySet.displaySetInstanceUID);
|
||||
// Remove this pending segmentation from the list
|
||||
setPendingSegmentations(pendingSegmentations.filter(id => id !== pendingId));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle pending foreground selection
|
||||
*/
|
||||
const handlePendingForegroundSelection = (pendingId: string, displaySetInstanceUID: string) => {
|
||||
const selectedDisplaySet = potentialForegroundDisplaySets.find(
|
||||
ds => ds.displaySetInstanceUID === displaySetInstanceUID
|
||||
);
|
||||
|
||||
if (selectedDisplaySet) {
|
||||
handleAddDisplaySetAsLayer(selectedDisplaySet.displaySetInstanceUID);
|
||||
// Remove this pending foreground from the list
|
||||
setPendingForegrounds(pendingForegrounds.filter(id => id !== pendingId));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-popover flex h-full w-[275px] flex-col rounded rounded-md p-1.5">
|
||||
{/* Top buttons row */}
|
||||
<div className={`flex`}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="text-primary flex items-center p-1"
|
||||
onClick={() => {
|
||||
// Add a new pending foreground slot with a unique ID
|
||||
setPendingForegrounds([...pendingForegrounds, `pending-${Date.now()}`]);
|
||||
}}
|
||||
disabled={potentialForegroundDisplaySets.length === 0}
|
||||
>
|
||||
<Icons.Plus className="h-4 w-4" />
|
||||
Foreground
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="text-primary ml-2 flex items-center"
|
||||
disabled={potentialOverlayDisplaySets.length === 0}
|
||||
onClick={() => {
|
||||
setPendingSegmentations([...pendingSegmentations, `seg-${Date.now()}`]);
|
||||
}}
|
||||
>
|
||||
<Icons.Plus className="h-4 w-4" />
|
||||
Segmentation
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="">
|
||||
{/* Overlays Segmentation section */}
|
||||
<div className="my-2 ml-1">
|
||||
{overlayDisplaySets.map((displaySet, index) => (
|
||||
<div
|
||||
key={displaySet.displaySetInstanceUID}
|
||||
className="mb-1 flex items-center"
|
||||
>
|
||||
<Icons.LayerSegmentation className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
|
||||
<Select
|
||||
value={displaySet.displaySetInstanceUID}
|
||||
onValueChange={value => handleOverlaySelectionChange(displaySet, value)}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue>{displaySet.label?.toUpperCase()}</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{/* Include both potential overlays and the current overlay */}
|
||||
<SelectItem
|
||||
key={displaySet.displaySetInstanceUID}
|
||||
value={displaySet.displaySetInstanceUID}
|
||||
>
|
||||
{displaySet.label}
|
||||
</SelectItem>
|
||||
{potentialOverlayDisplaySets.map(item => (
|
||||
<SelectItem
|
||||
key={item.displaySetInstanceUID}
|
||||
value={item.displaySetInstanceUID}
|
||||
>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="ml-2 flex-shrink-0"
|
||||
>
|
||||
<Icons.More className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleRemoveDisplaySetLayer(displaySet.displaySetInstanceUID)}
|
||||
>
|
||||
Remove
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{pendingSegmentations.map(pendingId => (
|
||||
<div
|
||||
key={pendingId}
|
||||
className="mb-2 flex items-center"
|
||||
>
|
||||
<Icons.LayerSegmentation className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
|
||||
<Select
|
||||
value=""
|
||||
onValueChange={value => handlePendingSegmentationSelection(pendingId, value)}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue placeholder="SELECT A SEGMENTATION" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{potentialOverlayDisplaySets.map(item => (
|
||||
<SelectItem
|
||||
key={item.displaySetInstanceUID}
|
||||
value={item.displaySetInstanceUID}
|
||||
>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="ml-2 flex-shrink-0"
|
||||
>
|
||||
<Icons.More className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
// Remove this pending segmentation
|
||||
setPendingSegmentations(pendingSegmentations.filter(id => id !== pendingId));
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* Foregrounds section */}
|
||||
<div className="my-2 px-1">
|
||||
{foregroundDisplaySets.map((displaySet, index) => (
|
||||
<div
|
||||
key={displaySet.displaySetInstanceUID}
|
||||
className="mb-1 flex items-center"
|
||||
>
|
||||
<Icons.LayerForeground className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
|
||||
<Select
|
||||
value={displaySet.displaySetInstanceUID}
|
||||
onValueChange={value => handleForegroundSelectionChange(displaySet, value)}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue>{displaySet.label?.toUpperCase()}</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{/* Include both potential foregrounds and the current foreground */}
|
||||
<SelectItem
|
||||
key={displaySet.displaySetInstanceUID}
|
||||
value={displaySet.displaySetInstanceUID}
|
||||
>
|
||||
{displaySet.label}
|
||||
</SelectItem>
|
||||
{potentialForegroundDisplaySets.map(item => (
|
||||
<SelectItem
|
||||
key={item.displaySetInstanceUID}
|
||||
value={item.displaySetInstanceUID}
|
||||
>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="ml-2 flex-shrink-0"
|
||||
>
|
||||
<Icons.More className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleRemoveDisplaySetLayer(displaySet.displaySetInstanceUID)}
|
||||
>
|
||||
Remove
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{pendingForegrounds.map(pendingId => (
|
||||
<div
|
||||
key={pendingId}
|
||||
className="mb-2 flex items-center"
|
||||
>
|
||||
<Icons.LayerForeground className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
|
||||
<Select
|
||||
value=""
|
||||
onValueChange={value => handlePendingForegroundSelection(pendingId, value)}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue placeholder="SELECT A FOREGROUND" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{potentialForegroundDisplaySets.map(item => (
|
||||
<SelectItem
|
||||
key={item.displaySetInstanceUID}
|
||||
value={item.displaySetInstanceUID}
|
||||
>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="ml-2 flex-shrink-0"
|
||||
>
|
||||
<Icons.More className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
// Remove this pending foreground
|
||||
setPendingForegrounds(pendingForegrounds.filter(id => id !== pendingId));
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* Background section */}
|
||||
<div className="mt-1 mb-1 flex items-center px-1">
|
||||
<Icons.LayerBackground className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
|
||||
<Select
|
||||
value={backgroundDisplaySet.displaySetInstanceUID}
|
||||
onValueChange={value => {
|
||||
const selectedDisplaySet = potentialBackgroundDisplaySets.find(
|
||||
ds => ds.displaySetInstanceUID === value
|
||||
);
|
||||
if (selectedDisplaySet) {
|
||||
handleBackgroundSelection(selectedDisplaySet);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="flex-1">
|
||||
<SelectValue>
|
||||
{(
|
||||
backgroundDisplaySet.SeriesDescription ||
|
||||
backgroundDisplaySet.label ||
|
||||
'background'
|
||||
).toUpperCase()}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{potentialBackgroundDisplaySets.map(displaySet => (
|
||||
<SelectItem
|
||||
key={displaySet.displaySetInstanceUID}
|
||||
value={displaySet.displaySetInstanceUID}
|
||||
>
|
||||
{displaySet.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
{/* {foregroundDisplaySets.length > 0 && (
|
||||
<div className="mt-1 ml-7">
|
||||
<div className="flex items-center">
|
||||
<Switch
|
||||
id="threshold-opacity-switch"
|
||||
className="mr-2"
|
||||
checked={thresholdOpacityEnabled}
|
||||
onCheckedChange={handleThresholdOpacityToggle}
|
||||
/>
|
||||
<label
|
||||
htmlFor="threshold-opacity-switch"
|
||||
className="text-muted-foreground cursor-pointer text-sm"
|
||||
onClick={() => setThresholdOpacityEnabled(!thresholdOpacityEnabled)}
|
||||
>
|
||||
Control threshold & opacity
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ViewportDataOverlayMenu;
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import {
|
||||
Button,
|
||||
cn,
|
||||
Icons,
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
useViewportGrid,
|
||||
} from '@ohif/ui-next';
|
||||
import ViewportDataOverlayMenu from './ViewportDataOverlayMenu';
|
||||
import classNames from 'classnames';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export function ViewportDataOverlayMenuWrapper({
|
||||
viewportId,
|
||||
displaySets,
|
||||
location,
|
||||
}: withAppTypes<{
|
||||
viewportId: string;
|
||||
element: HTMLElement;
|
||||
}>): ReactNode {
|
||||
const { servicesManager } = useSystem();
|
||||
const { viewportActionCornersService } = servicesManager.services;
|
||||
const [viewportGrid] = useViewportGrid();
|
||||
|
||||
const { align, side } = viewportActionCornersService.getAlignAndSide(location);
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
asChild
|
||||
className="flex items-center justify-center"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={cn(
|
||||
viewportGrid.activeViewportId === viewportId
|
||||
? 'visible'
|
||||
: 'invisible group-hover/pane:visible'
|
||||
)}
|
||||
>
|
||||
<Icons.ViewportViews className={classNames('text-highlight')} />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="border-none bg-transparent p-0 shadow-none"
|
||||
side={side}
|
||||
align={align}
|
||||
alignOffset={0}
|
||||
sideOffset={5}
|
||||
>
|
||||
<ViewportDataOverlayMenu
|
||||
className="w-full"
|
||||
viewportId={viewportId}
|
||||
displaySets={displaySets}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Icons, Separator } from '@ohif/ui-next';
|
||||
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||
|
||||
function ViewportSegmentationMenu({
|
||||
viewportId,
|
||||
servicesManager,
|
||||
}: withAppTypes<{ viewportId: string }>) {
|
||||
const { segmentationService } = servicesManager.services;
|
||||
const [activeSegmentations, setActiveSegmentations] = useState([]);
|
||||
const [availableSegmentations, setAvailableSegmentations] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const updateSegmentations = () => {
|
||||
const active = segmentationService.getSegmentationRepresentations(viewportId);
|
||||
setActiveSegmentations(active);
|
||||
|
||||
const all = segmentationService.getSegmentations();
|
||||
const available = all.filter(
|
||||
seg => !active.some(activeSeg => activeSeg.segmentationId === seg.segmentationId)
|
||||
);
|
||||
setAvailableSegmentations(available);
|
||||
};
|
||||
|
||||
updateSegmentations();
|
||||
|
||||
const subscriptions = [
|
||||
segmentationService.EVENTS.SEGMENTATION_MODIFIED,
|
||||
segmentationService.EVENTS.SEGMENTATION_REMOVED,
|
||||
segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED,
|
||||
].map(event => segmentationService.subscribe(event, updateSegmentations));
|
||||
|
||||
return () => {
|
||||
subscriptions.forEach(subscription => subscription.unsubscribe());
|
||||
};
|
||||
}, [segmentationService, viewportId]);
|
||||
|
||||
const toggleSegmentationRepresentationVisibility = (
|
||||
segmentationId,
|
||||
type = SegmentationRepresentations.Labelmap
|
||||
) => {
|
||||
segmentationService.toggleSegmentationRepresentationVisibility(viewportId, {
|
||||
segmentationId,
|
||||
type,
|
||||
});
|
||||
};
|
||||
|
||||
const addSegmentationToViewport = segmentationId => {
|
||||
segmentationService.addSegmentationRepresentation(viewportId, { segmentationId });
|
||||
};
|
||||
|
||||
const removeSegmentationFromViewport = segmentationId => {
|
||||
segmentationService.removeSegmentationRepresentations(viewportId, {
|
||||
segmentationId,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-muted flex h-full w-[262px] flex-col rounded p-3">
|
||||
<span className="text-muted-foreground mb-2 text-xs font-semibold">Current Viewport</span>
|
||||
<ul className="space-y-1">
|
||||
{activeSegmentations.map(segmentation => (
|
||||
<li
|
||||
key={segmentation.id}
|
||||
className="flex items-center text-sm"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-muted-foreground mr-2"
|
||||
onClick={() => removeSegmentationFromViewport(segmentation.segmentationId)}
|
||||
>
|
||||
<Icons.Minus className="h-6 w-6" />
|
||||
</Button>
|
||||
<span className="text-foreground flex-grow">{segmentation.label}</span>
|
||||
{segmentation.visible ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-muted-foreground"
|
||||
onClick={() =>
|
||||
toggleSegmentationRepresentationVisibility(
|
||||
segmentation.segmentationId,
|
||||
segmentation.type
|
||||
)
|
||||
}
|
||||
>
|
||||
<Icons.Hide className="h-6 w-6" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-muted-foreground"
|
||||
onClick={() =>
|
||||
toggleSegmentationRepresentationVisibility(
|
||||
segmentation.segmentationId,
|
||||
segmentation.type
|
||||
)
|
||||
}
|
||||
>
|
||||
<Icons.Show className="h-6 w-6" />
|
||||
</Button>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{availableSegmentations.length > 0 && (
|
||||
<>
|
||||
<Separator className="bg-input mb-3" />
|
||||
<span className="text-muted-foreground mb-2 text-xs font-semibold">Available</span>
|
||||
<ul className="space-y-1">
|
||||
{availableSegmentations.map(({ segmentationId, label }) => (
|
||||
<li
|
||||
key={segmentationId}
|
||||
className="flex items-center text-sm"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-muted-foreground mr-2"
|
||||
onClick={() => addSegmentationToViewport(segmentationId)}
|
||||
>
|
||||
<Icons.Plus className="h-6 w-6" />
|
||||
</Button>
|
||||
<span className="text-foreground/60">{label}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ViewportSegmentationMenu;
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
import React, { ReactNode, useEffect, useState } from 'react';
|
||||
import { Button, Icons, Popover, PopoverContent, PopoverTrigger } from '@ohif/ui-next';
|
||||
import ViewportSegmentationMenu from './ViewportSegmentationMenu';
|
||||
import classNames from 'classnames';
|
||||
import { useSegmentations } from '../../hooks/useSegmentations';
|
||||
|
||||
export function ViewportSegmentationMenuWrapper({
|
||||
viewportId,
|
||||
displaySets,
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
location,
|
||||
}: withAppTypes<{
|
||||
viewportId: string;
|
||||
element: HTMLElement;
|
||||
}>): ReactNode {
|
||||
const { viewportActionCornersService, viewportGridService } = servicesManager.services;
|
||||
|
||||
const segmentations = useSegmentations({ servicesManager });
|
||||
|
||||
const activeViewportId = viewportGridService.getActiveViewportId();
|
||||
const isActiveViewport = viewportId === activeViewportId;
|
||||
|
||||
const { align, side } = getAlignAndSide(viewportActionCornersService, location);
|
||||
|
||||
if (!segmentations?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
asChild
|
||||
className="flex items-center justify-center"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
>
|
||||
<Icons.ViewportViews
|
||||
className={classNames(
|
||||
'text-highlight',
|
||||
isActiveViewport ? 'visible' : 'invisible group-hover/pane:visible'
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="border-none bg-transparent p-0 shadow-none"
|
||||
side={side}
|
||||
align={align}
|
||||
alignOffset={-15}
|
||||
sideOffset={5}
|
||||
>
|
||||
<ViewportSegmentationMenu
|
||||
className="w-full"
|
||||
viewportId={viewportId}
|
||||
displaySets={displaySets}
|
||||
servicesManager={servicesManager}
|
||||
commandsManager={commandsManager}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
const getAlignAndSide = (viewportActionCornersService, location) => {
|
||||
const ViewportActionCornersLocations = viewportActionCornersService.LOCATIONS;
|
||||
|
||||
switch (location) {
|
||||
case ViewportActionCornersLocations.topLeft:
|
||||
return { align: 'start', side: 'bottom' };
|
||||
case ViewportActionCornersLocations.topRight:
|
||||
return { align: 'end', side: 'bottom' };
|
||||
case ViewportActionCornersLocations.bottomLeft:
|
||||
return { align: 'start', side: 'top' };
|
||||
case ViewportActionCornersLocations.bottomRight:
|
||||
return { align: 'end', side: 'top' };
|
||||
default:
|
||||
console.debug('Unknown location, defaulting to bottom-start');
|
||||
return { align: 'start', side: 'bottom' };
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { ViewportSegmentationMenuWrapper } from './ViewportSegmentationMenuWrapper';
|
||||
import { ViewportDataOverlayMenuWrapper } from './ViewportDataOverlayMenuWrapper';
|
||||
|
||||
export function getViewportDataOverlaySettingsMenu(
|
||||
props: withAppTypes<{
|
||||
@@ -7,5 +7,5 @@ export function getViewportDataOverlaySettingsMenu(
|
||||
element: HTMLElement;
|
||||
}>
|
||||
): ReactNode {
|
||||
return <ViewportSegmentationMenuWrapper {...props} />;
|
||||
return <ViewportDataOverlayMenuWrapper {...props} />;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { utilities as csUtils } from '@cornerstonejs/core';
|
||||
|
||||
export const DEFAULT_COLORMAP = 'hsv';
|
||||
export const DEFAULT_OPACITY = 0.5;
|
||||
export const DEFAULT_OPACITY_PERCENT = DEFAULT_OPACITY * 100;
|
||||
export const DERIVED_OVERLAY_MODALITIES = ['SEG', 'RTSTRUCT'];
|
||||
|
||||
/**
|
||||
* Get modality-specific color and opacity settings from the customization service
|
||||
*/
|
||||
export function getModalityOverlayColormap(customizationService, modality) {
|
||||
const modalityOverlayDefaultColorMaps = customizationService?.getCustomization(
|
||||
'cornerstone.modalityOverlayDefaultColorMaps'
|
||||
) || { defaultSettings: {} };
|
||||
|
||||
return (
|
||||
modalityOverlayDefaultColorMaps.defaultSettings[modality] || {
|
||||
colormap: DEFAULT_COLORMAP,
|
||||
opacity: DEFAULT_OPACITY,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies display sets that can be used as overlays for a specific viewport.
|
||||
*
|
||||
* "Enhanced" display sets are those that:
|
||||
* 1. Are not already in the viewport
|
||||
* 2. Are evaluated for their ability to be overlaid onto the background display set
|
||||
* 3. Have an "isOverlayable" flag indicating if they're compatible with the viewport
|
||||
*
|
||||
* A display set is considered overlayable when:
|
||||
* - The background display set is reconstructable
|
||||
* - The display set is not unsupported
|
||||
* - The Frame of Reference matches the background display set
|
||||
* - For non-derived modalities: background can be a volume and display set is either multiframe or valid volume
|
||||
*
|
||||
* @returns {Object} Object containing:
|
||||
* - viewportDisplaySets: Display sets already in the viewport
|
||||
* - enhancedDisplaySets: Other display sets with overlayability assessment
|
||||
*/
|
||||
export function getEnhancedDisplaySets({ viewportId, services }) {
|
||||
const { displaySetService, viewportGridService } = services;
|
||||
const displaySetsUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
|
||||
const allDisplaySets = displaySetService.getActiveDisplaySets();
|
||||
|
||||
const otherDisplaySets = allDisplaySets.filter(
|
||||
displaySet => !displaySetsUIDs.includes(displaySet.displaySetInstanceUID)
|
||||
);
|
||||
|
||||
const viewportDisplaySets = displaySetsUIDs.map(displaySetUID =>
|
||||
displaySetService.getDisplaySetByUID(displaySetUID)
|
||||
);
|
||||
|
||||
const backgroundCanBeVolume = csUtils.isValidVolume(viewportDisplaySets[0].imageIds);
|
||||
const backgroundDisplaySet = viewportDisplaySets[0];
|
||||
|
||||
const enhancedDisplaySets = otherDisplaySets.map(displaySet => {
|
||||
if (!backgroundDisplaySet.isReconstructable) {
|
||||
return {
|
||||
...displaySet,
|
||||
isOverlayable: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (displaySet.unsupported) {
|
||||
return {
|
||||
...displaySet,
|
||||
isOverlayable: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if Frame of Reference matches
|
||||
if (
|
||||
displaySet.FrameOfReferenceUID &&
|
||||
displaySet.FrameOfReferenceUID !== backgroundDisplaySet.FrameOfReferenceUID
|
||||
) {
|
||||
return {
|
||||
...displaySet,
|
||||
isOverlayable: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Special handling for derived modalities
|
||||
if (!DERIVED_OVERLAY_MODALITIES.includes(displaySet.Modality)) {
|
||||
if (!backgroundCanBeVolume) {
|
||||
return {
|
||||
...displaySet,
|
||||
isOverlayable: false,
|
||||
};
|
||||
}
|
||||
|
||||
const imageIds = displaySet.imageIds || displaySet.images?.map(image => image.imageId);
|
||||
const isMultiframe = displaySet.isMultiFrame;
|
||||
|
||||
if (!isMultiframe && imageIds?.length > 0 && !csUtils.isValidVolume(imageIds)) {
|
||||
return {
|
||||
...displaySet,
|
||||
isOverlayable: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...displaySet,
|
||||
isOverlayable: true,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
viewportDisplaySets,
|
||||
enhancedDisplaySets,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort function: puts disabled items (isOverlayable: false) at the end
|
||||
*/
|
||||
export const sortByOverlayable = (a, b) => {
|
||||
if (a.isOverlayable === b.isOverlayable) {
|
||||
return 0;
|
||||
}
|
||||
return a.isOverlayable ? -1 : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create display set options based on modality and opacity settings
|
||||
*/
|
||||
export function createColormapOverlayDisplaySetOptions(displaySet, opacity, customizationService) {
|
||||
if (displaySet.Modality === 'SEG') {
|
||||
return {};
|
||||
}
|
||||
|
||||
const modalitySettings = getModalityOverlayColormap(customizationService, displaySet.Modality);
|
||||
return {
|
||||
colormap: {
|
||||
name: modalitySettings.colormap || DEFAULT_COLORMAP,
|
||||
opacity: opacity / 100, // Convert from percentage to 0-1 range
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get segmentations that can be added as overlays to the viewport
|
||||
*
|
||||
* Note: This function is deprecated as we now use display sets for segmentations
|
||||
*/
|
||||
export function getAvailableSegmentations(segmentationService) {
|
||||
const segmentations = segmentationService.getSegmentations() || [];
|
||||
return segmentations.map(segmentation => ({
|
||||
segmentationId: segmentation.segmentationId,
|
||||
label: segmentation.label || 'Segmentation',
|
||||
segments: segmentation.segments,
|
||||
frameOfReferenceUID: segmentation.frameOfReferenceUID,
|
||||
}));
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
import React from 'react';
|
||||
import { Icons, useViewportGrid } from '@ohif/ui-next';
|
||||
import { useSystem } from '@ohif/core';
|
||||
import { Enums } from '@cornerstonejs/core';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
Button,
|
||||
} from '@ohif/ui-next';
|
||||
|
||||
function ViewportOrientationMenu({ location }: withAppTypes<{ location?: string }>) {
|
||||
const { servicesManager, commandsManager } = useSystem();
|
||||
const [viewportGridState, viewportGridService] = useViewportGrid();
|
||||
const { cornerstoneViewportService, displaySetService } = servicesManager.services;
|
||||
|
||||
const viewportId = viewportGridState.activeViewportId;
|
||||
|
||||
const handleOrientationChange = (orientation: string) => {
|
||||
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
|
||||
const currentViewportType = viewportInfo?.getViewportType();
|
||||
|
||||
// Get the displaySets in this viewport
|
||||
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
|
||||
const displaySets = displaySetUIDs
|
||||
.map(uid => displaySetService.getDisplaySetByUID(uid))
|
||||
.filter(Boolean);
|
||||
|
||||
if (!displaySets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if at least one displaySet is reconstructable
|
||||
const hasReconstructableDisplaySet = displaySets.some(ds => ds.isReconstructable);
|
||||
|
||||
if (!hasReconstructableDisplaySet) {
|
||||
console.warn('Cannot change orientation: No reconstructable display sets in viewport');
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the orientation enum based on the selected orientation
|
||||
let orientationEnum;
|
||||
switch (orientation.toLowerCase()) {
|
||||
case 'axial':
|
||||
orientationEnum = Enums.OrientationAxis.AXIAL;
|
||||
break;
|
||||
case 'sagittal':
|
||||
orientationEnum = Enums.OrientationAxis.SAGITTAL;
|
||||
break;
|
||||
case 'coronal':
|
||||
orientationEnum = Enums.OrientationAxis.CORONAL;
|
||||
break;
|
||||
default:
|
||||
orientationEnum = Enums.OrientationAxis.ACQUISITION;
|
||||
}
|
||||
|
||||
// If viewport is not already a volume type, we need to convert it
|
||||
if (currentViewportType !== Enums.ViewportType.ORTHOGRAPHIC) {
|
||||
// Configure the viewport to be a volume viewport with current display sets
|
||||
const updatedViewport = {
|
||||
viewportId,
|
||||
displaySetInstanceUIDs: displaySetUIDs,
|
||||
viewportOptions: {
|
||||
viewportType: Enums.ViewportType.ORTHOGRAPHIC,
|
||||
orientation: orientationEnum,
|
||||
},
|
||||
displaySetOptions: displaySetUIDs.map(() => ({})),
|
||||
};
|
||||
|
||||
// Update the viewport to be a volume viewport
|
||||
commandsManager.run('setDisplaySetsForViewports', {
|
||||
viewportsToUpdate: [updatedViewport],
|
||||
});
|
||||
} else {
|
||||
// Set the viewport orientation
|
||||
commandsManager.runCommand('setViewportOrientation', {
|
||||
viewportId,
|
||||
orientation: orientationEnum,
|
||||
});
|
||||
}
|
||||
};
|
||||
const { viewportActionCornersService } = servicesManager.services;
|
||||
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
|
||||
const displaySets = displaySetUIDs
|
||||
.map(uid => displaySetService.getDisplaySetByUID(uid))
|
||||
.filter(Boolean);
|
||||
|
||||
if (!displaySets.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasReconstructableDisplaySet = displaySets.some(ds => ds.isReconstructable);
|
||||
|
||||
// Get proper alignment and side based on the location
|
||||
let align = 'center';
|
||||
let side = 'bottom';
|
||||
|
||||
if (location !== undefined) {
|
||||
const positioning = viewportActionCornersService.getAlignAndSide(location);
|
||||
align = positioning.align;
|
||||
side = positioning.side;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-highlight"
|
||||
disabled={!hasReconstructableDisplaySet}
|
||||
>
|
||||
<Icons.OrientationSwitch />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="min-w-[160px]"
|
||||
align={align}
|
||||
side={side}
|
||||
sideOffset={5}
|
||||
>
|
||||
<DropdownMenuLabel className="-ml-1">Orientation</DropdownMenuLabel>
|
||||
<DropdownMenuItem onClick={() => handleOrientationChange('axial')}>Axial</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => handleOrientationChange('sagittal')}>
|
||||
Sagittal
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => handleOrientationChange('coronal')}>
|
||||
Coronal
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => handleOrientationChange('acquisition')}>
|
||||
Acquisition
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
export default ViewportOrientationMenu;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { useViewportGrid } from '@ohif/ui-next';
|
||||
import { cn } from '@ohif/ui-next';
|
||||
import ViewportOrientationMenu from './ViewportOrientationMenu';
|
||||
|
||||
export function ViewportOrientationMenuWrapper({
|
||||
viewportId,
|
||||
location,
|
||||
}: withAppTypes<{
|
||||
viewportId: string;
|
||||
element: HTMLElement;
|
||||
location: string;
|
||||
}>): ReactNode {
|
||||
const [viewportGrid] = useViewportGrid();
|
||||
const isActiveViewport = viewportId === viewportGrid.activeViewportId;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex justify-end',
|
||||
isActiveViewport ? 'visible' : 'invisible group-hover/pane:visible'
|
||||
)}
|
||||
>
|
||||
<ViewportOrientationMenu location={location} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { ViewportOrientationMenuWrapper } from './ViewportOrientationMenuWrapper';
|
||||
|
||||
export function getViewportOrientationMenu(
|
||||
props: withAppTypes<{
|
||||
viewportId: string;
|
||||
element: HTMLElement;
|
||||
location: string;
|
||||
}>
|
||||
): ReactNode {
|
||||
return <ViewportOrientationMenuWrapper {...props} />;
|
||||
}
|
||||
|
||||
export * from './ViewportOrientationMenu';
|
||||
@@ -1,18 +1,18 @@
|
||||
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
|
||||
import { Switch } from '@ohif/ui-next';
|
||||
import { StackViewport, VolumeViewport } from '@cornerstonejs/core';
|
||||
import { StackViewport } from '@cornerstonejs/core';
|
||||
import { ColorbarProps } from '../../types/Colorbar';
|
||||
import { utilities } from '@cornerstonejs/core';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export function setViewportColorbar(
|
||||
viewportId,
|
||||
displaySets,
|
||||
commandsManager,
|
||||
servicesManager: AppTypes.ServicesManager,
|
||||
colorbarOptions
|
||||
) {
|
||||
const { cornerstoneViewportService } = servicesManager.services;
|
||||
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
const { cornerstoneViewportService, viewportGridService } = servicesManager.services;
|
||||
const displaySetInstanceUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
|
||||
|
||||
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
|
||||
const backgroundColor = viewportInfo.getViewportOptions().background;
|
||||
@@ -32,36 +32,18 @@ export function setViewportColorbar(
|
||||
};
|
||||
}
|
||||
|
||||
const displaySetInstanceUIDs = [];
|
||||
|
||||
if (viewport instanceof StackViewport) {
|
||||
displaySetInstanceUIDs.push(viewportId);
|
||||
}
|
||||
|
||||
if (viewport instanceof VolumeViewport) {
|
||||
displaySets.forEach(ds => {
|
||||
displaySetInstanceUIDs.push(ds.displaySetInstanceUID);
|
||||
});
|
||||
}
|
||||
|
||||
commandsManager.run({
|
||||
commandName: 'toggleViewportColorbar',
|
||||
commandOptions: {
|
||||
viewportId,
|
||||
options: colorbarOptions,
|
||||
displaySetInstanceUIDs,
|
||||
},
|
||||
context: 'CORNERSTONE',
|
||||
commandsManager.run('toggleViewportColorbar', {
|
||||
viewportId,
|
||||
options: colorbarOptions,
|
||||
displaySetInstanceUIDs,
|
||||
});
|
||||
}
|
||||
|
||||
export function Colorbar({
|
||||
viewportId,
|
||||
displaySets,
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
colorbarProperties,
|
||||
}: withAppTypes<ColorbarProps>): ReactElement {
|
||||
const { servicesManager, commandsManager } = useSystem();
|
||||
const { colorbarService } = servicesManager.services;
|
||||
const {
|
||||
width: colorbarWidth,
|
||||
@@ -73,7 +55,7 @@ export function Colorbar({
|
||||
const [showColorbar, setShowColorbar] = useState(colorbarService.hasColorbar(viewportId));
|
||||
|
||||
const onSetColorbar = useCallback(() => {
|
||||
setViewportColorbar(viewportId, displaySets, commandsManager, servicesManager, {
|
||||
setViewportColorbar(viewportId, commandsManager, servicesManager, {
|
||||
viewportId,
|
||||
colormaps,
|
||||
ticks: {
|
||||
|
||||
@@ -3,14 +3,10 @@ import { AllInOneMenu, Switch, Tabs, TabsList, TabsTrigger } from '@ohif/ui-next
|
||||
|
||||
import { StackViewport, Types } from '@cornerstonejs/core';
|
||||
import { ColormapProps } from '../../types/Colormap';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export function Colormap({
|
||||
colormaps,
|
||||
viewportId,
|
||||
displaySets,
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
}: ColormapProps): ReactElement {
|
||||
export function Colormap({ colormaps, viewportId, displaySets }: ColormapProps): ReactElement {
|
||||
const { servicesManager, commandsManager } = useSystem();
|
||||
const { cornerstoneViewportService } = servicesManager.services;
|
||||
|
||||
const [activeDisplaySet, setActiveDisplaySet] = useState(displaySets[0]);
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import React, { ReactElement, useState, useEffect, useCallback } from 'react';
|
||||
import { VolumeLightingProps } from '../../types/ViewportPresets';
|
||||
import { Numeric } from '@ohif/ui-next';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export function VolumeLighting({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
viewportId,
|
||||
hasShade,
|
||||
}: VolumeLightingProps): ReactElement {
|
||||
export function VolumeLighting({ viewportId, hasShade }: VolumeLightingProps): ReactElement {
|
||||
const { servicesManager, commandsManager } = useSystem();
|
||||
const { cornerstoneViewportService } = servicesManager.services;
|
||||
const [lightingValues, setLightingValues] = useState({
|
||||
ambient: null,
|
||||
|
||||
+2
-14
@@ -5,43 +5,31 @@ import { VolumeRenderingQuality } from './VolumeRenderingQuality';
|
||||
import { VolumeShift } from './VolumeShift';
|
||||
import { VolumeLighting } from './VolumeLighting';
|
||||
import { VolumeShade } from './VolumeShade';
|
||||
|
||||
export function VolumeRenderingOptions({
|
||||
viewportId,
|
||||
commandsManager,
|
||||
volumeRenderingQualityRange,
|
||||
servicesManager,
|
||||
}: VolumeRenderingOptionsProps): ReactElement {
|
||||
const [hasShade, setShade] = useState(false);
|
||||
return (
|
||||
<AllInOneMenu.ItemPanel>
|
||||
<VolumeRenderingQuality
|
||||
viewportId={viewportId}
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
volumeRenderingQualityRange={volumeRenderingQualityRange}
|
||||
/>
|
||||
|
||||
<VolumeShift
|
||||
viewportId={viewportId}
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
/>
|
||||
<VolumeShift viewportId={viewportId} />
|
||||
<div className="mt-2 flex h-8 !h-[20px] w-full flex-shrink-0 items-center justify-start px-2 text-base">
|
||||
<div className="text-muted-foreground text-sm">Lighting</div>
|
||||
</div>
|
||||
<div className="bg-background mt-1 mb-1 h-px w-full"></div>
|
||||
<div className="hover:bg-accent flex h-8 w-full flex-shrink-0 items-center px-2 text-base hover:rounded">
|
||||
<VolumeShade
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
viewportId={viewportId}
|
||||
onClickShade={setShade}
|
||||
/>
|
||||
</div>
|
||||
<VolumeLighting
|
||||
viewportId={viewportId}
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
hasShade={hasShade}
|
||||
/>
|
||||
</AllInOneMenu.ItemPanel>
|
||||
|
||||
+2
-3
@@ -3,13 +3,13 @@ import { Icons } from '@ohif/ui-next';
|
||||
import React, { ReactElement } from 'react';
|
||||
import { VolumeRenderingPresetsProps } from '../../types/ViewportPresets';
|
||||
import { VolumeRenderingPresetsContent } from './VolumeRenderingPresetsContent';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export function VolumeRenderingPresets({
|
||||
viewportId,
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
volumeRenderingPresets,
|
||||
}: VolumeRenderingPresetsProps): ReactElement {
|
||||
const { servicesManager } = useSystem();
|
||||
const { uiDialogService } = servicesManager.services;
|
||||
|
||||
const onClickPresets = () => {
|
||||
@@ -21,7 +21,6 @@ export function VolumeRenderingPresets({
|
||||
contentProps: {
|
||||
presets: volumeRenderingPresets,
|
||||
viewportId,
|
||||
commandsManager,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,13 +1,13 @@
|
||||
import React, { ReactElement, useCallback, useState, useEffect } from 'react';
|
||||
import { VolumeRenderingQualityProps } from '../../types/ViewportPresets';
|
||||
import { Numeric } from '@ohif/ui-next';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export function VolumeRenderingQuality({
|
||||
volumeRenderingQualityRange,
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
viewportId,
|
||||
}: VolumeRenderingQualityProps): ReactElement {
|
||||
const { servicesManager, commandsManager } = useSystem();
|
||||
const { cornerstoneViewportService } = servicesManager.services;
|
||||
const { min, max, step } = volumeRenderingQualityRange;
|
||||
const [quality, setQuality] = useState(null);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
|
||||
import { Switch } from '@ohif/ui-next';
|
||||
import { VolumeShadeProps } from '../../types/ViewportPresets';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export function VolumeShade({
|
||||
commandsManager,
|
||||
viewportId,
|
||||
servicesManager,
|
||||
onClickShade = bool => {},
|
||||
}: VolumeShadeProps): ReactElement {
|
||||
const { servicesManager, commandsManager } = useSystem();
|
||||
const { cornerstoneViewportService } = servicesManager.services;
|
||||
const [shade, setShade] = useState(true);
|
||||
const [key, setKey] = useState(0);
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React, { ReactElement, useCallback, useEffect, useState, useRef } from 'react';
|
||||
import { VolumeShiftProps } from '../../types/ViewportPresets';
|
||||
import { Numeric } from '@ohif/ui-next';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export function VolumeShift({
|
||||
viewportId,
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
}: VolumeShiftProps): ReactElement {
|
||||
export function VolumeShift({ viewportId }: VolumeShiftProps): ReactElement {
|
||||
const { servicesManager, commandsManager } = useSystem();
|
||||
const { cornerstoneViewportService } = servicesManager.services;
|
||||
const [minShift, setMinShift] = useState<number | null>(null);
|
||||
const [maxShift, setMaxShift] = useState<number | null>(null);
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import React, { ReactElement, useCallback } from 'react';
|
||||
import { AllInOneMenu } from '@ohif/ui-next';
|
||||
import { WindowLevelPreset } from '../../types/WindowLevel';
|
||||
import { CommandsManager } from '@ohif/core';
|
||||
import { useSystem } from '@ohif/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export type WindowLevelProps = {
|
||||
viewportId: string;
|
||||
presets: Array<Record<string, Array<WindowLevelPreset>>>;
|
||||
commandsManager: CommandsManager;
|
||||
};
|
||||
|
||||
export function WindowLevel({
|
||||
viewportId,
|
||||
commandsManager,
|
||||
presets,
|
||||
}: WindowLevelProps): ReactElement {
|
||||
export function WindowLevel({ viewportId, presets }: WindowLevelProps): ReactElement {
|
||||
const { commandsManager } = useSystem();
|
||||
const { t } = useTranslation('WindowLevelActionMenu');
|
||||
|
||||
const onSetWindowLevel = useCallback(
|
||||
|
||||
+6
-17
@@ -15,8 +15,7 @@ import { VolumeRenderingOptions } from './VolumeRenderingOptions';
|
||||
import { ViewportPreset } from '../../types/ViewportPresets';
|
||||
import { VolumeViewport3D } from '@cornerstonejs/core';
|
||||
import { utilities } from '@cornerstonejs/core';
|
||||
|
||||
export const nonWLModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export type WindowLevelActionMenuProps = {
|
||||
viewportId: string;
|
||||
@@ -34,13 +33,12 @@ export function WindowLevelActionMenu({
|
||||
presets,
|
||||
verticalDirection,
|
||||
horizontalDirection,
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
colorbarProperties,
|
||||
displaySets,
|
||||
volumeRenderingPresets,
|
||||
volumeRenderingQualityRange,
|
||||
}: withAppTypes<WindowLevelActionMenuProps>): ReactElement {
|
||||
const { commandsManager, servicesManager } = useSystem();
|
||||
const {
|
||||
colormaps,
|
||||
colorbarContainerPosition,
|
||||
@@ -48,6 +46,7 @@ export function WindowLevelActionMenu({
|
||||
colorbarTickPosition,
|
||||
width: colorbarWidth,
|
||||
} = colorbarProperties;
|
||||
|
||||
const { colorbarService, cornerstoneViewportService } = servicesManager.services;
|
||||
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
|
||||
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
@@ -64,7 +63,7 @@ export function WindowLevelActionMenu({
|
||||
const [is3DVolume, setIs3DVolume] = useState(false);
|
||||
|
||||
const onSetColorbar = useCallback(() => {
|
||||
setViewportColorbar(viewportId, displaySets, commandsManager, servicesManager, {
|
||||
setViewportColorbar(viewportId, commandsManager, servicesManager, {
|
||||
colormaps,
|
||||
ticks: {
|
||||
position: colorbarTickPosition,
|
||||
@@ -117,7 +116,6 @@ export function WindowLevelActionMenu({
|
||||
verticalDirection={verticalDirection}
|
||||
horizontalDirection={horizontalDirection}
|
||||
iconClassName={classNames(
|
||||
// Visible on hover and for the active viewport
|
||||
activeViewportId === viewportId ? 'visible' : 'invisible group-hover/pane:visible',
|
||||
'flex shrink-0 cursor-pointer rounded active:text-foreground text-highlight',
|
||||
isLight ? ' hover:bg-primary/30' : 'hover:bg-primary/30'
|
||||
@@ -132,9 +130,7 @@ export function WindowLevelActionMenu({
|
||||
{!is3DVolume && (
|
||||
<Colorbar
|
||||
viewportId={viewportId}
|
||||
displaySets={displaySets.filter(ds => !nonWLModalities.includes(ds.Modality))}
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
displaySets={displaySets.filter(ds => ds.supportsWindowLevel === true)}
|
||||
colorbarProperties={colorbarProperties}
|
||||
/>
|
||||
)}
|
||||
@@ -150,9 +146,7 @@ export function WindowLevelActionMenu({
|
||||
className="flex h-full w-full flex-col"
|
||||
colormaps={colormaps}
|
||||
viewportId={viewportId}
|
||||
displaySets={displaySets.filter(ds => !nonWLModalities.includes(ds.Modality))}
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
displaySets={displaySets.filter(ds => ds.supportsWindowLevel === true)}
|
||||
/>
|
||||
</AllInOneMenu.SubMenu>
|
||||
)}
|
||||
@@ -165,7 +159,6 @@ export function WindowLevelActionMenu({
|
||||
>
|
||||
<WindowLevel
|
||||
viewportId={viewportId}
|
||||
commandsManager={commandsManager}
|
||||
presets={presets}
|
||||
/>
|
||||
</AllInOneMenu.SubMenu>
|
||||
@@ -173,9 +166,7 @@ export function WindowLevelActionMenu({
|
||||
|
||||
{volumeRenderingPresets && is3DVolume && (
|
||||
<VolumeRenderingPresets
|
||||
servicesManager={servicesManager}
|
||||
viewportId={viewportId}
|
||||
commandsManager={commandsManager}
|
||||
volumeRenderingPresets={volumeRenderingPresets}
|
||||
/>
|
||||
)}
|
||||
@@ -184,9 +175,7 @@ export function WindowLevelActionMenu({
|
||||
<AllInOneMenu.SubMenu itemLabel="Rendering Options">
|
||||
<VolumeRenderingOptions
|
||||
viewportId={viewportId}
|
||||
commandsManager={commandsManager}
|
||||
volumeRenderingQualityRange={volumeRenderingQualityRange}
|
||||
servicesManager={servicesManager}
|
||||
/>
|
||||
</AllInOneMenu.SubMenu>
|
||||
)}
|
||||
|
||||
+10
-14
@@ -1,12 +1,12 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { nonWLModalities } from './WindowLevelActionMenu';
|
||||
import { WindowLevelActionMenuProps } from './WindowLevelActionMenu';
|
||||
import { useSystem } from '@ohif/core';
|
||||
import { WindowLevelActionMenu as WindowLevelActionMenuComponent } from './WindowLevelActionMenu';
|
||||
|
||||
export function getWindowLevelActionMenu({
|
||||
function WindowLevelActionMenu({
|
||||
viewportId,
|
||||
element,
|
||||
displaySets,
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
verticalDirection,
|
||||
horizontalDirection,
|
||||
}: withAppTypes<{
|
||||
@@ -14,31 +14,25 @@ export function getWindowLevelActionMenu({
|
||||
element: HTMLElement;
|
||||
displaySets: AppTypes.DisplaySet[];
|
||||
}>): ReactNode {
|
||||
const { servicesManager } = useSystem();
|
||||
const { customizationService } = servicesManager.services;
|
||||
|
||||
const presets = customizationService.getCustomization('cornerstone.windowLevelPresets');
|
||||
const colorbarProperties = customizationService.getCustomization('cornerstone.colorbar');
|
||||
const { volumeRenderingPresets, volumeRenderingQualityRange } =
|
||||
customizationService.getCustomization('cornerstone.3dVolumeRendering');
|
||||
const WindowLevelActionMenu = customizationService.getCustomization(
|
||||
'viewportActionMenu.windowLevelActionMenu'
|
||||
);
|
||||
const displaySetPresets = displaySets
|
||||
.filter(displaySet => presets[displaySet.Modality])
|
||||
.map(displaySet => {
|
||||
return { [displaySet.Modality]: presets[displaySet.Modality] };
|
||||
});
|
||||
|
||||
const modalities = displaySets
|
||||
.map(displaySet => displaySet.Modality)
|
||||
.filter(modality => !nonWLModalities.includes(modality));
|
||||
const modalities = displaySets.map(displaySet => displaySet.supportsWindowLevel);
|
||||
|
||||
if (modalities.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const WindowLevelActionMenuComponent = WindowLevelActionMenu?.component;
|
||||
|
||||
return (
|
||||
<WindowLevelActionMenuComponent
|
||||
viewportId={viewportId}
|
||||
@@ -46,8 +40,6 @@ export function getWindowLevelActionMenu({
|
||||
presets={displaySetPresets}
|
||||
verticalDirection={verticalDirection}
|
||||
horizontalDirection={horizontalDirection}
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
colorbarProperties={colorbarProperties}
|
||||
displaySets={displaySets}
|
||||
volumeRenderingPresets={volumeRenderingPresets}
|
||||
@@ -55,3 +47,7 @@ export function getWindowLevelActionMenu({
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function getWindowLevelActionMenu(props: WindowLevelActionMenuProps) {
|
||||
return <WindowLevelActionMenu {...props} />;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Volume loader schemes
|
||||
export const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
|
||||
export const DYNAMIC_VOLUME_LOADER_SCHEME = 'cornerstoneStreamingDynamicImageVolume';
|
||||
@@ -0,0 +1,23 @@
|
||||
export default {
|
||||
'cornerstone.modalityOverlayDefaultColorMaps': {
|
||||
defaultSettings: {
|
||||
PT: {
|
||||
colormap: 'hsv',
|
||||
// Note: Right now, there is a nonlinear relationship between the opacity value
|
||||
// below and how it will get applied to the image. The limitation is in rendering.
|
||||
// We are working on this and will remove this note when it's fixed.
|
||||
// But don't expect 0.5 to be 50% opacity, but rather close to that.
|
||||
opacity: 0.5,
|
||||
},
|
||||
RTDOSE: {
|
||||
colormap: 'Isodose',
|
||||
// Note: Right now, there is a nonlinear relationship between the opacity value
|
||||
// below and how it will get applied to the image. The limitation is in rendering.
|
||||
// We are working on this and will remove this note when it's fixed.
|
||||
// But don't expect 0.5 to be 50% opacity, but rather close to that.
|
||||
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,14 +1,55 @@
|
||||
import viewportActionCornersService from '../services/ViewportActionCornersService/ViewportActionCornersService';
|
||||
import { WindowLevelActionMenu } from '../components/WindowLevelActionMenu/WindowLevelActionMenu';
|
||||
import { getWindowLevelActionMenu } from '../components/WindowLevelActionMenu/getWindowLevelActionMenu';
|
||||
import { getViewportDataOverlaySettingsMenu } from '../components/ViewportDataOverlaySettingMenu';
|
||||
import { getViewportOrientationMenu } from '../components/ViewportOrientationMenu';
|
||||
import { AllInOneMenu } from '@ohif/ui-next';
|
||||
|
||||
// Generate component renderer functions for each component type
|
||||
const createOrientationMenu = ({ viewportId, element, location }) => {
|
||||
return getViewportOrientationMenu({
|
||||
viewportId,
|
||||
element,
|
||||
location,
|
||||
});
|
||||
};
|
||||
|
||||
const createDataOverlay = ({ viewportId, element, displaySets, location }) => {
|
||||
return getViewportDataOverlaySettingsMenu({
|
||||
viewportId,
|
||||
element,
|
||||
displaySets,
|
||||
location,
|
||||
});
|
||||
};
|
||||
|
||||
const createWindowLevelMenu = ({ viewportId, element, displaySets }) => {
|
||||
return getWindowLevelActionMenu({
|
||||
viewportId,
|
||||
element,
|
||||
displaySets,
|
||||
verticalDirection: AllInOneMenu.VerticalDirection.TopToBottom,
|
||||
horizontalDirection: AllInOneMenu.HorizontalDirection.LeftToRight,
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
'viewportActionMenu.windowLevelActionMenu': {
|
||||
enabled: true,
|
||||
location: viewportActionCornersService.LOCATIONS.topRight,
|
||||
component: WindowLevelActionMenu,
|
||||
},
|
||||
'viewportActionMenu.segmentationOverlay': {
|
||||
enabled: true,
|
||||
location: viewportActionCornersService.LOCATIONS.topRight,
|
||||
},
|
||||
'viewportActionMenu.topLeft': [
|
||||
{
|
||||
id: 'orientationMenu',
|
||||
enabled: true,
|
||||
component: createOrientationMenu,
|
||||
},
|
||||
{
|
||||
id: 'dataOverlay',
|
||||
enabled: true,
|
||||
component: createDataOverlay,
|
||||
},
|
||||
{
|
||||
id: 'windowLevelActionMenu',
|
||||
enabled: true,
|
||||
component: createWindowLevelMenu,
|
||||
},
|
||||
],
|
||||
'viewportActionMenu.topRight': [],
|
||||
'viewportActionMenu.bottomLeft': [],
|
||||
'viewportActionMenu.bottomRight': [],
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import viewportClickCommandsCustomization from './customizations/viewportClickCo
|
||||
import measurementsCustomization from './customizations/measurementsCustomization';
|
||||
import volumeRenderingCustomization from './customizations/volumeRenderingCustomization';
|
||||
import colorbarCustomization from './customizations/colorbarCustomization';
|
||||
import modalityColorMapCustomization from './customizations/modalityColorMapCustomization';
|
||||
import windowLevelPresetsCustomization from './customizations/windowLevelPresetsCustomization';
|
||||
import miscCustomization from './customizations/miscCustomization';
|
||||
import captureViewportModalCustomization from './customizations/captureViewportModalCustomization';
|
||||
@@ -25,6 +26,7 @@ function getCustomizationModule({ commandsManager, servicesManager }) {
|
||||
...measurementsCustomization,
|
||||
...volumeRenderingCustomization,
|
||||
...colorbarCustomization,
|
||||
...modalityColorMapCustomization,
|
||||
...windowLevelPresetsCustomization,
|
||||
...miscCustomization,
|
||||
...captureViewportModalCustomization,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import i18n from '@ohif/i18n';
|
||||
import { utilities as csUtils, Enums as csEnums } from '@cornerstonejs/core';
|
||||
import dcmjs from 'dcmjs';
|
||||
import { dicomWebUtils } from '@ohif/extension-default';
|
||||
@@ -112,6 +113,7 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
|
||||
instances,
|
||||
othersFrameOfReferenceUID,
|
||||
imageIds: instances.map(instance => instance.imageId),
|
||||
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SM')}`,
|
||||
};
|
||||
// The microscopy viewer directly accesses the metadata already loaded, and
|
||||
// uses the DICOMweb client library directly for loading, so it has to be
|
||||
|
||||
@@ -233,7 +233,7 @@ function _evaluateToggle({
|
||||
}
|
||||
const toolName = toolbarService.getToolNameForButton(button);
|
||||
|
||||
if (!toolGroup.hasTool(toolName)) {
|
||||
if (!toolGroup?.hasTool(toolName)) {
|
||||
return getDisabledState(disabledText);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export { default as useViewportActionCorners } from './useViewportActionCorners';
|
||||
export * from './useViewportActionCorners';
|
||||
export * from './useActiveViewportSegmentationRepresentations';
|
||||
export * from './useMeasurements';
|
||||
export * from './useSegmentations';
|
||||
@@ -84,7 +84,7 @@ function mapSegmentationToDisplay(segmentation, customizationService) {
|
||||
* @param options.debounceTime - Debounce time in milliseconds for updates.
|
||||
* @returns An array of segmentation data.
|
||||
*/
|
||||
export function useSegmentations(options: {
|
||||
export function useSegmentations(options?: {
|
||||
subscribeToDataModified?: boolean;
|
||||
debounceTime?: number;
|
||||
}): SegmentationData[] {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { ViewportActionCornersLocations } from '@ohif/ui-next';
|
||||
import { useSystem } from '@ohif/core/src';
|
||||
|
||||
/**
|
||||
* Viewport action corner location type
|
||||
*/
|
||||
export interface ViewportActionCornerLocation {
|
||||
type: 'VIEWPORT';
|
||||
id: string;
|
||||
corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
||||
}
|
||||
|
||||
/**
|
||||
* Viewport action corner service interface
|
||||
*/
|
||||
export interface ViewportActionCornerService {
|
||||
addComponent: (params: {
|
||||
viewportId: string;
|
||||
id: string;
|
||||
component: React.ReactNode;
|
||||
location: ViewportActionCornersLocations | string;
|
||||
indexPriority?: number;
|
||||
}) => void;
|
||||
clear: (viewportId: string) => void;
|
||||
getAlignAndSide: (location: ViewportActionCornersLocations | string) => {
|
||||
align: 'start' | 'center' | 'end';
|
||||
side: 'top' | 'right' | 'bottom' | 'left';
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization service interface
|
||||
*/
|
||||
export interface CustomizationService {
|
||||
getCustomization: (name: string) => any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for the useViewportActionCorners hook
|
||||
*/
|
||||
interface UseViewportActionCornersProps {
|
||||
viewportId: string;
|
||||
elementRef: React.MutableRefObject<HTMLDivElement>;
|
||||
displaySets: AppTypes.DisplaySet[];
|
||||
}
|
||||
|
||||
// Map of customization keys to their corresponding enum values
|
||||
const locationMap = {
|
||||
'viewportActionMenu.topLeft': ViewportActionCornersLocations.topLeft,
|
||||
'viewportActionMenu.topRight': ViewportActionCornersLocations.topRight,
|
||||
'viewportActionMenu.bottomLeft': ViewportActionCornersLocations.bottomLeft,
|
||||
'viewportActionMenu.bottomRight': ViewportActionCornersLocations.bottomRight,
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to manage viewport action corners for a Cornerstone viewport
|
||||
*/
|
||||
export function useViewportActionCorners({
|
||||
viewportId,
|
||||
elementRef,
|
||||
displaySets,
|
||||
}: UseViewportActionCornersProps): void {
|
||||
const { servicesManager } = useSystem();
|
||||
const { viewportActionCornersService, customizationService } = servicesManager.services;
|
||||
|
||||
useEffect(() => {
|
||||
// Process each location
|
||||
Object.entries(locationMap).forEach(([locationKey, locationValue]) => {
|
||||
const items = customizationService.getCustomization(locationKey);
|
||||
|
||||
if (!items || !items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.forEach(item => {
|
||||
if (!item.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const componentId = item.id;
|
||||
|
||||
if (item.component) {
|
||||
// Use the component renderer provided directly in the item
|
||||
const component = item.component({
|
||||
viewportId,
|
||||
element: elementRef.current,
|
||||
displaySets,
|
||||
location: locationValue,
|
||||
});
|
||||
|
||||
viewportActionCornersService.addComponent({
|
||||
viewportId,
|
||||
id: componentId,
|
||||
component,
|
||||
location: locationValue,
|
||||
});
|
||||
} else if (item.component) {
|
||||
// Handle static components
|
||||
viewportActionCornersService.addComponent({
|
||||
viewportId,
|
||||
id: item.id,
|
||||
component: item.component,
|
||||
location: locationValue,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [displaySets, viewportId, viewportActionCornersService, elementRef]);
|
||||
}
|
||||
|
||||
export default useViewportActionCorners;
|
||||
@@ -0,0 +1,247 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useSystem, utils } from '@ohif/core';
|
||||
import { useViewportGrid } from '@ohif/ui-next';
|
||||
import {
|
||||
getEnhancedDisplaySets,
|
||||
sortByOverlayable,
|
||||
DERIVED_OVERLAY_MODALITIES,
|
||||
} from '../components/ViewportDataOverlaySettingMenu/utils';
|
||||
|
||||
const sortByPriority = (a, b) => {
|
||||
if (utils.isLowPriorityModality(a.Modality)) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for the useViewportDisplaySets hook
|
||||
*/
|
||||
export type UseViewportDisplaySetsOptions = {
|
||||
/**
|
||||
* Whether to include background display set
|
||||
*/
|
||||
includeBackground?: boolean;
|
||||
/**
|
||||
* Whether to include foreground display sets
|
||||
*/
|
||||
includeForeground?: boolean;
|
||||
/**
|
||||
* Whether to include overlay display sets
|
||||
*/
|
||||
includeOverlay?: boolean;
|
||||
/**
|
||||
* Whether to include potential overlay display sets
|
||||
*/
|
||||
includePotentialOverlay?: boolean;
|
||||
/**
|
||||
* Whether to include potential foreground display sets
|
||||
*/
|
||||
includePotentialForeground?: boolean;
|
||||
/**
|
||||
* Whether to include potential background display sets
|
||||
*/
|
||||
includePotentialBackground?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return type for useViewportDisplaySets
|
||||
*/
|
||||
export type ViewportDisplaySets = {
|
||||
/**
|
||||
* The primary display set for the viewport (base image)
|
||||
*/
|
||||
backgroundDisplaySet?: any;
|
||||
/**
|
||||
* Display sets currently shown with background (non-overlay layers)
|
||||
*/
|
||||
foregroundDisplaySets?: any[];
|
||||
/**
|
||||
* Segmentation display sets currently applied as overlays
|
||||
*/
|
||||
overlayDisplaySets?: any[];
|
||||
/**
|
||||
* Display sets that could be toggled on as overlays (derived modalities)
|
||||
*/
|
||||
potentialOverlayDisplaySets?: any[];
|
||||
/**
|
||||
* Display sets that could be added as foreground layers
|
||||
*/
|
||||
potentialForegroundDisplaySets?: any[];
|
||||
/**
|
||||
* Display sets that could replace the current background
|
||||
*/
|
||||
potentialBackgroundDisplaySets?: any[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to provide display sets and overlay information for a viewport based on options.
|
||||
*
|
||||
* @param viewportId - The viewport ID to get display sets for
|
||||
* @param options - Options to control which display sets to compute
|
||||
* @returns Object containing requested display set collections based on options
|
||||
*/
|
||||
export function useViewportDisplaySets(
|
||||
viewportId: string,
|
||||
options?: UseViewportDisplaySetsOptions
|
||||
): ViewportDisplaySets {
|
||||
const { servicesManager } = useSystem();
|
||||
const { displaySetService, segmentationService } = servicesManager.services;
|
||||
|
||||
// Note: this is very important we should use the useViewportGrid hook here,
|
||||
// since if the viewport displaySet is changed we should re-run this hook
|
||||
// to get the latest displaySets
|
||||
const [_, viewportGridService] = useViewportGrid();
|
||||
|
||||
// Apply defaults - include everything if no options specified
|
||||
const {
|
||||
includeBackground = true,
|
||||
includeForeground = true,
|
||||
includeOverlay = true,
|
||||
includePotentialOverlay = true,
|
||||
includePotentialForeground = true,
|
||||
includePotentialBackground = true,
|
||||
} = options || {};
|
||||
|
||||
// Get all available display sets (only if needed)
|
||||
const needsAllDisplaySets = includePotentialBackground;
|
||||
const allDisplaySets = useMemo(
|
||||
() => (needsAllDisplaySets ? displaySetService.getActiveDisplaySets() : []),
|
||||
[displaySetService, needsAllDisplaySets]
|
||||
);
|
||||
|
||||
// Get all available segmentations (only if needed)
|
||||
const needsSegmentations = includeOverlay;
|
||||
const segmentationRepresentations = useMemo(
|
||||
() =>
|
||||
needsSegmentations ? segmentationService.getSegmentationRepresentations(viewportId) : [],
|
||||
[segmentationService, viewportId, needsSegmentations]
|
||||
);
|
||||
|
||||
const overlayDisplaySets = useMemo(() => {
|
||||
if (!includeOverlay) {
|
||||
return [];
|
||||
}
|
||||
return segmentationRepresentations.map(repr => {
|
||||
const displaySet = displaySetService.getDisplaySetByUID(repr.segmentationId);
|
||||
return displaySet;
|
||||
});
|
||||
}, [includeOverlay, segmentationRepresentations, displaySetService]);
|
||||
|
||||
const overlayDisplaySetUIDs = useMemo(
|
||||
() => overlayDisplaySets.map(ds => ds.displaySetInstanceUID),
|
||||
[overlayDisplaySets]
|
||||
);
|
||||
|
||||
// Get enhanced display sets (only if needed)
|
||||
const needsEnhancedDisplaySets =
|
||||
includeBackground || includeForeground || includePotentialOverlay || includePotentialForeground;
|
||||
|
||||
const { viewportDisplaySets, enhancedDisplaySets } = useMemo(() => {
|
||||
if (!needsEnhancedDisplaySets) {
|
||||
return { viewportDisplaySets: [], enhancedDisplaySets: [] };
|
||||
}
|
||||
return getEnhancedDisplaySets({
|
||||
viewportId,
|
||||
services: { displaySetService, viewportGridService },
|
||||
});
|
||||
}, [viewportId, displaySetService, viewportGridService, needsEnhancedDisplaySets]);
|
||||
|
||||
const backgroundDisplaySet = useMemo(
|
||||
() => (includeBackground ? viewportDisplaySets[0] : undefined),
|
||||
[includeBackground, viewportDisplaySets]
|
||||
);
|
||||
|
||||
const foregroundDisplaySets = useMemo(() => {
|
||||
if (!includeForeground || !backgroundDisplaySet) {
|
||||
return [];
|
||||
}
|
||||
return viewportDisplaySets.filter(
|
||||
ds =>
|
||||
!DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
|
||||
ds.displaySetInstanceUID !== backgroundDisplaySet.displaySetInstanceUID
|
||||
);
|
||||
}, [includeForeground, viewportDisplaySets, backgroundDisplaySet]);
|
||||
|
||||
const foregroundDisplaySetUIDs = useMemo(
|
||||
() => foregroundDisplaySets.map(ds => ds.displaySetInstanceUID),
|
||||
[foregroundDisplaySets]
|
||||
);
|
||||
|
||||
const potentialOverlayDisplaySets = useMemo(() => {
|
||||
if (!includePotentialOverlay) {
|
||||
return [];
|
||||
}
|
||||
return enhancedDisplaySets
|
||||
.filter(
|
||||
ds =>
|
||||
DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
|
||||
!overlayDisplaySetUIDs.includes(ds.displaySetInstanceUID) &&
|
||||
ds.isOverlayable
|
||||
)
|
||||
.sort(sortByOverlayable);
|
||||
}, [includePotentialOverlay, enhancedDisplaySets, overlayDisplaySetUIDs]);
|
||||
|
||||
const potentialForegroundDisplaySets = useMemo(() => {
|
||||
if (!includePotentialForeground) {
|
||||
return [];
|
||||
}
|
||||
return enhancedDisplaySets
|
||||
.filter(
|
||||
ds =>
|
||||
!DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
|
||||
!foregroundDisplaySetUIDs.includes(ds.displaySetInstanceUID) &&
|
||||
ds.isOverlayable
|
||||
)
|
||||
.sort(sortByPriority);
|
||||
}, [includePotentialForeground, enhancedDisplaySets, foregroundDisplaySetUIDs]);
|
||||
|
||||
const potentialBackgroundDisplaySets = useMemo(() => {
|
||||
if (!includePotentialBackground || !backgroundDisplaySet) {
|
||||
return [];
|
||||
}
|
||||
return allDisplaySets
|
||||
.filter(
|
||||
ds =>
|
||||
!DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
|
||||
ds.displaySetInstanceUID !== backgroundDisplaySet.displaySetInstanceUID &&
|
||||
!overlayDisplaySetUIDs.includes(ds.displaySetInstanceUID) &&
|
||||
!foregroundDisplaySetUIDs.includes(ds.displaySetInstanceUID)
|
||||
)
|
||||
.sort(sortByPriority);
|
||||
}, [
|
||||
includePotentialBackground,
|
||||
allDisplaySets,
|
||||
backgroundDisplaySet,
|
||||
overlayDisplaySetUIDs,
|
||||
foregroundDisplaySetUIDs,
|
||||
]);
|
||||
|
||||
const result: ViewportDisplaySets = {};
|
||||
|
||||
if (includeBackground) {
|
||||
result.backgroundDisplaySet = backgroundDisplaySet;
|
||||
}
|
||||
|
||||
if (includeForeground) {
|
||||
result.foregroundDisplaySets = foregroundDisplaySets;
|
||||
}
|
||||
|
||||
if (includeOverlay) {
|
||||
result.overlayDisplaySets = overlayDisplaySets;
|
||||
}
|
||||
|
||||
if (includePotentialOverlay) {
|
||||
result.potentialOverlayDisplaySets = potentialOverlayDisplaySets;
|
||||
}
|
||||
|
||||
if (includePotentialForeground) {
|
||||
result.potentialForegroundDisplaySets = potentialForegroundDisplaySets;
|
||||
}
|
||||
|
||||
if (includePotentialBackground) {
|
||||
result.potentialBackgroundDisplaySets = potentialBackgroundDisplaySets;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -269,4 +269,7 @@ export {
|
||||
CornerstoneViewportDownloadForm,
|
||||
utils,
|
||||
};
|
||||
|
||||
// Export constants
|
||||
export { VOLUME_LOADER_SCHEME, DYNAMIC_VOLUME_LOADER_SCHEME } from './constants';
|
||||
export default cornerstoneExtension;
|
||||
@@ -96,6 +96,8 @@ export default async function init({
|
||||
viewportGridService,
|
||||
segmentationService,
|
||||
measurementService,
|
||||
colorbarService,
|
||||
displaySetService,
|
||||
} = servicesManager.services;
|
||||
|
||||
window.services = servicesManager.services;
|
||||
@@ -232,16 +234,6 @@ export default async function init({
|
||||
}
|
||||
);
|
||||
|
||||
// resize the cornerstone viewport service when the grid size changes
|
||||
// IMPORTANT: this should happen outside of the OHIFCornerstoneViewport
|
||||
// since it will trigger a rerender of each viewport and each resizing
|
||||
// the offscreen canvas which would result in a performance hit, this should
|
||||
// done only once per grid resize here. Doing it once here, allows us to reduce
|
||||
// the refreshRage(in ms) to 10 from 50. I tried with even 1 or 5 ms it worked fine
|
||||
viewportGridService.subscribe(viewportGridService.EVENTS.GRID_SIZE_CHANGED, () => {
|
||||
cornerstoneViewportService.resize(true);
|
||||
});
|
||||
|
||||
initContextMenu({
|
||||
cornerstoneViewportService,
|
||||
customizationService,
|
||||
@@ -265,8 +257,17 @@ export default async function init({
|
||||
eventTarget.addEventListener(EVENTS.IMAGE_LOAD_FAILED, imageLoadFailedHandler);
|
||||
eventTarget.addEventListener(EVENTS.IMAGE_LOAD_ERROR, imageLoadFailedHandler);
|
||||
|
||||
const getDisplaySetFromVolumeId = (volumeId: string) => {
|
||||
const allDisplaySets = displaySetService.getActiveDisplaySets();
|
||||
const volume = cornerstone.cache.getVolume(volumeId);
|
||||
const imageIds = volume.imageIds;
|
||||
return allDisplaySets.find(ds => ds.imageIds?.some(id => imageIds.includes(id)));
|
||||
};
|
||||
|
||||
function elementEnabledHandler(evt) {
|
||||
const { element } = evt.detail;
|
||||
const { viewport } = getEnabledElement(element);
|
||||
initViewTiming({ element });
|
||||
|
||||
element.addEventListener(EVENTS.CAMERA_RESET, evt => {
|
||||
const { element } = evt.detail;
|
||||
@@ -278,7 +279,10 @@ export default async function init({
|
||||
commandsManager.runCommand('resetCrosshairs', { viewportId });
|
||||
});
|
||||
|
||||
initViewTiming({ element });
|
||||
// limitation: currently supporting only volume viewports with fusion
|
||||
if (viewport.type !== cornerstone.Enums.ViewportType.ORTHOGRAPHIC) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
eventTarget.addEventListener(EVENTS.ELEMENT_ENABLED, elementEnabledHandler.bind(null));
|
||||
@@ -289,58 +293,136 @@ export default async function init({
|
||||
eventTarget.addEventListenerDebounced(
|
||||
EVENTS.ERROR_EVENT,
|
||||
({ detail }) => {
|
||||
// Create a stable ID for deduplication based on error type and message
|
||||
const errorId = `cornerstone-error-${detail.type}-${detail.message.substring(0, 50)}`;
|
||||
|
||||
uiNotificationService.show({
|
||||
title: detail.type,
|
||||
message: detail.message,
|
||||
type: 'error',
|
||||
id: errorId,
|
||||
allowDuplicates: false, // Prevent duplicate error notifications
|
||||
deduplicationInterval: 30000, // 30 seconds deduplication window
|
||||
});
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
// Subscribe to actor events to dynamically update colorbars
|
||||
|
||||
// Call this function when initializing
|
||||
initializeWebWorkerProgressHandler(servicesManager.services.uiNotificationService);
|
||||
|
||||
const { unsubscribe: unsubscribeSegmentationCreated } = segmentationService.subscribe(
|
||||
segmentationService.EVENTS.SEGMENTATION_ADDED,
|
||||
evt => {
|
||||
const { segmentationId } = evt;
|
||||
const displaySet = displaySetService.getDisplaySetByUID(segmentationId);
|
||||
if (displaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||
const label = segmentation.cachedStats.info;
|
||||
const imageIds = segmentation.representationData.Labelmap.imageIds;
|
||||
|
||||
// Create a display set for the segmentation
|
||||
const segmentationDisplaySet = {
|
||||
displaySetInstanceUID: segmentationId,
|
||||
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
|
||||
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
|
||||
SeriesDescription: label,
|
||||
Modality: 'SEG',
|
||||
numImageFrames: imageIds.length,
|
||||
imageIds,
|
||||
isOverlayDisplaySet: true,
|
||||
label,
|
||||
madeInClient: true,
|
||||
segmentationId: segmentationId,
|
||||
isDerived: true,
|
||||
};
|
||||
|
||||
displaySetService.addDisplaySets(segmentationDisplaySet);
|
||||
}
|
||||
);
|
||||
|
||||
const unsubscriptions = [
|
||||
unsubscribeSegmentationDataModifiedHandler,
|
||||
unsubscribeSegmentationModifiedHandler,
|
||||
unsubscribeSegmentationCreated,
|
||||
];
|
||||
|
||||
return { unsubscriptions };
|
||||
}
|
||||
|
||||
function initializeWebWorkerProgressHandler(uiNotificationService) {
|
||||
const activeToasts = new Map();
|
||||
// Use a single map to track all active worker tasks
|
||||
const activeWorkerTasks = new Map();
|
||||
|
||||
// eventTarget.addEventListener(EVENTS.WEB_WORKER_PROGRESS, ({ detail }) => {
|
||||
// const { progress, type, id } = detail;
|
||||
// Create a normalized task key that doesn't include the random ID
|
||||
// This helps us identify and deduplicate the same type of task
|
||||
const getNormalizedTaskKey = type => {
|
||||
return `worker-task-${type.toLowerCase().replace(/\s+/g, '-')}`;
|
||||
};
|
||||
|
||||
// const cacheKey = `${type}-${id}`;
|
||||
// if (progress === 0 && !activeToasts.has(cacheKey)) {
|
||||
// const progressPromise = new Promise((resolve, reject) => {
|
||||
// activeToasts.set(cacheKey, { resolve, reject });
|
||||
// });
|
||||
eventTarget.addEventListener(EVENTS.WEB_WORKER_PROGRESS, ({ detail }) => {
|
||||
const { progress, type, id } = detail;
|
||||
|
||||
// uiNotificationService.show({
|
||||
// id: cacheKey,
|
||||
// title: `${type}`,
|
||||
// message: `${type}: ${progress}%`,
|
||||
// autoClose: false,
|
||||
// promise: progressPromise,
|
||||
// promiseMessages: {
|
||||
// loading: `Computing...`,
|
||||
// success: `Completed successfully`,
|
||||
// error: 'Web Worker failed',
|
||||
// },
|
||||
// });
|
||||
// } else {
|
||||
// if (progress === 100) {
|
||||
// const { resolve } = activeToasts.get(cacheKey);
|
||||
// resolve({ progress, type });
|
||||
// activeToasts.delete(cacheKey);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// Skip notifications for compute statistics
|
||||
if (type === cornerstoneTools.Enums.WorkerTypes.COMPUTE_STATISTICS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedKey = getNormalizedTaskKey(type);
|
||||
|
||||
if (progress === 0) {
|
||||
// Check if we're already tracking a task of this type
|
||||
if (!activeWorkerTasks.has(normalizedKey)) {
|
||||
const progressPromise = new Promise((resolve, reject) => {
|
||||
activeWorkerTasks.set(normalizedKey, {
|
||||
resolve,
|
||||
reject,
|
||||
originalId: id,
|
||||
type,
|
||||
});
|
||||
});
|
||||
|
||||
uiNotificationService.show({
|
||||
id: normalizedKey, // Use the normalized key as ID for better deduplication
|
||||
title: `${type}`,
|
||||
message: `Computing...`,
|
||||
autoClose: false,
|
||||
allowDuplicates: false,
|
||||
deduplicationInterval: 60000, // 60 seconds - prevent frequent notifications of same type
|
||||
promise: progressPromise,
|
||||
promiseMessages: {
|
||||
loading: `Computing...`,
|
||||
success: `Completed successfully`,
|
||||
error: 'Web Worker failed',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// Already tracking this type of task, just let it continue
|
||||
console.debug(`Already tracking a "${type}" task, skipping duplicate notification`);
|
||||
}
|
||||
}
|
||||
// Task completed
|
||||
else if (progress === 100) {
|
||||
// Check if we have this task type in our tracking map
|
||||
const taskData = activeWorkerTasks.get(normalizedKey);
|
||||
|
||||
if (taskData) {
|
||||
// Resolve the promise to update the notification
|
||||
const { resolve } = taskData;
|
||||
resolve({ progress, type });
|
||||
|
||||
// Remove from tracking
|
||||
activeWorkerTasks.delete(normalizedKey);
|
||||
|
||||
console.debug(`Worker task "${type}" completed successfully`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,6 +48,8 @@ import ImageOverlayViewerTool from './tools/ImageOverlayViewerTool';
|
||||
|
||||
export default function initCornerstoneTools(configuration = {}) {
|
||||
CrosshairsTool.isAnnotation = false;
|
||||
LabelmapSlicePropagationTool.isAnnotation = false;
|
||||
MarkerLabelmapTool.isAnnotation = false;
|
||||
ReferenceLinesTool.isAnnotation = false;
|
||||
AdvancedMagnifyTool.isAnnotation = false;
|
||||
PlanarFreehandContourSegmentationTool.isAnnotation = false;
|
||||
|
||||
@@ -38,14 +38,16 @@ export default class ColorbarService extends PubSubService {
|
||||
|
||||
public static REGISTRATION = {
|
||||
name: 'colorbarService',
|
||||
create: () => {
|
||||
return new ColorbarService();
|
||||
create: ({ servicesManager }: OhifTypes.Extensions.ExtensionParams) => {
|
||||
return new ColorbarService(servicesManager);
|
||||
},
|
||||
};
|
||||
colorbars = {};
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
|
||||
constructor() {
|
||||
constructor(servicesManager: AppTypes.ServicesManager) {
|
||||
super(ColorbarService.EVENTS);
|
||||
this.servicesManager = servicesManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,6 +70,7 @@ export default class ColorbarService extends PubSubService {
|
||||
* @param options Configuration options for the colorbar, including position, colormaps, active colormap name, ticks, and width.
|
||||
*/
|
||||
public addColorbar(viewportId, displaySetInstanceUIDs, options = {} as ColorbarOptions) {
|
||||
const { displaySetService } = this.servicesManager.services;
|
||||
const renderingEngine = getRenderingEngine(RENDERING_ENGINE_ID);
|
||||
const viewport = renderingEngine.getViewport(viewportId);
|
||||
|
||||
@@ -95,6 +98,12 @@ export default class ColorbarService extends PubSubService {
|
||||
);
|
||||
|
||||
displaySetInstanceUIDs.forEach((displaySetInstanceUID, index) => {
|
||||
// don't show colorbar for overlay display sets (e.g. segmentation)
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
if (displaySet.isOverlayDisplaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
const volumeId = this.getVolumeIdForIdentifier(viewport, displaySetInstanceUID);
|
||||
const properties = viewport?.getProperties(volumeId);
|
||||
const colormap = properties?.colormap;
|
||||
@@ -123,9 +132,19 @@ export default class ColorbarService extends PubSubService {
|
||||
volumeId: viewport instanceof VolumeViewport ? volumeId : undefined,
|
||||
});
|
||||
if (this.colorbars[viewportId]) {
|
||||
this.colorbars[viewportId].push({ colorbar, container: colorbarContainer });
|
||||
this.colorbars[viewportId].push({
|
||||
colorbar,
|
||||
container: colorbarContainer,
|
||||
displaySetInstanceUID,
|
||||
});
|
||||
} else {
|
||||
this.colorbars[viewportId] = [{ colorbar, container: colorbarContainer }];
|
||||
this.colorbars[viewportId] = [
|
||||
{
|
||||
colorbar,
|
||||
container: colorbarContainer,
|
||||
displaySetInstanceUID,
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -136,24 +155,50 @@ export default class ColorbarService extends PubSubService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the colorbar associated with a given viewport ID. This involves cleaning up any created DOM elements and internal references.
|
||||
* Removes a colorbar from a specific viewport. If displaySetInstanceUID is provided,
|
||||
* only the colorbar associated with that specific displaySetInstanceUID will be removed.
|
||||
* Otherwise, all colorbars for the given viewport will be removed.
|
||||
*
|
||||
* @param viewportId The identifier for the viewport from which the colorbar will be removed.
|
||||
* @param displaySetInstanceUID Optional. The specific display set instance UID associated with the colorbar to remove.
|
||||
*/
|
||||
public removeColorbar(viewportId) {
|
||||
public removeColorbar(viewportId, displaySetInstanceUID?: string) {
|
||||
const colorbarInfo = this.colorbars[viewportId];
|
||||
if (!colorbarInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
colorbarInfo.forEach(({ colorbar, container }) => {
|
||||
container.parentNode.removeChild(container);
|
||||
});
|
||||
if (displaySetInstanceUID) {
|
||||
// Find the index of the colorbar with the matching displaySetInstanceUID
|
||||
const index = colorbarInfo.findIndex(
|
||||
info => info.displaySetInstanceUID === displaySetInstanceUID
|
||||
);
|
||||
|
||||
delete this.colorbars[viewportId];
|
||||
if (index !== -1) {
|
||||
// Remove only the specific colorbar container
|
||||
const { container } = colorbarInfo[index];
|
||||
container.parentNode.removeChild(container);
|
||||
|
||||
// Remove the colorbar from the array
|
||||
colorbarInfo.splice(index, 1);
|
||||
|
||||
// If there are no more colorbars for this viewport, remove the entry
|
||||
if (colorbarInfo.length === 0) {
|
||||
delete this.colorbars[viewportId];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Remove all colorbars for the viewport (original behavior)
|
||||
colorbarInfo.forEach(({ container }) => {
|
||||
container.parentNode.removeChild(container);
|
||||
});
|
||||
|
||||
delete this.colorbars[viewportId];
|
||||
}
|
||||
|
||||
this._broadcastEvent(ColorbarService.EVENTS.STATE_CHANGED, {
|
||||
viewportId,
|
||||
displaySetInstanceUID,
|
||||
changeType: ChangeTypes.Removed,
|
||||
});
|
||||
}
|
||||
|
||||
+1
-2
@@ -3,8 +3,7 @@ import { cache as cs3DCache, Enums, volumeLoader } from '@cornerstonejs/core';
|
||||
|
||||
import getCornerstoneViewportType from '../../utils/getCornerstoneViewportType';
|
||||
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
|
||||
|
||||
const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
|
||||
import { VOLUME_LOADER_SCHEME } from '../../constants';
|
||||
|
||||
class CornerstoneCacheService {
|
||||
static REGISTRATION = {
|
||||
|
||||
+21
-18
@@ -8,26 +8,29 @@
|
||||
* and geometry Id
|
||||
*/
|
||||
export function mapROIContoursToRTStructData(structureSet: unknown, rtDisplaySetUID: unknown) {
|
||||
return structureSet.ROIContours.map(({ contourPoints, ROINumber, ROIName, colorArray }) => {
|
||||
const data = contourPoints.map(({ points, ...rest }) => {
|
||||
const newPoints = points.map(({ x, y, z }) => {
|
||||
return [x, y, z];
|
||||
return structureSet.ROIContours.map(
|
||||
({ contourPoints, ROINumber, ROIName, colorArray, ROIGroup }) => {
|
||||
const data = contourPoints.map(({ points, ...rest }) => {
|
||||
const newPoints = points.map(({ x, y, z }) => {
|
||||
return [x, y, z];
|
||||
});
|
||||
|
||||
return {
|
||||
...rest,
|
||||
points: newPoints,
|
||||
};
|
||||
});
|
||||
|
||||
const id = ROIName || ROINumber;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
points: newPoints,
|
||||
data,
|
||||
id,
|
||||
segmentIndex: ROINumber,
|
||||
color: colorArray,
|
||||
group: ROIGroup,
|
||||
geometryId: `${rtDisplaySetUID}:${id}:segmentIndex-${ROINumber}`,
|
||||
};
|
||||
});
|
||||
|
||||
const id = ROIName || ROINumber;
|
||||
|
||||
return {
|
||||
data,
|
||||
id,
|
||||
segmentIndex: ROINumber,
|
||||
color: colorArray,
|
||||
geometryId: `${rtDisplaySetUID}:${id}:segmentIndex-${ROINumber}`,
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import { updateLabelmapSegmentationImageReferences } from '@cornerstonejs/tools/
|
||||
import { triggerSegmentationRepresentationModified } from '@cornerstonejs/tools/segmentation/triggerSegmentationEvents';
|
||||
import { convertStackToVolumeLabelmap } from '@cornerstonejs/tools/segmentation/helpers/convertStackToVolumeLabelmap';
|
||||
import { getLabelmapImageIds } from '@cornerstonejs/tools/segmentation';
|
||||
import { VOLUME_LOADER_SCHEME } from '../../constants';
|
||||
|
||||
const LABELMAP = csToolsEnums.SegmentationRepresentations.Labelmap;
|
||||
const CONTOUR = csToolsEnums.SegmentationRepresentations.Contour;
|
||||
@@ -80,8 +81,6 @@ const EVENTS = {
|
||||
|
||||
const VALUE_TYPES = {};
|
||||
|
||||
const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
|
||||
|
||||
class SegmentationService extends PubSubService {
|
||||
static REGISTRATION = {
|
||||
name: 'segmentationService',
|
||||
@@ -615,7 +614,7 @@ class SegmentationService extends PubSubService {
|
||||
|
||||
// Process each segment similarly to the SEG function
|
||||
for (const rtStructData of allRTStructData) {
|
||||
const { data, id, color, segmentIndex, geometryId } = rtStructData;
|
||||
const { data, id, color, segmentIndex, geometryId, group } = rtStructData;
|
||||
|
||||
// Add the color to the colorLUT array
|
||||
colorLUT.push(color);
|
||||
@@ -646,6 +645,7 @@ class SegmentationService extends PubSubService {
|
||||
cachedStats: segmentsCachedStats,
|
||||
locked: false,
|
||||
active: false,
|
||||
group,
|
||||
};
|
||||
|
||||
// Broadcast segment loading progress
|
||||
|
||||
+21
@@ -10,6 +10,11 @@ export type ActionComponentInfo = {
|
||||
indexPriority?: number;
|
||||
};
|
||||
|
||||
export type AlignAndSide = {
|
||||
align: 'start' | 'end' | 'center';
|
||||
side: 'top' | 'bottom' | 'left' | 'right';
|
||||
};
|
||||
|
||||
class ViewportActionCornersService extends PubSubService {
|
||||
public static readonly EVENTS = {};
|
||||
public static readonly LOCATIONS = ViewportActionCornersLocations;
|
||||
@@ -66,6 +71,22 @@ class ViewportActionCornersService extends PubSubService {
|
||||
public clear(viewportId: string) {
|
||||
this.serviceImplementation._clear(viewportId);
|
||||
}
|
||||
|
||||
public getAlignAndSide(location: ViewportActionCornersLocations): AlignAndSide {
|
||||
switch (location) {
|
||||
case ViewportActionCornersLocations.topLeft:
|
||||
return { align: 'start', side: 'bottom' };
|
||||
case ViewportActionCornersLocations.topRight:
|
||||
return { align: 'end', side: 'bottom' };
|
||||
case ViewportActionCornersLocations.bottomLeft:
|
||||
return { align: 'start', side: 'top' };
|
||||
case ViewportActionCornersLocations.bottomRight:
|
||||
return { align: 'end', side: 'top' };
|
||||
default:
|
||||
console.debug('Unknown location, defaulting to bottom-start');
|
||||
return { align: 'start', side: 'bottom' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ViewportActionCornersService;
|
||||
@@ -32,12 +32,16 @@ import { useLutPresentationStore } from '../../stores/useLutPresentationStore';
|
||||
import { usePositionPresentationStore } from '../../stores/usePositionPresentationStore';
|
||||
import { useSynchronizersStore } from '../../stores/useSynchronizersStore';
|
||||
import { useSegmentationPresentationStore } from '../../stores/useSegmentationPresentationStore';
|
||||
import { VOLUME_LOADER_SCHEME } from '../../constants';
|
||||
|
||||
const EVENTS = {
|
||||
VIEWPORT_DATA_CHANGED: 'event::cornerstoneViewportService:viewportDataChanged',
|
||||
VIEWPORT_VOLUMES_CHANGED: 'event::cornerstoneViewportService:viewportVolumesChanged',
|
||||
};
|
||||
|
||||
const MIN_STACK_VIEWPORTS_TO_ENQUEUE_RESIZE = 12;
|
||||
const MIN_VOLUME_VIEWPORTS_TO_ENQUEUE_RESIZE = 6;
|
||||
|
||||
export const WITH_NAVIGATION = { withNavigation: true, withOrientation: false };
|
||||
|
||||
/**
|
||||
@@ -62,9 +66,6 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
beforeResizePositionPresentations: Map<string, PositionPresentation> = new Map();
|
||||
|
||||
// Some configs
|
||||
enableResizeDetector: true;
|
||||
resizeRefreshRateMs: 200;
|
||||
resizeRefreshMode: 'debounce';
|
||||
servicesManager: AppTypes.ServicesManager = null;
|
||||
|
||||
resizeQueue = [];
|
||||
@@ -125,13 +126,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
/**
|
||||
* It triggers the resize on the rendering engine, and renders the viewports
|
||||
*
|
||||
* @param isGridResize - if the resize is triggered by a grid resize
|
||||
* this is used to avoid double resize of the viewports since if the
|
||||
* grid is resized, all viewports will be resized so there is no need
|
||||
* to resize them individually which will get triggered by their
|
||||
* individual resize observers
|
||||
*/
|
||||
public resize(isGridResize = false) {
|
||||
public resize() {
|
||||
// https://stackoverflow.com/a/26279685
|
||||
// This resize() call, among other things, rerenders the viewports. But when the entire viewer is
|
||||
// display: none'd, it makes the size of all hidden elements 0, including the viewport canvas and its containers.
|
||||
@@ -147,12 +143,24 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
return;
|
||||
}
|
||||
|
||||
const numStackViewportsInViewportGrid = Array.from(this.viewportsById.values()).filter(
|
||||
viewportInfo => viewportInfo.getViewportType() === csEnums.ViewportType.STACK
|
||||
).length;
|
||||
|
||||
const numVolumeViewportsInViewportGrid = Array.from(this.viewportsById.values()).filter(
|
||||
viewportInfo => viewportInfo.getViewportType() === csEnums.ViewportType.ORTHOGRAPHIC
|
||||
).length;
|
||||
|
||||
const isEasyResize =
|
||||
numStackViewportsInViewportGrid <= MIN_STACK_VIEWPORTS_TO_ENQUEUE_RESIZE &&
|
||||
numVolumeViewportsInViewportGrid <= MIN_VOLUME_VIEWPORTS_TO_ENQUEUE_RESIZE;
|
||||
|
||||
// if there is a grid resize happening, it means the viewport grid
|
||||
// has been manipulated (e.g., panels closed, added, etc.) and we need
|
||||
// to resize all viewports, so we will add a timeout here to make sure
|
||||
// we don't double resize the viewports when viewports in the grid are
|
||||
// resized individually
|
||||
if (isGridResize) {
|
||||
if (isEasyResize) {
|
||||
this.performResize();
|
||||
this.resetGridResizeTimeout();
|
||||
this.resizeQueue = [];
|
||||
@@ -400,11 +408,6 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
// and we would lose the presentation.
|
||||
this.storePresentation({ viewportId: viewportInfo.getViewportId() });
|
||||
|
||||
// Todo: i don't like this here, move it
|
||||
this.servicesManager.services.segmentationService.clearSegmentationRepresentations(
|
||||
viewportInfo.getViewportId()
|
||||
);
|
||||
|
||||
if (!viewportInfo) {
|
||||
throw new Error('element is not enabled for the given viewportId');
|
||||
}
|
||||
@@ -506,6 +509,11 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
return this.viewportsById.get(viewportId);
|
||||
}
|
||||
|
||||
public getOrientation(viewportId: string): string {
|
||||
const viewportInfo = this.getViewportInfo(viewportId);
|
||||
return viewportInfo.getOrientation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks through the viewports to see if the specified measurement can be
|
||||
* displayed in one of the viewports. This function tries to get a "best fit"
|
||||
@@ -754,8 +762,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
const { imageIds, displaySetInstanceUID } = data;
|
||||
let volume = data.volume;
|
||||
|
||||
if (!volume) {
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
if (!volume && displaySet.images) {
|
||||
volume = csToolsUtils.getOrCreateImageVolume(displaySet.images.map(image => image.imageId));
|
||||
}
|
||||
|
||||
@@ -773,6 +781,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
volumeInputArray.push({
|
||||
imageIds,
|
||||
volumeId,
|
||||
modality: displaySet.Modality,
|
||||
displaySetInstanceUID,
|
||||
blendMode: displaySetOptions.blendMode,
|
||||
slabThickness: this._getSlabThickness(displaySetOptions, volumeId),
|
||||
});
|
||||
@@ -815,8 +825,15 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewport.id);
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetUIDs[0]);
|
||||
const displaySetModality = displaySet?.Modality;
|
||||
|
||||
// filter overlay display sets (e.g. segmentation) since they will get handled below via the segmentation service
|
||||
const filteredVolumeInputArray = volumeInputArray.filter(volumeInput => {
|
||||
const displaySet = displaySetService.getDisplaySetByUID(volumeInput.displaySetInstanceUID);
|
||||
return !displaySet?.isOverlayDisplaySet;
|
||||
});
|
||||
|
||||
// Todo: use presentations states
|
||||
const volumesProperties = volumeInputArray.map((volumeInput, index) => {
|
||||
const volumesProperties = filteredVolumeInputArray.map((volumeInput, index) => {
|
||||
const { volumeId } = volumeInput;
|
||||
const displaySetOption = displaySetOptions[index];
|
||||
const { voi, voiInverted, colormap, displayPreset } = displaySetOption;
|
||||
@@ -846,9 +863,29 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
});
|
||||
|
||||
// For SEG and RT viewports
|
||||
const { addOverlayFn } = this._processExtraDisplaySetsForViewport(viewport) || {};
|
||||
const { addOverlayFn, imageIds } = this._processExtraDisplaySetsForViewport(viewport) || {};
|
||||
|
||||
await viewport.setVolumes(volumeInputArray);
|
||||
if (!filteredVolumeInputArray.length && addOverlayFn) {
|
||||
// if there is no volume input array, and there is an addOverlayFn, means we need to take
|
||||
// care of the background overlay display set first then the addOverlayFn will add the
|
||||
// SEG displaySet
|
||||
const sampleImageId = imageIds[0];
|
||||
const backgroundDisplaySet = displaySetService.getDisplaySetsBy(
|
||||
displaySet =>
|
||||
!displaySet.isOverlayDisplaySet &&
|
||||
displaySet.images.some(image => image.imageId === sampleImageId)
|
||||
);
|
||||
|
||||
if (backgroundDisplaySet.length !== 1) {
|
||||
throw new Error('Background display set not found');
|
||||
}
|
||||
|
||||
await viewport.setVolumes([
|
||||
{ volumeId: `${VOLUME_LOADER_SCHEME}:${backgroundDisplaySet[0].displaySetInstanceUID}` },
|
||||
]);
|
||||
} else {
|
||||
await viewport.setVolumes(filteredVolumeInputArray);
|
||||
}
|
||||
|
||||
if (addOverlayFn) {
|
||||
addOverlayFn();
|
||||
@@ -889,31 +926,30 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
// load any secondary displaySets
|
||||
const displaySetInstanceUIDs = this.viewportsDisplaySets.get(viewport.id);
|
||||
|
||||
// Can be SEG or RTSTRUCT for now but not PMAP
|
||||
const segOrRTSOverlayDisplaySet = displaySetInstanceUIDs
|
||||
// Find overlay display sets (e.g. SEG, RTSTRUCT)
|
||||
const overlayDisplaySet = displaySetInstanceUIDs
|
||||
.map(displaySetService.getDisplaySetByUID)
|
||||
.find(
|
||||
displaySet =>
|
||||
displaySet?.isOverlayDisplaySet && ['SEG', 'RTSTRUCT'].includes(displaySet.Modality)
|
||||
);
|
||||
.find(displaySet => displaySet?.isOverlayDisplaySet);
|
||||
|
||||
// if it is only the overlay displaySet, then we need to get the reference
|
||||
// displaySet imageIds and set them as the imageIds for the viewport,
|
||||
// here we can do some logic if the reference is missing
|
||||
// then find the most similar match of displaySet instead
|
||||
if (!segOrRTSOverlayDisplaySet) {
|
||||
if (!overlayDisplaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
const referenceDisplaySet = displaySetService.getDisplaySetByUID(
|
||||
segOrRTSOverlayDisplaySet.referencedDisplaySetInstanceUID
|
||||
);
|
||||
const imageIds = referenceDisplaySet.images.map(image => image.imageId);
|
||||
let imageIds;
|
||||
if (overlayDisplaySet.referencedDisplaySetInstanceUID) {
|
||||
const referenceDisplaySet = displaySetService.getDisplaySetByUID(
|
||||
overlayDisplaySet.referencedDisplaySetInstanceUID
|
||||
);
|
||||
imageIds = referenceDisplaySet.images.map(image => image.imageId);
|
||||
}
|
||||
|
||||
return {
|
||||
imageIds,
|
||||
addOverlayFn: () =>
|
||||
this.addOverlayRepresentationForDisplaySet(segOrRTSOverlayDisplaySet, viewport),
|
||||
addOverlayFn: () => this.addOverlayRepresentationForDisplaySet(overlayDisplaySet, viewport),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,8 @@ export interface IViewportService {
|
||||
/**
|
||||
* It creates a resize observer for the viewport element, and observes
|
||||
* the element for resizing events
|
||||
* @param {*} elementRef
|
||||
*/
|
||||
resize(isGridResize: boolean): void;
|
||||
resize(): void;
|
||||
/**
|
||||
* Removes the viewport from cornerstone, and destroys the rendering engine
|
||||
*/
|
||||
|
||||
@@ -316,6 +316,10 @@ class ViewportInfo {
|
||||
return this.viewportOptions.orientation;
|
||||
}
|
||||
|
||||
public setOrientation(orientation: Enums.OrientationAxis): void {
|
||||
this.viewportOptions.orientation = orientation;
|
||||
}
|
||||
|
||||
public getDisplayArea(): Types.DisplayArea {
|
||||
return this.viewportOptions.displayArea;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { CommandsManager } from '@ohif/core';
|
||||
|
||||
export type ViewportPreset = {
|
||||
name: string;
|
||||
gradientOpacity: string;
|
||||
@@ -15,8 +13,6 @@ export type ViewportPreset = {
|
||||
|
||||
export type VolumeRenderingPresetsProps = {
|
||||
viewportId: string;
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
commandsManager: CommandsManager;
|
||||
volumeRenderingPresets: ViewportPreset[];
|
||||
};
|
||||
|
||||
@@ -24,13 +20,10 @@ export type VolumeRenderingPresetsContentProps = {
|
||||
presets: ViewportPreset[];
|
||||
onClose: () => void;
|
||||
viewportId: string;
|
||||
commandsManager: CommandsManager;
|
||||
};
|
||||
|
||||
export type VolumeRenderingOptionsProps = {
|
||||
viewportId: string;
|
||||
commandsManager: CommandsManager;
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
volumeRenderingQualityRange: VolumeRenderingQualityRange;
|
||||
};
|
||||
|
||||
@@ -42,27 +35,19 @@ export type VolumeRenderingQualityRange = {
|
||||
|
||||
export type VolumeRenderingQualityProps = {
|
||||
viewportId: string;
|
||||
commandsManager: CommandsManager;
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
volumeRenderingQualityRange: VolumeRenderingQualityRange;
|
||||
};
|
||||
|
||||
export type VolumeShiftProps = {
|
||||
viewportId: string;
|
||||
commandsManager: CommandsManager;
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
};
|
||||
|
||||
export type VolumeShadeProps = {
|
||||
viewportId: string;
|
||||
commandsManager: CommandsManager;
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
onClickShade?: (bool: boolean) => void;
|
||||
};
|
||||
|
||||
export type VolumeLightingProps = {
|
||||
viewportId: string;
|
||||
commandsManager: CommandsManager;
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
hasShade: boolean;
|
||||
};
|
||||
@@ -1,5 +1,10 @@
|
||||
import * as CornerstoneCacheService from './CornerstoneCacheService';
|
||||
import type CornerstoneServices from './CornerstoneServices';
|
||||
export { type SyncGroup } from '../services/SyncGroupService';
|
||||
export {
|
||||
type ViewportActionCornerLocation,
|
||||
type ViewportActionCornerService,
|
||||
type CustomizationService,
|
||||
} from '../hooks/useViewportActionCorners';
|
||||
|
||||
export type { CornerstoneCacheService, CornerstoneServices };
|
||||
@@ -180,15 +180,12 @@ const CornerstoneViewportDownloadForm = ({
|
||||
const toolGroup = ToolGroupManager.getToolGroupForViewport(activeViewportId, renderingEngineId);
|
||||
toolGroup.addViewport(downloadViewportId, renderingEngineId);
|
||||
|
||||
Object.keys(toolGroup.getToolInstances()).forEach(toolName => {
|
||||
if (show && toolName !== 'Crosshairs') {
|
||||
try {
|
||||
toolGroup.setToolEnabled(toolName);
|
||||
} catch (error) {
|
||||
console.debug('Error enabling tool:', error);
|
||||
}
|
||||
} else {
|
||||
toolGroup.setToolDisabled(toolName);
|
||||
const toolInstances = toolGroup.getToolInstances();
|
||||
const toolInstancesArray = Object.values(toolInstances);
|
||||
|
||||
toolInstancesArray.forEach(toolInstance => {
|
||||
if (toolInstance.constructor.isAnnotation !== false) {
|
||||
toolGroup.setToolEnabled(toolInstance.toolName);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -13,6 +13,15 @@ const colormaps = [
|
||||
RGBPoints: [0, 1, 1, 1, 1, 0, 0, 0],
|
||||
description: 'X Ray',
|
||||
},
|
||||
{
|
||||
ColorSpace: 'RGB',
|
||||
Name: 'Isodose',
|
||||
NanColor: [1, 0, 0],
|
||||
RGBPoints: [
|
||||
0, 0, 1, 0, 0.1, 0.5, 1, 0, 0.2, 1, 1, 0, 0.3, 1, 0.66, 0, 0.4, 1, 0.33, 0, 0.5, 1, 0, 0,
|
||||
],
|
||||
description: 'Isodose',
|
||||
},
|
||||
{
|
||||
ColorSpace: 'RGB',
|
||||
Name: 'hsv',
|
||||
|
||||
@@ -24,9 +24,13 @@ export function getViewportPresentations(
|
||||
|
||||
const { lutPresentationId, positionPresentationId, segmentationPresentationId } = presentationIds;
|
||||
|
||||
const positionPresentation = positionPresentationStore[positionPresentationId];
|
||||
const lutPresentation = lutPresentationStore[lutPresentationId];
|
||||
const segmentationPresentation = segmentationPresentationStore[segmentationPresentationId];
|
||||
|
||||
return {
|
||||
positionPresentation: positionPresentationStore[positionPresentationId],
|
||||
lutPresentation: lutPresentationStore[lutPresentationId],
|
||||
segmentationPresentation: segmentationPresentationStore[segmentationPresentationId],
|
||||
positionPresentation,
|
||||
lutPresentation,
|
||||
segmentationPresentation,
|
||||
};
|
||||
}
|
||||
@@ -3,6 +3,103 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improve menu modals customizations ([#5011](https://github.com/OHIF/Viewers/issues/5011)) ([7dbe5e3](https://github.com/OHIF/Viewers/commit/7dbe5e37b06056fdd5262a131417013d112c3d30))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **referencelines:** and bring back the notification for worker updates but disallow duplication ([#5005](https://github.com/OHIF/Viewers/issues/5005)) ([98f8187](https://github.com/OHIF/Viewers/commit/98f8187dfeb8aec6eca145da75c855aec0da4e1d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-default",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "Common/default features and functionality for basic image viewing",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
@@ -32,8 +32,8 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/i18n": "3.11.0-beta.15",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/i18n": "3.11.0-beta.26",
|
||||
"dcmjs": "*",
|
||||
"dicomweb-client": "^0.10.4",
|
||||
"prop-types": "^15.6.2",
|
||||
|
||||
@@ -7,17 +7,14 @@ import {
|
||||
Icons,
|
||||
Button,
|
||||
} from '@ohif/ui-next';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
/**
|
||||
* The default sub-menu appearance and setup is defined here, but this can be
|
||||
* replaced by
|
||||
*/
|
||||
const getMenuItemsDefault = ({
|
||||
commandsManager,
|
||||
items,
|
||||
servicesManager,
|
||||
...props
|
||||
}: withAppTypes) => {
|
||||
const getMenuItemsDefault = ({ commandsManager, items, ...props }: withAppTypes) => {
|
||||
const { servicesManager } = useSystem();
|
||||
const { customizationService } = servicesManager.services;
|
||||
|
||||
// This allows replacing the default child item for menus, whereas the entire
|
||||
@@ -36,7 +33,7 @@ const getMenuItemsDefault = ({
|
||||
onClick: ({ commandsManager, ...props }: withAppTypes) => () => void;
|
||||
};
|
||||
}) => (
|
||||
<DropdownMenuItem onClick={() => item.onClick({ commandsManager, ...props })}>
|
||||
<DropdownMenuItem onClick={() => item.onClick({ commandsManager, servicesManager, ...props })}>
|
||||
<div className="flex items-center gap-2">
|
||||
{item.iconName && <Icons.ByName name={item.iconName} />}
|
||||
<span>{item.label}</span>
|
||||
@@ -76,12 +73,8 @@ const getMenuItemsDefault = ({
|
||||
* @returns Component bound to the bindProps
|
||||
*/
|
||||
export default function MoreDropdownMenu(bindProps) {
|
||||
const {
|
||||
menuItemsKey,
|
||||
getMenuItems = getMenuItemsDefault,
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
} = bindProps;
|
||||
const { menuItemsKey, getMenuItems = getMenuItemsDefault, commandsManager } = bindProps;
|
||||
const { servicesManager } = useSystem();
|
||||
const { customizationService } = servicesManager.services;
|
||||
|
||||
const items = customizationService.getCustomization(menuItemsKey);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Toolbar } from '../Toolbar/Toolbar';
|
||||
import HeaderPatientInfo from './HeaderPatientInfo';
|
||||
import { PatientInfoVisibility } from './HeaderPatientInfo/HeaderPatientInfo';
|
||||
import { preserveQueryParameters } from '@ohif/app';
|
||||
import { Types } from '@ohif/core';
|
||||
|
||||
function ViewerHeader({ appConfig }: withAppTypes<{ appConfig: AppTypes.Config }>) {
|
||||
const { servicesManager, extensionManager, commandsManager } = useSystem();
|
||||
@@ -38,28 +39,34 @@ function ViewerHeader({ appConfig }: withAppTypes<{ appConfig: AppTypes.Config }
|
||||
const { t } = useTranslation();
|
||||
const { show } = useModal();
|
||||
|
||||
const AboutModal = customizationService.getCustomization('ohif.aboutModal');
|
||||
const UserPreferencesModal = customizationService.getCustomization('ohif.userPreferencesModal');
|
||||
const AboutModal = customizationService.getCustomization(
|
||||
'ohif.aboutModal'
|
||||
) as Types.MenuComponentCustomization;
|
||||
|
||||
const UserPreferencesModal = customizationService.getCustomization(
|
||||
'ohif.userPreferencesModal'
|
||||
) as Types.MenuComponentCustomization;
|
||||
|
||||
const menuOptions = [
|
||||
{
|
||||
title: t('Header:About'),
|
||||
title: AboutModal?.menuTitle ?? t('Header:About'),
|
||||
icon: 'info',
|
||||
onClick: () =>
|
||||
show({
|
||||
content: AboutModal,
|
||||
title: t('AboutModal:About OHIF Viewer'),
|
||||
containerClassName: 'max-w-md',
|
||||
title: AboutModal?.title ?? t('AboutModal:About OHIF Viewer'),
|
||||
containerClassName: AboutModal?.containerClassName ?? 'max-w-md',
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: t('Header:Preferences'),
|
||||
title: UserPreferencesModal.menuTitle ?? t('Header:Preferences'),
|
||||
icon: 'settings',
|
||||
onClick: () =>
|
||||
show({
|
||||
content: UserPreferencesModal,
|
||||
title: t('UserPreferencesModal:User preferences'),
|
||||
containerClassName: 'flex max-w-4xl p-6 flex-col',
|
||||
title: UserPreferencesModal.title ?? t('UserPreferencesModal:User preferences'),
|
||||
containerClassName:
|
||||
UserPreferencesModal?.containerClassName ?? 'flex max-w-4xl p-6 flex-col',
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -4,7 +4,7 @@ const collapsedOutsideBorderSize = 4;
|
||||
const collapsedWidth = 25;
|
||||
|
||||
const rightPanelInitialExpandedWidth = 280;
|
||||
const leftPanelInitialExpandedWidth = 292;
|
||||
const leftPanelInitialExpandedWidth = 282;
|
||||
|
||||
const panelGroupDefinition = {
|
||||
groupId: 'viewerLayoutResizablePanelGroup',
|
||||
|
||||
@@ -3,6 +3,12 @@ import { Types, DicomMetadataStore } from '@ohif/core';
|
||||
import { ContextMenuController } from './CustomizableContextMenu';
|
||||
import DicomTagBrowser from './DicomTagBrowser/DicomTagBrowser';
|
||||
import reuseCachedLayouts from './utils/reuseCachedLayouts';
|
||||
import {
|
||||
configureViewportForLayerAddition,
|
||||
configureViewportForLayerRemoval,
|
||||
canAddDisplaySetToViewport,
|
||||
DERIVED_OVERLAY_MODALITIES,
|
||||
} from './utils/layerConfigurationUtils';
|
||||
import findViewportsByPosition, {
|
||||
findOrCreateViewport as layoutFindOrCreate,
|
||||
} from './findViewportsByPosition';
|
||||
@@ -51,6 +57,137 @@ const commandsModule = ({
|
||||
const contextMenuController = new ContextMenuController(servicesManager, commandsManager);
|
||||
|
||||
const actions = {
|
||||
/**
|
||||
* Adds a display set as a layer to the specified viewport
|
||||
*
|
||||
* @param options.viewportId - The ID of the viewport to add the layer to
|
||||
* @param options.displaySetInstanceUID - The UID of the display set to add as a layer
|
||||
* @param options.removeFirst - Optional flag to remove the display set first if it's already added
|
||||
*/
|
||||
addDisplaySetAsLayer: ({ viewportId, displaySetInstanceUID, removeFirst = false }) => {
|
||||
if (!viewportId || !displaySetInstanceUID) {
|
||||
console.warn('Missing required parameters for addDisplaySetAsLayer command');
|
||||
return;
|
||||
}
|
||||
|
||||
const { displaySetService, viewportGridService, hangingProtocolService } =
|
||||
servicesManager.services;
|
||||
|
||||
// Get the display set
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
if (!displaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get current display sets for the viewport
|
||||
const currentDisplaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
|
||||
|
||||
// Check if we can add this display set to the viewport
|
||||
const canAdd = canAddDisplaySetToViewport({
|
||||
viewportId,
|
||||
displaySetInstanceUID,
|
||||
servicesManager,
|
||||
});
|
||||
|
||||
if (!canAdd) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the display set to the viewport
|
||||
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||
viewportId,
|
||||
displaySetInstanceUID
|
||||
);
|
||||
|
||||
// Configure each viewport for layer addition
|
||||
updatedViewports.forEach(viewport => {
|
||||
configureViewportForLayerAddition({
|
||||
viewport,
|
||||
displaySetInstanceUID,
|
||||
currentDisplaySetUIDs,
|
||||
servicesManager,
|
||||
});
|
||||
});
|
||||
|
||||
// Update position presentation
|
||||
commandsManager.runCommand('updateStoredPositionPresentation', {
|
||||
viewportId,
|
||||
displaySetInstanceUIDs: updatedViewports[0].displaySetInstanceUIDs,
|
||||
});
|
||||
|
||||
// Run command to update viewports
|
||||
commandsManager.run('setDisplaySetsForViewports', {
|
||||
viewportsToUpdate: updatedViewports,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a display set layer from the specified viewport
|
||||
*
|
||||
* @param options.viewportId - The ID of the viewport to remove the layer from
|
||||
* @param options.displaySetInstanceUID - The UID of the display set to remove
|
||||
*/
|
||||
removeDisplaySetLayer: ({ viewportId, displaySetInstanceUID }) => {
|
||||
if (!viewportId || !displaySetInstanceUID) {
|
||||
console.warn('Missing required parameters for removeDisplaySetLayer command');
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
displaySetService,
|
||||
viewportGridService,
|
||||
hangingProtocolService,
|
||||
segmentationService,
|
||||
} = servicesManager.services;
|
||||
|
||||
// Get the display set
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
if (!displaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get current display sets for the viewport
|
||||
const currentDisplaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
|
||||
|
||||
// If the display set is not in the viewport, no need to remove it
|
||||
if (!currentDisplaySetUIDs.includes(displaySetInstanceUID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's a segmentation and handle accordingly
|
||||
const isSegmentation = DERIVED_OVERLAY_MODALITIES.includes(displaySet.Modality);
|
||||
if (isSegmentation) {
|
||||
segmentationService.removeSegmentationRepresentations(viewportId, {
|
||||
segmentationId: displaySetInstanceUID,
|
||||
});
|
||||
}
|
||||
|
||||
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||
viewportId,
|
||||
displaySetInstanceUID
|
||||
);
|
||||
|
||||
// Configure each viewport for layer removal
|
||||
updatedViewports.forEach(viewport => {
|
||||
configureViewportForLayerRemoval({
|
||||
viewport,
|
||||
displaySetInstanceUID,
|
||||
currentDisplaySetUIDs,
|
||||
servicesManager,
|
||||
});
|
||||
});
|
||||
|
||||
// Update position presentation
|
||||
commandsManager.runCommand('updateStoredPositionPresentation', {
|
||||
viewportId,
|
||||
displaySetInstanceUIDs: updatedViewports[0].displaySetInstanceUIDs,
|
||||
});
|
||||
|
||||
// Update the viewports
|
||||
commandsManager.run('setDisplaySetsForViewports', {
|
||||
viewportsToUpdate: updatedViewports,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Runs a command in multi-monitor mode. No-op if not multi-monitor.
|
||||
*/
|
||||
@@ -644,6 +781,8 @@ const commandsModule = ({
|
||||
openDICOMTagViewer: actions.openDICOMTagViewer,
|
||||
updateViewportDisplaySet: actions.updateViewportDisplaySet,
|
||||
scrollActiveThumbnailIntoView: actions.scrollActiveThumbnailIntoView,
|
||||
addDisplaySetAsLayer: actions.addDisplaySetAsLayer,
|
||||
removeDisplaySetLayer: actions.removeDisplaySetLayer,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -14,6 +14,26 @@ export default {
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'addAsLayer',
|
||||
label: 'Add as Layer',
|
||||
iconName: 'ViewportViews',
|
||||
onClick: ({ commandsManager, displaySetInstanceUID, servicesManager }: withAppTypes) => {
|
||||
const { viewportGridService } = servicesManager.services;
|
||||
|
||||
// Get the active viewport
|
||||
const { activeViewportId } = viewportGridService.getState();
|
||||
if (!activeViewportId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the new command to add the display set as a layer
|
||||
commandsManager.runCommand('addDisplaySetAsLayer', {
|
||||
viewportId: activeViewportId,
|
||||
displaySetInstanceUID,
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
'studyBrowser.sortFunctions': [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { utils, classes } from '@ohif/core';
|
||||
import i18n from '@ohif/i18n';
|
||||
import { id } from './id';
|
||||
import getDisplaySetMessages from './getDisplaySetMessages';
|
||||
import getDisplaySetsFromUnsupportedSeries from './getDisplaySetsFromUnsupportedSeries';
|
||||
@@ -99,6 +100,11 @@ const makeDisplaySet = instances => {
|
||||
averageSpacingBetweenFrames: averageSpacingBetweenFrames || null,
|
||||
isDynamicVolume,
|
||||
dynamicVolumeInfo,
|
||||
supportsWindowLevel: true,
|
||||
label:
|
||||
instance.SeriesDescription ||
|
||||
`${i18n.t('Series')} ${instance.SeriesNumber} - ${i18n.t(instance.Modality)}`,
|
||||
FrameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||
});
|
||||
|
||||
// Sort the images in this series if needed
|
||||
@@ -260,6 +266,7 @@ const sopClassUids = [
|
||||
sopClassDictionary.LegacyConvertedEnhancedPETImageStorage,
|
||||
sopClassDictionary.RTImageStorage,
|
||||
sopClassDictionary.EnhancedUSVolumeStorage,
|
||||
sopClassDictionary.RTDoseStorage,
|
||||
];
|
||||
|
||||
function getSopClassHandlerModule(appContextParam) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ToolbarButton as ToolbarButtonLegacy } from '@ohif/ui';
|
||||
import { ToolButton, utils } from '@ohif/ui-next';
|
||||
|
||||
import ToolbarLayoutSelectorWithServices from './Toolbar/ToolbarLayoutSelector';
|
||||
@@ -33,28 +32,6 @@ export default function getToolbarModule({ commandsManager, servicesManager }: w
|
||||
name: 'ohif.toolBoxButton',
|
||||
defaultComponent: ToolBoxButtonWrapper,
|
||||
},
|
||||
// legacy will get removed in the future
|
||||
// legacy will get removed in the future
|
||||
// legacy will get removed in the future
|
||||
// legacy will get removed in the future
|
||||
// legacy will get removed in the future
|
||||
// legacy will get removed in the future
|
||||
{
|
||||
name: 'ohif.radioGroup',
|
||||
defaultComponent: ToolbarButtonLegacy,
|
||||
},
|
||||
{
|
||||
name: 'ohif.buttonGroup',
|
||||
defaultComponent: ToolbarButtonGroupWithServicesLegacy,
|
||||
},
|
||||
{
|
||||
name: 'ohif.divider',
|
||||
defaultComponent: ToolbarDividerLegacy,
|
||||
},
|
||||
{
|
||||
name: 'ohif.splitButton',
|
||||
defaultComponent: ToolbarSplitButtonWithServicesLegacy,
|
||||
},
|
||||
// others
|
||||
{
|
||||
name: 'ohif.layoutSelector',
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { CommandsManager, ExtensionManager } from '@ohif/core';
|
||||
import LineChartViewport from './Components/LineChartViewport/index';
|
||||
|
||||
const getViewportModule = ({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
extensionManager,
|
||||
}: {
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
commandsManager: CommandsManager;
|
||||
extensionManager: ExtensionManager;
|
||||
}) => {
|
||||
const getViewportModule = () => {
|
||||
return [
|
||||
{
|
||||
name: 'chartViewport',
|
||||
|
||||
@@ -22,11 +22,11 @@ export default function init({
|
||||
cineService.EVENTS.CINE_STATE_CHANGED,
|
||||
]);
|
||||
// Add
|
||||
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.INSTANCES_ADDED, handlePETImageMetadata);
|
||||
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.INSTANCES_ADDED, handleScalingModules);
|
||||
|
||||
// If the metadata for PET has changed by the user (e.g. manually changing the PatientWeight)
|
||||
// we need to recalculate the SUV Scaling Factors
|
||||
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.SERIES_UPDATED, handlePETImageMetadata);
|
||||
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.SERIES_UPDATED, handleScalingModules);
|
||||
|
||||
// Adds extra custom attributes for use by hanging protocols
|
||||
registerHangingProtocolAttributes({ servicesManager });
|
||||
@@ -71,7 +71,7 @@ export default function init({
|
||||
});
|
||||
}
|
||||
|
||||
const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
|
||||
const handleScalingModules = ({ SeriesInstanceUID, StudyInstanceUID }) => {
|
||||
const { instances } = DicomMetadataStore.getSeries(StudyInstanceUID, SeriesInstanceUID);
|
||||
|
||||
if (!instances?.length) {
|
||||
@@ -80,12 +80,41 @@ const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
|
||||
|
||||
const modality = instances[0].Modality;
|
||||
|
||||
if (!modality || modality !== 'PT') {
|
||||
const allowedModality = ['PT', 'RTDOSE'];
|
||||
|
||||
if (!allowedModality.includes(modality)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const imageIds = instances.map(instance => instance.imageId);
|
||||
const instanceMetadataArray = [];
|
||||
|
||||
if (modality === 'RTDOSE') {
|
||||
const DoseGridScaling = instances[0].DoseGridScaling;
|
||||
const DoseSummation = instances[0].DoseSummation;
|
||||
const DoseType = instances[0].DoseType;
|
||||
const DoseUnit = instances[0].DoseUnit;
|
||||
const NumberOfFrames = instances[0].NumberOfFrames;
|
||||
const imageId = imageIds[0];
|
||||
|
||||
// add scaling module to the metadata
|
||||
// since RTDOSE is always a multiframe we should add the scaling module to each frame
|
||||
for (let i = 0; i < NumberOfFrames; i++) {
|
||||
const frameIndex = i + 1;
|
||||
|
||||
// Todo: we should support other things like wadouri, local etc
|
||||
const newImageId = `${imageId.replace(/\/frames\/\d+$/, '')}/frames/${frameIndex}`;
|
||||
metadataProvider.addCustomMetadata(newImageId, 'scalingModule', {
|
||||
DoseGridScaling,
|
||||
DoseSummation,
|
||||
DoseType,
|
||||
DoseUnit,
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// try except block to prevent errors when the metadata is not correct
|
||||
try {
|
||||
imageIds.forEach(imageId => {
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
export const DERIVED_OVERLAY_MODALITIES = ['SEG', 'RTSTRUCT'];
|
||||
export const DEFAULT_COLORMAP = 'hsv';
|
||||
export const DEFAULT_OPACITY = 0.9;
|
||||
export const DEFAULT_OPACITY_PERCENT = DEFAULT_OPACITY * 100;
|
||||
|
||||
/**
|
||||
* Get modality-specific color and opacity settings from the customization service
|
||||
*/
|
||||
export function getModalityOverlayColormap(customizationService, modality) {
|
||||
const modalityOverlayDefaultColorMaps = customizationService?.getCustomization(
|
||||
'cornerstone.modalityOverlayDefaultColorMaps'
|
||||
) || { defaultSettings: {} };
|
||||
|
||||
return (
|
||||
modalityOverlayDefaultColorMaps.defaultSettings[modality] || {
|
||||
colormap: DEFAULT_COLORMAP,
|
||||
opacity: DEFAULT_OPACITY,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create display set options based on modality and opacity settings
|
||||
*/
|
||||
export function createColormapOverlayDisplaySetOptions(displaySet, opacity, customizationService) {
|
||||
if (displaySet.Modality === 'SEG') {
|
||||
return {};
|
||||
}
|
||||
|
||||
const modalitySettings = getModalityOverlayColormap(customizationService, displaySet.Modality);
|
||||
return {
|
||||
colormap: {
|
||||
name: modalitySettings.colormap || DEFAULT_COLORMAP,
|
||||
opacity: opacity / 100, // Convert from percentage to 0-1 range
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure viewport for adding a display set layer
|
||||
*/
|
||||
export function configureViewportForLayerAddition(params: {
|
||||
viewport: any;
|
||||
displaySetInstanceUID: string;
|
||||
currentDisplaySetUIDs: string[];
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
}): any {
|
||||
const { viewport, displaySetInstanceUID, currentDisplaySetUIDs, servicesManager } = params;
|
||||
const { cornerstoneViewportService, displaySetService, customizationService } =
|
||||
servicesManager.services;
|
||||
|
||||
const { viewportId } = viewport;
|
||||
|
||||
// Set the display set UIDs for the viewport
|
||||
const allDisplaySetInstanceUIDs = [...currentDisplaySetUIDs, displaySetInstanceUID];
|
||||
viewport.displaySetInstanceUIDs = allDisplaySetInstanceUIDs;
|
||||
|
||||
if (!viewport.viewportOptions) {
|
||||
viewport.viewportOptions = {};
|
||||
}
|
||||
|
||||
const requestedLayerDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
|
||||
if (!viewport.viewportOptions.orientation) {
|
||||
viewport.viewportOptions.orientation = cornerstoneViewportService.getOrientation(viewportId);
|
||||
}
|
||||
|
||||
// Do not force volume for SEG and RTSTRUCT if there is only one display set
|
||||
if (requestedLayerDisplaySet.isOverlayDisplaySet && currentDisplaySetUIDs.length === 1) {
|
||||
viewport.viewportOptions.viewportType = 'stack';
|
||||
} else {
|
||||
viewport.viewportOptions.viewportType = 'volume';
|
||||
}
|
||||
|
||||
// create same amount of display set options as the number of display set UIDs
|
||||
const displaySetOptions = allDisplaySetInstanceUIDs.map((uid, index) => {
|
||||
if (index === 0) {
|
||||
// no colormap for background
|
||||
return {};
|
||||
}
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetByUID(uid);
|
||||
return createColormapOverlayDisplaySetOptions(displaySet, 90, customizationService);
|
||||
});
|
||||
|
||||
viewport.displaySetOptions = displaySetOptions;
|
||||
|
||||
return viewport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure viewport for removing a display set layer
|
||||
*/
|
||||
export function configureViewportForLayerRemoval(params: {
|
||||
viewport: any;
|
||||
displaySetInstanceUID: string;
|
||||
currentDisplaySetUIDs: string[];
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
}): any {
|
||||
const { viewport, displaySetInstanceUID, currentDisplaySetUIDs, servicesManager } = params;
|
||||
const { cornerstoneViewportService, displaySetService } = servicesManager.services;
|
||||
|
||||
const { viewportId } = viewport;
|
||||
|
||||
// Filter out the display set to remove
|
||||
viewport.displaySetInstanceUIDs = currentDisplaySetUIDs.filter(
|
||||
uid => uid !== displaySetInstanceUID
|
||||
);
|
||||
|
||||
if (!viewport.viewportOptions) {
|
||||
viewport.viewportOptions = {};
|
||||
}
|
||||
|
||||
viewport.viewportOptions.viewportType = 'volume';
|
||||
|
||||
// orientation
|
||||
if (!viewport.viewportOptions.orientation) {
|
||||
viewport.viewportOptions.orientation = cornerstoneViewportService.getOrientation(viewportId);
|
||||
}
|
||||
|
||||
// Recreate the display set options
|
||||
viewport.displaySetOptions = viewport.displaySetInstanceUIDs.map(() => {
|
||||
// For simplicity, we're returning empty options for now
|
||||
// In a more complete implementation, we would need to preserve existing display set options
|
||||
return {};
|
||||
});
|
||||
|
||||
return viewport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a display set can be added as a layer to the specified viewport
|
||||
*/
|
||||
export function canAddDisplaySetToViewport(params: {
|
||||
viewportId: string;
|
||||
displaySetInstanceUID: string;
|
||||
servicesManager: AppTypes.ServicesManager;
|
||||
}): boolean {
|
||||
const { viewportId, displaySetInstanceUID, servicesManager } = params;
|
||||
const { displaySetService, viewportGridService } = servicesManager.services;
|
||||
|
||||
// Check if the display set exists
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
if (!displaySet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get current display sets in the viewport
|
||||
const currentDisplaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
|
||||
|
||||
// Check if the display set is already in the viewport
|
||||
if (currentDisplaySetUIDs.includes(displaySetInstanceUID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -3,6 +3,94 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-microscopy",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "OHIF extension for DICOM microscopy",
|
||||
"author": "Bill Wallace, md-prog",
|
||||
"license": "MIT",
|
||||
@@ -30,10 +30,10 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/extension-default": "3.11.0-beta.15",
|
||||
"@ohif/i18n": "3.11.0-beta.15",
|
||||
"@ohif/ui": "3.11.0-beta.15",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/extension-default": "3.11.0-beta.26",
|
||||
"@ohif/i18n": "3.11.0-beta.26",
|
||||
"@ohif/ui": "3.11.0-beta.26",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
|
||||
@@ -3,6 +3,97 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-pdf",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "OHIF extension for PDF display",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -30,8 +30,8 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/ui": "3.11.0-beta.15",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/ui": "3.11.0-beta.26",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { SOPClassHandlerId } from './id';
|
||||
import { utils, classes } from '@ohif/core';
|
||||
|
||||
const { ImageSet } = classes;
|
||||
import { utils, Types as OhifTypes } from '@ohif/core';
|
||||
import i18n from '@ohif/i18n';
|
||||
|
||||
const SOP_CLASS_UIDS = {
|
||||
ENCAPSULATED_PDF: '1.2.840.10008.5.1.4.1.1.104.1',
|
||||
@@ -50,12 +49,15 @@ const _getDisplaySetsFromSeries = (instances, servicesManager, extensionManager)
|
||||
numImageFrames: 0,
|
||||
numInstances: 1,
|
||||
instance,
|
||||
supportsWindowLevel: true,
|
||||
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t(Modality)}`,
|
||||
};
|
||||
return displaySet;
|
||||
});
|
||||
};
|
||||
|
||||
export default function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
||||
export default function getSopClassHandlerModule(params) {
|
||||
const { servicesManager, extensionManager } = params;
|
||||
const getDisplaySetsFromSeries = instances => {
|
||||
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,97 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-video
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-video",
|
||||
"version": "3.11.0-beta.15",
|
||||
"version": "3.11.0-beta.26",
|
||||
"description": "OHIF extension for video display",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -30,8 +30,8 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.11.0-beta.15",
|
||||
"@ohif/ui": "3.11.0-beta.15",
|
||||
"@ohif/core": "3.11.0-beta.26",
|
||||
"@ohif/ui": "3.11.0-beta.26",
|
||||
"dcmjs": "*",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"hammerjs": "^2.0.8",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SOPClassHandlerId } from './id';
|
||||
import { utils } from '@ohif/core';
|
||||
import i18n from '@ohif/i18n';
|
||||
import { utilities as csUtils, Enums as csEnums } from '@cornerstonejs/core';
|
||||
|
||||
const SOP_CLASS_UIDS = {
|
||||
@@ -90,6 +91,8 @@ const _getDisplaySetsFromSeries = (instances, servicesManager, extensionManager)
|
||||
sopClassUids,
|
||||
numImageFrames: NumberOfFrames,
|
||||
instance,
|
||||
supportsWindowLevel: true,
|
||||
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t(Modality)}`,
|
||||
};
|
||||
csUtils.genericMetadataProvider.add(imageId, {
|
||||
type: 'imageUrlModule',
|
||||
@@ -99,7 +102,8 @@ const _getDisplaySetsFromSeries = (instances, servicesManager, extensionManager)
|
||||
});
|
||||
};
|
||||
|
||||
export default function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
||||
export default function getSopClassHandlerModule(params) {
|
||||
const { servicesManager, extensionManager } = params;
|
||||
const getDisplaySetsFromSeries = instances => {
|
||||
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,97 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-measurement-tracking
|
||||
|
||||
Loaded 100 of 679 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user