Compare commits

...
Author SHA1 Message Date
ohif-bot f412dc254b chore(version): Update package versions [skip ci] 2025-05-07 20:24:55 +00:00
ohif-bot c0ed128fb7 chore(version): version.json [skip ci] 2025-05-07 20:24:49 +00:00
Rafael Osipov c829270c80 feat: add locale files for Russian language, translated texts in locale json files to Russian language. (#4984) 2025-05-07 16:20:10 -04:00
ohif-bot a16d612a8a chore(version): Update package versions [skip ci] 2025-05-06 15:10:20 +00:00
ohif-bot 5f94ebe81a chore(version): version.json [skip ci] 2025-05-06 15:10:13 +00:00
Ibrahim 8218a9d034 feat(i18n): add Arabic translations (#5013) 2025-05-06 11:05:34 -04:00
ohif-bot 4b94f18f7c chore(version): Update package versions [skip ci] 2025-05-05 20:40:11 +00:00
ohif-bot 8e7e9ca02f chore(version): version.json [skip ci] 2025-05-05 20:40:04 +00:00
Alireza cd4e6408bc feat(data overlay): add foreground background control plus orientation change in the UI and add RTDOSE support (#5007) 2025-05-05 16:35:05 -04:00
ohif-bot c1fced0573 chore(version): Update package versions [skip ci] 2025-05-05 15:32:36 +00:00
ohif-bot e41019f2b3 chore(version): version.json [skip ci] 2025-05-05 15:32:30 +00:00
Alireza 383f45823c refactor(viewport): Remove resize detector and optimize viewport resizing logic (#5006) 2025-05-05 11:27:26 -04:00
ohif-bot 6337d2e9cb chore(version): Update package versions [skip ci] 2025-05-02 18:50:57 +00:00
ohif-bot e987710908 chore(version): version.json [skip ci] 2025-05-02 18:50:50 +00:00
Pedro Köhler 7dbe5e37b0 feat: improve menu modals customizations (#5011) 2025-05-02 14:45:39 -04:00
ohif-bot ff974e2a03 chore(version): Update package versions [skip ci] 2025-05-02 18:10:04 +00:00
ohif-bot cccf74d6ea chore(version): version.json [skip ci] 2025-05-02 18:09:57 +00:00
Alireza 4be8495335 fix/remove legacy ui (#5010) 2025-05-02 14:05:40 -04:00
ohif-bot 1cb8d328a4 chore(version): Update package versions [skip ci] 2025-05-01 22:22:48 +00:00
ohif-bot 2c8dda01ae chore(version): version.json [skip ci] 2025-05-01 22:22:42 +00:00
Alireza 4e185e4b21 docs: add llm txt (#5009) 2025-05-01 18:13:21 -04:00
ohif-bot 4732e8093e chore(version): Update package versions [skip ci] 2025-05-01 18:51:19 +00:00
ohif-bot 984471e972 chore(version): version.json [skip ci] 2025-05-01 18:51:13 +00:00
Julia Young 25cb71b3f6 docs: clarify terminology for study, series, and display sets (#5000) 2025-05-01 14:46:21 -04:00
ohif-bot 0e75f6d62c chore(version): Update package versions [skip ci] 2025-04-30 16:29:06 +00:00
ohif-bot d75b3e25b6 chore(version): version.json [skip ci] 2025-04-30 16:28:59 +00:00
Tang Cheng 0bb9b17636 Fix: issue #4759 Enable proper DICOM image rendering when Rescale parameters are set to null (#5003) 2025-04-30 12:24:06 -04:00
ohif-bot b21ee9bc5b chore(version): Update package versions [skip ci] 2025-04-29 18:58:50 +00:00
ohif-bot 7ed0f6d53b chore(version): version.json [skip ci] 2025-04-29 18:58:44 +00:00
Alireza 98f8187dfe fix(referencelines): and bring back the notification for worker updates but disallow duplication (#5005) 2025-04-29 14:53:37 -04:00
ohif-bot 4643d6dc40 chore(version): Update package versions [skip ci] 2025-04-29 17:02:30 +00:00
ohif-bot 258b353269 chore(version): version.json [skip ci] 2025-04-29 17:02:23 +00:00
devin-ai-integration[bot]andAlireza Sedghi 0f39496290 fix(template): Update mode template to match current implementation (OHI-1833) (#4998)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Alireza Sedghi <ar.sedghi@gmail.com>
2025-04-29 12:57:24 -04:00
679 changed files with 7986 additions and 19678 deletions

No files matched your search

+2
View File
@@ -58,3 +58,5 @@ tests/test-results/
tests/playwright-report/
/blob-report/
/playwright/.cache/
**/.claude/settings.local.json
+3 -1
View File
@@ -109,6 +109,8 @@
"wado",
"wadors",
"wadouri",
"workerpool"
"workerpool",
"Colorbar",
"Colorbars"
]
}
+106
View File
@@ -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
View File
@@ -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
View File
@@ -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": {
+88
View File
@@ -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
View File
@@ -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"
+139 -139
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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
+8 -8
View File
@@ -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"
}
}
@@ -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"
}
}
+94
View File
@@ -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
+10 -10
View File
@@ -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,
});
};
+52 -8
View File
@@ -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}
/>
);
}
@@ -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;
@@ -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>
);
}
@@ -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;
@@ -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,
}));
}
@@ -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;
@@ -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,
@@ -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>
@@ -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,
},
});
};
@@ -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(
@@ -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>
)}
@@ -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;
}
+3
View File
@@ -269,4 +269,7 @@ export {
CornerstoneViewportDownloadForm,
utils,
};
// Export constants
export { VOLUME_LOADER_SCHEME, DYNAMIC_VOLUME_LOADER_SCHEME } from './constants';
export default cornerstoneExtension;
+121 -39
View File
@@ -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,
});
}
@@ -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 = {
@@ -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
@@ -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,
};
}
+97
View File
@@ -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
+3 -3
View File
@@ -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',
+139
View File
@@ -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 -10
View File
@@ -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',
+33 -4
View File
@@ -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;
}
+88
View File
@@ -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
+5 -5
View File
@@ -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",
+91
View File
@@ -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
+3 -3
View File
@@ -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);
};
+91
View File
@@ -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
+3 -3
View File
@@ -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