Compare commits

...
Author SHA1 Message Date
ohif-bot 59cadad98c chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.10.2
 - @ohif/extension-dicom-html@1.3.2
 - @ohif/extension-dicom-rt@0.6.5
 - @ohif/extension-dicom-segmentation@0.5.3
 - @ohif/extension-dicom-tag-browser@0.2.3
 - @ohif/extension-vtk@1.10.5
 - @ohif/core@2.12.4
 - @ohif/viewer@4.8.11
2021-02-25 21:31:47 +00:00
Igor Octaviano 64f87d2c93 Merge pull request #2281 from OHIF/fix/bump-dcmjs
Update dependency: dcmjs@0.18.1
2021-02-25 18:28:37 -03:00
igoroctaviano cfd66e5ef7 Update dependency: dcmjs@0.18.1
dcmjs
  * @ohif/core: 0.17.2 → 0.18.1
  * @ohif/extension-cornerstone: 0.17.2 → 0.18.1
  * @ohif/extension-dicom-html: 0.17.2 → 0.18.1
  * @ohif/extension-dicom-rt: 0.17.2 → 0.18.1
  * @ohif/extension-dicom-segmentation: 0.17.2 → 0.18.1
  * @ohif/extension-dicom-tag-browser: 0.17.2 → 0.18.1
  * @ohif/extension-vtk: 0.17.2 → 0.18.1
  * @ohif/viewer: 0.17.2 → 0.18.1
2021-02-25 17:05:24 -03:00
Andrey Fedorovandigoroctaviano 6480389778 chore: add acknowledgment of IDC (#2255)
* add acknowldgment of IDC

resolves #1136

* Fix typo + remove IDC ack example

Co-authored-by: igoroctaviano <igoroctaviano@gmail.com>
2021-02-12 12:08:43 +01:00
ohif-bot 0e736e7a56 chore(release): publish [skip ci]
- @ohif/viewer@4.8.10
2021-02-08 11:56:46 +00:00
Igor Octaviano d246cd9842 Merge pull request #2267 from OHIF/IDC-2251
IDC-2251: Add error page and not found pages if failed to retrieve study data
2021-02-08 08:53:35 -03:00
igoroctaviano 4fa1eb683d Merge branch 'master' of github.com:OHIF/Viewers into IDC-2251 2021-02-07 22:29:45 -03:00
ohif-bot ea6842a276 chore(release): publish [skip ci]
- @ohif/extension-dicom-tag-browser@0.2.2
 - @ohif/extension-vtk@1.10.4
 - @ohif/core@2.12.3
 - @ohif/ui@1.8.2
 - @ohif/viewer@4.8.9
2021-02-05 20:03:45 +00:00
igoroctaviano d97270f995 Merge branch 'master' of github.com:OHIF/Viewers into IDC-2251 2021-02-05 17:00:06 -03:00
Igor Octaviano 0ef76f8b66 Merge pull request #2260 from OHIF/IDC-2258
IDC-2258: Replace instance dropdown to slider for dicom tag browser
2021-02-05 16:59:38 -03:00
igoroctaviano 05b5a10af9 Merge branch 'master' of github.com:OHIF/Viewers into IDC-2258 2021-02-05 16:15:29 -03:00
ohif-bot e1150acaf6 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.10.1
 - @ohif/extension-dicom-html@1.3.1
 - @ohif/extension-dicom-rt@0.6.4
 - @ohif/extension-dicom-segmentation@0.5.2
 - @ohif/extension-dicom-tag-browser@0.2.1
 - @ohif/extension-vtk@1.10.3
 - @ohif/core@2.12.2
 - @ohif/viewer@4.8.8
2021-02-05 18:42:46 +00:00
Igor Octaviano 4abdbe0584 Merge branch 'master' into IDC-2258 2021-02-05 15:40:40 -03:00
Igor Octaviano 9223a52af9 Merge pull request #2268 from OHIF/IDC-2252
Update dependency: dcmjs@0.17.2 to fix Failure to load a valid SEG object due to incorrect expectations about ReferencedSegmentNumber
2021-02-05 15:40:09 -03:00
igoroctaviano 06f58d18bf Bump dcmjs 2021-02-03 17:38:20 -03:00
igoroctaviano 7c5f9c44b6 Add error page and not found pages if failed to retrieve study data 2021-02-03 13:58:46 -03:00
igoroctaviano ef341d1518 Replace instance dropdown to slider for dicom tag browser 2021-01-28 15:43:51 -03:00
ohif-bot d956963ef0 chore(release): publish [skip ci]
- @ohif/viewer@4.8.7
2021-01-28 14:32:43 +00:00
noerog 83b7d9e8b4 fix(config): Update Google Healthcare API endpoint from v1beta1 to v1 (#2256) 2021-01-28 15:29:53 +01:00
ohif-bot 12e73228d1 chore(release): publish [skip ci]
- @ohif/viewer@4.8.6
2021-01-26 12:43:46 +00:00
Jason Klotzer 4002401c66 fixes #2249 2021-01-26 13:39:48 +01:00
ohif-bot 18a8e059b6 chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.6.3
 - @ohif/viewer@4.8.5
2021-01-21 17:33:28 +00:00
Igor Octaviano f3582e514e Merge pull request #2244 from OHIF/IDC-2123
feat(IDC-2123): When RTSTRUCT does not apply to the specific image series, the button and the panel should not be shown
2021-01-21 14:30:41 -03:00
igoroctaviano c76bfd4a58 Merge branch 'IDC-2123' of github.com:OHIF/Viewers into IDC-2123 2021-01-21 14:15:25 -03:00
igoroctaviano 7f8cf5f5da :wq
erge branch 'master' of github.com:OHIF/Viewers into IDC-2123
2021-01-21 14:15:13 -03:00
Igor Octaviano 5ef926477c Merge pull request #2240 from OHIF/IDC-1136
docs(IDC-1136): Add funding acknowledgment of the IDC project
2021-01-21 13:59:34 -03:00
igoroctaviano 87f5b97983 Merge branch 'master' of github.com:OHIF/Viewers into IDC-1136 2021-01-21 13:12:00 -03:00
ohif-bot 4c7b2b4ae0 chore(release): publish [skip ci]
- @ohif/extension-dicom-microscopy@0.51.4
 - @ohif/viewer@4.8.4
2021-01-21 14:59:06 +00:00
igoroctaviano d1e52dd6a6 Merge branch 'master' of github.com:OHIF/Viewers into IDC-1136 2021-01-21 11:55:17 -03:00
Igor Octaviano cb147dfc69 Merge pull request #2241 from OHIF/IDC-2223
feat(IDC-2223): Add error notification on microscopy viewport initialization errors
2021-01-21 11:54:51 -03:00
igoroctaviano a5fdeddfb4 Merge branch 'master' of github.com:OHIF/Viewers into IDC-2223 2021-01-21 10:42:41 -03:00
ohif-bot e0e5f6000f chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.6.2
 - @ohif/extension-dicom-segmentation@0.5.1
 - @ohif/viewer@4.8.3
2021-01-21 13:36:40 +00:00
igoroctaviano cffb7d6f2f Merge branch 'master' of github.com:OHIF/Viewers into IDC-2223 2021-01-21 10:29:15 -03:00
Igor Octaviano 360616be13 Merge pull request #2243 from OHIF/IDC-1801
feat(IDC-1801): Improve/harmonize segmentations panel with rtstruct and add new visibility toggles
2021-01-21 10:28:40 -03:00
igoroctaviano d8fe8929b6 Merge branch 'IDC-1801' of github.com:OHIF/Viewers into IDC-1801 2021-01-21 09:37:50 -03:00
igoroctaviano 6b99a21227 Merge branch 'master' of github.com:OHIF/Viewers into IDC-1801 2021-01-21 09:37:39 -03:00
Igor Octaviano 16631e1fc4 Merge branch 'master' into IDC-1801 2021-01-21 09:36:51 -03:00
ohif-bot 587de94cea chore(release): publish [skip ci]
- @ohif/extension-vtk@1.10.2
 - @ohif/core@2.12.1
 - @ohif/ui@1.8.1
 - @ohif/viewer@4.8.2
2021-01-21 12:35:41 +00:00
Igor Octaviano 0ebe8a67d2 Merge branch 'master' into IDC-2123 2021-01-21 09:31:29 -03:00
Igor Octaviano 39a988dac7 Merge branch 'master' into IDC-1136 2021-01-21 09:31:26 -03:00
Igor Octaviano f95928bcdd Merge branch 'master' into IDC-2223 2021-01-21 09:31:22 -03:00
Igor Octaviano 0f736bd294 Merge branch 'master' into IDC-1801 2021-01-21 09:31:17 -03:00
Igor Octaviano 6cbd9839c6 Merge pull request #2246 from OHIF/IDC-2192
feat(IDC-2192): Notify buffer allocation failure trying to do MPR for a large series
2021-01-21 09:31:07 -03:00
igoroctaviano 649bc5825a Trigger netlify deploy 2021-01-21 09:06:29 -03:00
igoroctaviano 8038009afd Add exit mpr action 2021-01-20 20:59:03 -03:00
igoroctaviano f07f21117d Notify user of buffer errors from vtk / add possibility to add actions to notifications 2021-01-20 18:05:20 -03:00
igoroctaviano 77c7463c53 RTSTRUCT does not apply to the specific image series, the button and the panel should not be shown 2021-01-19 17:56:41 -03:00
igoroctaviano 449e8f25f6 Toggle visibility for cornerstone by default 2021-01-19 14:22:52 -03:00
igoroctaviano 437c01511e Toggle visibility for multiple labelmaps (overlapping) 2021-01-19 14:10:40 -03:00
igoroctaviano 00fddc1550 Improve/harmonize segmentations panel and add bulk visibility toggles 2021-01-16 01:19:12 -03:00
igoroctaviano 00b5cccae5 Add error message on microscopy viewport errors 2021-01-15 15:23:43 -03:00
igoroctaviano e545dd5b28 Add funding acknowledgment of the IDC project 2021-01-15 09:53:32 -03:00
ohif-bot efde6ed20e chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.6.1
 - @ohif/extension-vtk@1.10.1
 - @ohif/viewer@4.8.1
2020-12-10 16:49:23 +00:00
Igor Octaviano 32022f51f2 fix: panel issues and mpr button (IDC2122-IDC2117) 2020-12-10 17:44:44 +01:00
55 changed files with 1368 additions and 665 deletions

No files matched your search

+17 -2
View File
@@ -121,8 +121,7 @@ window.config = {
}; };
``` ```
- Install the viewer: - Install the viewer: `window.OHIFViewer.installViewer(window.config);`
`window.OHIFViewer.installViewer(window.config);`
This exact setup is demonstrated in this This exact setup is demonstrated in this
[CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our [CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our
@@ -259,6 +258,22 @@ Cancer Institute, Informatics Technology for Cancer Research (ITCR) program,
under a under a
[grant to Dr. Gordon Harris at Massachusetts General Hospital (U24 CA199460)](https://projectreporter.nih.gov/project_info_description.cfm?aid=8971104). [grant to Dr. Gordon Harris at Massachusetts General Hospital (U24 CA199460)](https://projectreporter.nih.gov/project_info_description.cfm?aid=8971104).
## Projects that contributed to OHIF Viewer
The following is a (partial) list of projects that contributed resources towards
development of OHIF Viewer:
- [NCI Imaging Data Commons (IDC) project](https://imaging.datacommons.cancer.gov/)
supported development of new features and bug fixes marked with
["IDC:priority"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Apriority),
["IDC:candidate"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acandidate)
or
["IDC:collaboration"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acollaboration).
NCI Imaging Data Commons is supported by the contract number 19X037Q from
Leidos Biomedical Research under Task Order HHSN26100071 from NCI.
[IDC Viewer](https://learn.canceridc.dev/portal/visualization) is a customized
version of the OHIF Viewer.
## License ## License
MIT © [OHIF](https://github.com/OHIF) MIT © [OHIF](https://github.com/OHIF)
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.10.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.1...@ohif/extension-cornerstone@2.10.2) (2021-02-25)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.10.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.0...@ohif/extension-cornerstone@2.10.1) (2021-02-05)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [2.10.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.6...@ohif/extension-cornerstone@2.10.0) (2020-12-10) # [2.10.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.6...@ohif/extension-cornerstone@2.10.0) (2020-12-10)
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-cornerstone", "name": "@ohif/extension-cornerstone",
"version": "2.10.0", "version": "2.10.2",
"description": "OHIF extension for Cornerstone", "description": "OHIF extension for Cornerstone",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -36,7 +36,7 @@
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "^4.20.1", "cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "0.17.0", "dcmjs": "0.18.1",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.1...@ohif/extension-dicom-html@1.3.2) (2021-02-25)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.0...@ohif/extension-dicom-html@1.3.1) (2021-02-05)
**Note:** Version bump only for package @ohif/extension-dicom-html
# [1.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.9...@ohif/extension-dicom-html@1.3.0) (2020-12-10) # [1.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.9...@ohif/extension-dicom-html@1.3.0) (2020-12-10)
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-html", "name": "@ohif/extension-dicom-html",
"version": "1.3.0", "version": "1.3.2",
"description": "OHIF extension for rendering structured reports to HTML", "description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -29,7 +29,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"dcmjs": "0.17.0", "dcmjs": "0.18.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6" "react-dom": "^16.8.6"
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.51.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.3...@ohif/extension-dicom-microscopy@0.51.4) (2021-01-21)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
## [0.51.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.2...@ohif/extension-dicom-microscopy@0.51.3) (2020-09-10) ## [0.51.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.2...@ohif/extension-dicom-microscopy@0.51.3) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy **Note:** Version bump only for package @ohif/extension-dicom-microscopy
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-microscopy", "name": "@ohif/extension-dicom-microscopy",
"version": "0.51.3", "version": "0.51.4",
"description": "OHIF extension for Dicom Microscopy", "description": "OHIF extension for Dicom Microscopy",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -62,11 +62,24 @@ class DicomMicroscopyViewport extends Component {
); );
const microscopyViewer = api.VLWholeSlideMicroscopyImageViewer; const microscopyViewer = api.VLWholeSlideMicroscopyImageViewer;
this.viewer = new microscopyViewer({ try {
client: dicomWebClient, this.viewer = new microscopyViewer({
metadata, client: dicomWebClient,
retrieveRendered: false, metadata,
}); retrieveRendered: false,
});
} catch (error) {
console.error('[Microscopy Viewer] Failed to load:', error);
const { UINotificationService } = this.props.servicesManager.services;
if (UINotificationService) {
UINotificationService.show({
title: 'Microscopy Viewport',
message:
'Failed to load viewport. Please check that you have hardware acceleration enabled.',
type: 'error',
});
}
}
this.viewer.render({ container }); this.viewer.render({ container });
}); });
@@ -92,8 +105,8 @@ class DicomMicroscopyViewport extends Component {
{this.state.error ? ( {this.state.error ? (
<h2>{JSON.stringify(this.state.error)}</h2> <h2>{JSON.stringify(this.state.error)}</h2>
) : ( ) : (
<div style={style} ref={this.container} /> <div style={style} ref={this.container} />
)} )}
</div> </div>
); );
} }
+8 -10
View File
@@ -6,14 +6,6 @@ const Component = React.lazy(() => {
return import('./DicomMicroscopyViewport'); return import('./DicomMicroscopyViewport');
}); });
const DicomMicroscopyViewport = props => {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Component {...props} />
</React.Suspense>
);
};
export default { export default {
/** /**
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
@@ -21,8 +13,14 @@ export default {
id: 'microscopy', id: 'microscopy',
version, version,
getViewportModule() { getViewportModule({ servicesManager }) {
return DicomMicroscopyViewport; return props => {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Component {...props} servicesManager={servicesManager} />
</React.Suspense>
);
};
}, },
getSopClassHandlerModule() { getSopClassHandlerModule() {
return DicomMicroscopySopClassHandler; return DicomMicroscopySopClassHandler;
+43
View File
@@ -3,6 +3,49 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.6.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.4...@ohif/extension-dicom-rt@0.6.5) (2021-02-25)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.3...@ohif/extension-dicom-rt@0.6.4) (2021-02-05)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.2...@ohif/extension-dicom-rt@0.6.3) (2021-01-21)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.1...@ohif/extension-dicom-rt@0.6.2) (2021-01-21)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.0...@ohif/extension-dicom-rt@0.6.1) (2020-12-10)
### Bug Fixes
* panel issues and mpr button (IDC2122-IDC2117) ([32022f5](https://github.com/OHIF/Viewers/commit/32022f51f2d24f53f4c98188980db038a6dfe76c))
# [0.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.5.0...@ohif/extension-dicom-rt@0.6.0) (2020-12-10) # [0.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.5.0...@ohif/extension-dicom-rt@0.6.0) (2020-12-10)
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-rt", "name": "@ohif/extension-dicom-rt",
"version": "0.6.0", "version": "0.6.5",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.", "description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.20.1", "cornerstone-tools": "^4.20.1",
"dcmjs": "0.17.0", "dcmjs": "0.18.1",
"gl-matrix": "^3.3.0", "gl-matrix": "^3.3.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
@@ -15,7 +15,7 @@ const OHIFDicomRTStructSopClassHandler = {
id: 'OHIFDicomRTStructSopClassHandler', id: 'OHIFDicomRTStructSopClassHandler',
type: MODULE_TYPES.SOP_CLASS_HANDLER, type: MODULE_TYPES.SOP_CLASS_HANDLER,
sopClassUIDs, sopClassUIDs,
getDisplaySetFromSeries: function( getDisplaySetFromSeries: function (
series, series,
study, study,
dicomWebClient, dicomWebClient,
@@ -71,11 +71,11 @@ const OHIFDicomRTStructSopClassHandler = {
} }
} }
rtStructDisplaySet.getSourceDisplaySet = function(studies) { rtStructDisplaySet.getSourceDisplaySet = function (studies) {
return getSourceDisplaySet(studies, rtStructDisplaySet); return getSourceDisplaySet(studies, rtStructDisplaySet);
}; };
rtStructDisplaySet.load = function(referencedDisplaySet, studies) { rtStructDisplaySet.load = function (referencedDisplaySet, studies) {
return loadRTStruct( return loadRTStruct(
rtStructDisplaySet, rtStructDisplaySet,
referencedDisplaySet, referencedDisplaySet,
@@ -39,6 +39,8 @@
border-radius: 5px; border-radius: 5px;
} }
.icons .angle-double-down, .icons .angle-double-up { .icons .angle-double-down,
.icons .angle-double-up {
margin-left: 15px; margin-left: 15px;
margin-right: -5px;
} }
@@ -9,16 +9,21 @@
.dcmrt-panel .dcmrt-panel-header { .dcmrt-panel .dcmrt-panel-header {
display: flex; display: flex;
flex-direction: row; flex-direction: column;
justify-content: space-between; justify-content: space-between;
color: white; color: white;
} }
.dcmrt-panel .dcmrt-panel-header h3 {
margin-top: 0;
}
.dcmrt-panel .dcmrt-panel-header .cog-icon { .dcmrt-panel .dcmrt-panel-header .cog-icon {
align-self: flex-end; align-self: flex-end;
color: var(--default-color); color: var(--default-color);
cursor: pointer; cursor: pointer;
margin-bottom: 20px; min-height: 23px;
max-height: 23px;
} }
.dcmrt-panel .dcmrt-panel-header .cog-icon:hover { .dcmrt-panel .dcmrt-panel-header .cog-icon:hover {
@@ -44,7 +44,7 @@ const RTPanel = ({
activeContexts = [], activeContexts = [],
contexts = {}, contexts = {},
activeViewport, activeViewport,
getActiveViewport getActiveViewport,
}) => { }) => {
const isVTK = () => activeContexts.includes(contexts.VTK); const isVTK = () => activeContexts.includes(contexts.VTK);
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE); const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
@@ -55,7 +55,7 @@ const RTPanel = ({
referencedDisplaysets: [], referencedDisplaysets: [],
sets: [], sets: [],
selectedSet: null, selectedSet: null,
isLocked: false isLocked: false,
}; };
const [state, setState] = useState(DEFAULT_STATE); const [state, setState] = useState(DEFAULT_STATE);
@@ -79,15 +79,22 @@ const RTPanel = ({
* without subscribing to external events. * without subscribing to external events.
*/ */
useEffect(() => { useEffect(() => {
updateStructureSets();
document.addEventListener('extensiondicomrtrtloaded', updateStructureSets); document.addEventListener('extensiondicomrtrtloaded', updateStructureSets);
return () => { return () => {
document.removeEventListener('extensiondicomrtrtloaded', updateStructureSets); document.removeEventListener(
'extensiondicomrtrtloaded',
updateStructureSets
);
}; };
}, []); }, []);
useEffect(() => { useEffect(() => {
const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID); const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const referencedDisplaysets = studyMetadata.getDerivedDatasets({ const referencedDisplaysets = studyMetadata.getDerivedDatasets({
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID, referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
Modality: 'RTSTRUCT', Modality: 'RTSTRUCT',
@@ -233,7 +240,7 @@ const RTPanel = ({
return ( return (
<div className="dcmrt-panel"> <div className="dcmrt-panel">
<div className="dcmrt-panel-header"> <div className="dcmrt-panel-header">
<h3>RT Structure Sets</h3> {' '}
{!state.isLocked && ( {!state.isLocked && (
<Icon <Icon
className="cog-icon" className="cog-icon"
@@ -243,71 +250,75 @@ const RTPanel = ({
onClick={() => setShowSettings(true)} onClick={() => setShowSettings(true)}
/> />
)} )}
<h3>RT Structure Sets</h3>
</div> </div>
{!state.isLocked && !state.referencedDisplaysets.length && <LoadingIndicator expand height="70px" width="70px" />} {!state.isLocked && !state.referencedDisplaysets.length && (
{state.sets && state.referencedDisplaysets.map(displaySet => { <LoadingIndicator expand height="70px" width="70px" />
const { SeriesInstanceUID, metadata, isLoaded } = displaySet; )}
{state.sets &&
state.referencedDisplaysets.map(displaySet => {
const { SeriesInstanceUID, metadata, isLoaded } = displaySet;
const module = cornerstoneTools.getModule('rtstruct'); const module = cornerstoneTools.getModule('rtstruct');
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid( const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
activeViewport.SeriesInstanceUID activeViewport.SeriesInstanceUID
); );
const loadedSet = sets.find( const loadedSet = sets.find(
set => set.SeriesInstanceUID === SeriesInstanceUID set => set.SeriesInstanceUID === SeriesInstanceUID
); );
if (!loadedSet) return null; if (!loadedSet) return null;
return ( return (
<PanelSection <PanelSection
key={SeriesInstanceUID} key={SeriesInstanceUID}
title={metadata.StructureSetLabel} title={metadata.StructureSetLabel}
loading={!isLoaded || !loadedSet} loading={!isLoaded || !loadedSet}
visible={isLoaded && loadedSet && loadedSet.visible} visible={isLoaded && loadedSet && loadedSet.visible}
hideVisibleButton={!isLoaded} hideVisibleButton={!isLoaded}
expanded={ expanded={
isLoaded && isLoaded &&
loadedSet.SeriesInstanceUID === loadedSet.SeriesInstanceUID ===
state.selectedSet.SeriesInstanceUID state.selectedSet.SeriesInstanceUID
}
onVisibilityChange={newVisibility => {
const module = cornerstoneTools.getModule('rtstruct');
if (newVisibility) {
module.setters.showStructureSet(loadedSet.SeriesInstanceUID);
} else {
module.setters.hideStructureSet(loadedSet.SeriesInstanceUID);
} }
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid( onVisibilityChange={newVisibility => {
activeViewport.SeriesInstanceUID
);
setState(state => ({ ...state, sets }));
refreshViewport();
}}
onExpandChange={async () => {
if (!isLoaded) {
await displaySet.load(activeViewport, studies);
const module = cornerstoneTools.getModule('rtstruct'); const module = cornerstoneTools.getModule('rtstruct');
if (newVisibility) {
module.setters.showStructureSet(loadedSet.SeriesInstanceUID);
} else {
module.setters.hideStructureSet(loadedSet.SeriesInstanceUID);
}
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid( const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
activeViewport.SeriesInstanceUID activeViewport.SeriesInstanceUID
); );
const selectedSet = sets.find( setState(state => ({ ...state, sets }));
set => set.SeriesInstanceUID === SeriesInstanceUID refreshViewport();
); }}
setState(state => ({ ...state, selectedSet, sets })); onExpandChange={async () => {
} if (!isLoaded) {
}} await displaySet.load(activeViewport, studies);
> const module = cornerstoneTools.getModule('rtstruct');
<ScrollableArea> const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
<TableList headless> activeViewport.SeriesInstanceUID
{isLoaded && );
loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))} const selectedSet = sets.find(
</TableList> set => set.SeriesInstanceUID === SeriesInstanceUID
</ScrollableArea> );
</PanelSection> setState(state => ({ ...state, selectedSet, sets }));
); }
})} }}
>
<ScrollableArea>
<TableList headless>
{isLoaded &&
loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))}
</TableList>
</ScrollableArea>
</PanelSection>
);
})}
</div> </div>
); );
}; };
+27 -14
View File
@@ -5,6 +5,9 @@ import id from './id.js';
import RTPanel from './components/RTPanel/RTPanel'; import RTPanel from './components/RTPanel/RTPanel';
import { version } from '../package.json'; import { version } from '../package.json';
import { utils } from '@ohif/core';
const { studyMetadataManager } = utils;
export default { export default {
/** /**
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
@@ -45,24 +48,34 @@ export default {
icon: 'list', icon: 'list',
label: 'RTSTRUCT', label: 'RTSTRUCT',
target: 'rt-panel', target: 'rt-panel',
isDisabled: studies => { isDisabled: (studies, activeViewport) => {
if (!studies) { if (!studies) {
return true; return true;
} }
for (let i = 0; i < studies.length; i++) { if (activeViewport) {
const study = studies[i]; const study = studies.find(
s => s.StudyInstanceUID === activeViewport.StudyInstanceUID
if (study && study.series) { );
for (let j = 0; j < study.series.length; j++) { const ds = study.displaySets.find(
const series = study.series[j]; ds =>
if ( ds.displaySetInstanceUID ===
/* Could be expanded to contain RTPLAN and RTDOSE information in the future */ activeViewport.displaySetInstanceUID
['RTSTRUCT'].includes(series.Modality) );
) { const studyMetadata = studyMetadataManager.get(
return false; activeViewport.StudyInstanceUID
} );
} const referencedDisplaySets = studyMetadata.getDerivedDatasets({
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
Modality: 'RTSTRUCT',
});
if (
referencedDisplaySets &&
referencedDisplaySets.some(ds =>
['RTSTRUCT'].includes(ds.Modality)
)
) {
return false;
} }
} }
+6 -1
View File
@@ -96,6 +96,11 @@ export default async function loadRTStruct(
imageIdSopInstanceUidPairs imageIdSopInstanceUidPairs
); );
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID); const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
if (!imageId) {
continue;
}
const imageIdSpecificToolData = _getOrCreateImageIdSpecificToolData( const imageIdSpecificToolData = _getOrCreateImageIdSpecificToolData(
toolState, toolState,
imageId, imageId,
@@ -294,7 +299,7 @@ const _getImageId = (imageIdSopInstanceUidPairs, sopInstanceUID) => {
imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID
); );
return imageIdSopInstanceUidPairsEntry.imageId; return imageIdSopInstanceUidPairsEntry ? imageIdSopInstanceUidPairsEntry.imageId : null;
}; };
function _getImageIdSopInstanceUidPairsForDisplaySet( function _getImageIdSopInstanceUidPairsForDisplaySet(
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.5.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.2...@ohif/extension-dicom-segmentation@0.5.3) (2021-02-25)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.5.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.1...@ohif/extension-dicom-segmentation@0.5.2) (2021-02-05)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.5.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.0...@ohif/extension-dicom-segmentation@0.5.1) (2021-01-21)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
# [0.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.7...@ohif/extension-dicom-segmentation@0.5.0) (2020-12-10) # [0.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.7...@ohif/extension-dicom-segmentation@0.5.0) (2020-12-10)
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-segmentation", "name": "@ohif/extension-dicom-segmentation",
"version": "0.5.0", "version": "0.5.3",
"description": "OHIF extension for viewing segmentations in the 2D MPR view", "description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.20.1", "cornerstone-tools": "^4.20.1",
"dcmjs": "0.17.0", "dcmjs": "0.18.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6" "react-dom": "^16.8.6"
@@ -24,7 +24,7 @@ const SegmentItem = ({
onClick, onClick,
itemClass, itemClass,
color, color,
visible = true, visible,
onVisibilityChange, onVisibilityChange,
}) => { }) => {
const [isVisible, setIsVisible] = useState(visible); const [isVisible, setIsVisible] = useState(visible);
@@ -33,6 +33,15 @@ const SegmentItem = ({
setIsVisible(visible); setIsVisible(visible);
}, [visible]); }, [visible]);
const onClickHandler = () => onClick(index);
const onVisibilityChangeHandler = event => {
event.stopPropagation();
const newVisibility = !isVisible;
setIsVisible(newVisibility);
onVisibilityChange(newVisibility, index);
};
return ( return (
<div className="dcmseg-segment-item"> <div className="dcmseg-segment-item">
<TableListItem <TableListItem
@@ -42,7 +51,7 @@ const SegmentItem = ({
itemClass={itemClass} itemClass={itemClass}
itemMeta={<ColoredCircle color={color} />} itemMeta={<ColoredCircle color={color} />}
itemMetaClass="segment-color-section" itemMetaClass="segment-color-section"
onItemClick={onClick} onItemClick={onClickHandler}
> >
<div> <div>
<div className="segment-label" style={{ marginBottom: 4 }}> <div className="segment-label" style={{ marginBottom: 4 }}>
@@ -63,12 +72,7 @@ const SegmentItem = ({
name={isVisible ? 'eye' : 'eye-closed'} name={isVisible ? 'eye' : 'eye-closed'}
width="20px" width="20px"
height="20px" height="20px"
onClick={event => { onClick={onVisibilityChangeHandler}
event.stopPropagation();
const newVisibility = !isVisible;
setIsVisible(newVisibility);
onVisibilityChange(newVisibility);
}}
/> />
</div> </div>
{false && <div className="segment-info">{'...'}</div>} {false && <div className="segment-info">{'...'}</div>}
@@ -6,6 +6,7 @@
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 20px;
} }
.dcmseg-segmentation-panel.disabled { .dcmseg-segmentation-panel.disabled {
@@ -14,19 +15,19 @@
} }
.dcmseg-segmentation-panel h3 { .dcmseg-segmentation-panel h3 {
padding-left: 20px;
margin-top: 0; margin-top: 0;
} }
.dcmseg-segmentation-panel .segmentations { .dcmseg-segmentation-panel .segmentations {
padding: 0 20px 20px 20px; padding-bottom: 10px;
} }
.dcmseg-segmentation-panel .cog-icon { .dcmseg-segmentation-panel .cog-icon {
margin: 20px 20px 0 0;
align-self: flex-end; align-self: flex-end;
color: var(--default-color); color: var(--default-color);
cursor: pointer; cursor: pointer;
min-height: 23px;
max-height: 23px;
} }
.dcmseg-segmentation-panel .cog-icon:hover { .dcmseg-segmentation-panel .cog-icon:hover {
@@ -48,11 +49,11 @@
height: 150px; height: 150px;
} }
.dcmseg-segmentation-panel .selector-form>div { .dcmseg-segmentation-panel .selector-form > div {
width: 100%; width: 100%;
} }
.dcmseg-segmentation-panel .selector-form>div:first-of-type { .dcmseg-segmentation-panel .selector-form > div:first-of-type {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
@@ -64,3 +65,61 @@
flex: unset; flex: unset;
margin-right: 16px; margin-right: 16px;
} }
.SegmentsSection {
display: flex;
flex-direction: column;
color: var(--text-primary-color);
font-size: 12px;
margin-bottom: 0px;
height: 100%;
}
.SegmentsSection .header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
background-color: var(--ui-gray-dark);
border-radius: 5px;
font-weight: bold;
padding: 10px 15px;
}
.SegmentsSection .count {
font-size: 15px;
color: var(--active-color);
margin-left: 15px;
border-radius: 5px;
background-color: var(--ui-gray-darker);
text-align: center;
min-width: 25px;
}
.SegmentsSection .header .eye-icon,
.SegmentsSection .header .angle-double-down,
.SegmentsSection .header .angle-double-up {
cursor: pointer;
color: var(--active-color);
}
.SegmentsSection .header .eye-icon:hover,
.SegmentsSection .header .angle-double-down:hover,
.SegmentsSection .header .angle-double-up:hover {
color: var(--hover-color);
}
.SegmentsSection .header .eye-icon.expanded {
color: var(--default-color);
}
.SegmentsSection .header .icons {
display: flex;
align-items: center;
border-radius: 5px;
}
.SegmentsSection .header .icons .angle-double-down,
.SegmentsSection .header .icons .angle-double-up {
margin-left: 15px;
}
@@ -1,8 +1,9 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cornerstoneTools from 'cornerstone-tools'; import cornerstoneTools from 'cornerstone-tools';
import cornerstone from 'cornerstone-core'; import cornerstone from 'cornerstone-core';
import moment from 'moment'; import moment from 'moment';
import classNames from 'classnames';
import { utils, log } from '@ohif/core'; import { utils, log } from '@ohif/core';
import { ScrollableArea, TableList, Icon } from '@ohif/ui'; import { ScrollableArea, TableList, Icon } from '@ohif/ui';
import DICOMSegTempCrosshairsTool from '../../tools/DICOMSegTempCrosshairsTool'; import DICOMSegTempCrosshairsTool from '../../tools/DICOMSegTempCrosshairsTool';
@@ -67,29 +68,104 @@ const SegmentationPanel = ({
const [state, setState] = useState({ const [state, setState] = useState({
brushRadius: DEFAULT_BRUSH_RADIUS, brushRadius: DEFAULT_BRUSH_RADIUS,
brushColor: 'rgba(221, 85, 85, 1)', brushColor: 'rgba(221, 85, 85, 1)',
selectedSegment: null, selectedSegment: 0,
selectedSegmentation: null, selectedSegmentation: 0,
showSegmentationSettings: false, showSettings: false,
brushStackState: null, labelMapList: [],
labelmapList: [],
segmentList: [], segmentList: [],
cachedSegmentsProperties: [], segmentsHidden: [],
segmentNumbers: [],
isLoading: false, isLoading: false,
isDisabled: true, isDisabled: true,
}); });
const getActiveViewport = () => viewports[activeIndex];
const getFirstImageId = () => {
const { StudyInstanceUID, displaySetInstanceUID } = getActiveViewport();
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
return studyMetadata.getFirstImageId(displaySetInstanceUID);
};
const getActiveLabelMaps3D = () => {
const { labelmaps3D, activeLabelmapIndex } = getBrushStackState();
return labelmaps3D[activeLabelmapIndex];
};
const getActiveLabelMapIndex = () => {
const { activeLabelmapIndex } = getBrushStackState();
return activeLabelmapIndex;
};
const getActiveSegmentIndex = () => {
const { activeSegmentIndex } = getActiveLabelMaps3D();
return activeSegmentIndex;
};
const getActiveLabelMaps2D = () => {
const { labelmaps2D } = getActiveLabelMaps3D();
return labelmaps2D;
};
const getCurrentDisplaySet = () => {
const { StudyInstanceUID, displaySetInstanceUID } = getActiveViewport();
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
const allDisplaySets = studyMetadata.getDisplaySets();
return allDisplaySets.find(
ds => ds.displaySetInstanceUID === displaySetInstanceUID
);
};
const setActiveSegment = segmentIndex => {
const activeSegmentIndex = getActiveSegmentIndex();
const activeViewport = getActiveViewport();
if (segmentIndex === activeSegmentIndex) {
log.info(`${activeSegmentIndex} is already the active segment`);
return;
}
const labelmap3D = getActiveLabelMaps3D();
labelmap3D.activeSegmentIndex = segmentIndex;
/**
* Activates the correct label map if clicked segment
* does not belong to the active labelmap
*/
const { StudyInstanceUID } = activeViewport;
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
const allDisplaySets = studyMetadata.getDisplaySets();
let selectedSegmentation;
let newLabelmapIndex = getActiveLabelMapIndex();
allDisplaySets.forEach(displaySet => {
if (displaySet.labelmapSegments) {
Object.keys(displaySet.labelmapSegments).forEach(labelmapIndex => {
if (
displaySet.labelmapSegments[labelmapIndex].includes(segmentIndex)
) {
newLabelmapIndex = labelmapIndex;
selectedSegmentation =
displaySet.hasOverlapping === true
? displaySet.originLabelMapIndex
: labelmapIndex;
}
});
}
});
const brushStackState = getBrushStackState();
brushStackState.activeLabelmapIndex = newLabelmapIndex;
setState(state => ({ ...state, selectedSegmentation }));
refreshViewports();
return segmentIndex;
};
useEffect(() => { useEffect(() => {
const labelmapModifiedHandler = event => { const labelmapModifiedHandler = event => {
log.warn('Segmentation Panel: labelmap modified', event); log.warn('Segmentation Panel: labelmap modified', event);
const module = cornerstoneTools.getModule('segmentation'); refreshSegmentations();
const activeViewport = viewports[activeIndex];
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const firstImageId = studyMetadata.getFirstImageId(
activeViewport.displaySetInstanceUID
);
updateState('brushStackState', module.state.series[firstImageId]);
}; };
/* /*
@@ -140,50 +216,38 @@ const SegmentationPanel = ({
}; };
}, [activeIndex, viewports]); }, [activeIndex, viewports]);
const refreshSegmentations = useCallback(() => { const refreshSegmentations = () => {
const module = cornerstoneTools.getModule('segmentation'); const activeViewport = getActiveViewport();
const activeViewport = viewports[activeIndex];
const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID; const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
if (!isDisabled) { if (!isDisabled) {
const studyMetadata = studyMetadataManager.get( const brushStackState = getBrushStackState();
activeViewport.StudyInstanceUID
);
const firstImageId = studyMetadata.getFirstImageId(
activeViewport.displaySetInstanceUID
);
const brushStackState = module.state.series[firstImageId];
if (brushStackState) { if (brushStackState) {
const labelmap3D = const labelMapList = getLabelMapList();
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex]; const {
const labelmapList = getLabelmapList( items: segmentList,
brushStackState, numbers: segmentNumbers,
firstImageId, segmentsHidden,
activeViewport, } = getSegmentList();
);
const segmentList = getSegmentList(
labelmap3D,
firstImageId,
brushStackState,
);
setState(state => ({ setState(state => ({
...state, ...state,
brushStackState, segmentsHidden,
selectedSegmentation: brushStackState.activeLabelmapIndex, segmentNumbers,
labelmapList, labelMapList,
segmentList, segmentList,
isDisabled, isDisabled,
})); }));
} else { } else {
setState(state => ({ setState(state => ({
...state, ...state,
labelmapList: [], segmentsHidden: [],
segmentNumbers: [],
labelMapList: [],
segmentList: [], segmentList: [],
isDisabled, isDisabled,
})); }));
} }
} }
}, [viewports, activeIndex, state.isLoading]); };
useEffect(() => { useEffect(() => {
refreshSegmentations(); refreshSegmentations();
@@ -200,309 +264,285 @@ const SegmentationPanel = ({
useEffect(() => { useEffect(() => {
setState(state => ({ setState(state => ({
...state, ...state,
showSegmentationSettings: state.showSegmentationSettings && !isOpen, showSettings: state.showSettings && !isOpen,
})); }));
}, [isOpen]); }, [isOpen]);
const getLabelmapList = useCallback( const getLabelMapList = () => {
(brushStackState, firstImageId, activeViewport) => { const activeViewport = getActiveViewport();
/* Get list of SEG labelmaps specific to active viewport (reference series) */
const referencedSegDisplaysets = _getReferencedSegDisplaysets(
activeViewport.StudyInstanceUID,
activeViewport.SeriesInstanceUID
);
return referencedSegDisplaysets.map((displaySet, index) => { /* Get list of SEG labelmaps specific to active viewport (reference series) */
const { labelmapIndex, SeriesDate, SeriesTime } = displaySet; const referencedSegDisplaysets = _getReferencedSegDisplaysets(
activeViewport.StudyInstanceUID,
activeViewport.SeriesInstanceUID
);
/* Map to display representation */ return referencedSegDisplaysets.map((displaySet, index) => {
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0]; const {
const date = moment(dateStr, 'YYYYMMDD:HHmmss'); labelmapIndex,
const isActiveLabelmap = originLabelMapIndex,
labelmapIndex === brushStackState.activeLabelmapIndex; hasOverlapping,
const displayDate = date.format('ddd, MMM Do YYYY'); SeriesDate,
const displayTime = date.format('h:mm:ss a'); SeriesTime,
const displayDescription = displaySet.SeriesDescription; } = displaySet;
return { /* Map to display representation */
value: labelmapIndex, const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
title: displayDescription, const date = moment(dateStr, 'YYYYMMDD:HHmmss');
description: displayDate, const displayDate = date.format('ddd, MMM Do YYYY');
onClick: async () => { const displayDescription = displaySet.SeriesDescription;
const activatedLabelmapIndex = await setActiveLabelmap(
activeViewport,
studies,
displaySet,
() => onSelectedSegmentationChange(),
onDisplaySetLoadFailure
);
updateState('selectedSegmentation', activatedLabelmapIndex);
},
};
});
},
[studies]
);
const getSegmentList = useCallback( return {
(labelmap3D, firstImageId, brushStackState) => { value: hasOverlapping === true ? originLabelMapIndex : labelmapIndex,
/* title: displayDescription,
* Newly created segments have no `meta` description: displayDate,
* So we instead build a list of all segment indexes in use onClick: async () => {
* Then find any associated metadata const activatedLabelmapIndex = await setActiveLabelmap(
*/ activeViewport,
const uniqueSegmentIndexes = labelmap3D.labelmaps2D studies,
.reduce((acc, labelmap2D) => { displaySet,
if (labelmap2D) { onSelectedSegmentationChange,
const segmentIndexes = labelmap2D.segmentsOnLabelmap; onDisplaySetLoadFailure
for (let i = 0; i < segmentIndexes.length; i++) {
if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) {
acc.push(segmentIndexes[i]);
}
}
}
return acc;
}, [])
.sort((a, b) => a - b);
const module = cornerstoneTools.getModule('segmentation');
const colorLutTable =
module.state.colorLutTables[labelmap3D.colorLUTIndex];
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
const segmentList = [];
for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
const segmentIndex = uniqueSegmentIndexes[i];
const color = colorLutTable[segmentIndex];
let segmentLabel = '(unlabeled)';
let segmentNumber = segmentIndex;
/* Meta */
if (hasLabelmapMeta) {
const segmentMeta = labelmap3D.metadata.data[segmentIndex];
if (segmentMeta) {
segmentNumber = segmentMeta.SegmentNumber;
segmentLabel = segmentMeta.SegmentLabel;
}
}
const sameSegment = state.selectedSegment === segmentNumber;
const setCurrentSelectedSegment = () => {
const activeViewport = viewports[activeIndex];
_setActiveSegment(
firstImageId,
segmentNumber,
labelmap3D.activeSegmentIndex,
activeViewport
); );
updateState('selectedSegment', sameSegment ? null : segmentNumber); setState(state => ({
...state,
const validIndexList = []; selectedSegmentation: activatedLabelmapIndex,
labelmap3D.labelmaps2D.forEach((labelMap2D, index) => { }));
if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) { },
validIndexList.push(index); };
} });
});
const avg = array => array.reduce((a, b) => a + b) / array.length;
const average = avg(validIndexList);
const closest = validIndexList.reduce((prev, curr) => {
return Math.abs(curr - average) < Math.abs(prev - average)
? curr
: prev;
});
if (isCornerstone()) {
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
if (!toolState) {
return;
}
const imageIds = toolState.data[0].imageIds;
const imageId = imageIds[closest];
const frameIndex = imageIds.indexOf(imageId);
const SOPInstanceUID = cornerstone.metaData.get(
'SOPInstanceUID',
imageId
);
const StudyInstanceUID = cornerstone.metaData.get(
'StudyInstanceUID',
imageId
);
DICOMSegTempCrosshairsTool.addCrosshair(
element,
imageId,
segmentNumber
);
onSegmentItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex,
});
}
if (isVTK()) {
const activeViewport = viewports[activeIndex];
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const allDisplaySets = studyMetadata.getDisplaySets();
const currentDisplaySet = allDisplaySets.find(
displaySet =>
displaySet.displaySetInstanceUID ===
activeViewport.displaySetInstanceUID
);
const frame = labelmap3D.labelmaps2D[closest];
onSegmentItemClick({
studies,
StudyInstanceUID: currentDisplaySet.StudyInstanceUID,
displaySetInstanceUID: currentDisplaySet.displaySetInstanceUID,
SOPClassUID: viewports[activeIndex].sopClassUIDs[0],
SOPInstanceUID: currentDisplaySet.SOPInstanceUID,
segmentNumber,
frameIndex: closest,
frame,
});
}
};
const isSegmentVisible = () => {
return !labelmap3D.segmentsHidden[segmentIndex];
};
const toggleSegmentVisibility = () => {
const segmentsHidden = labelmap3D.segmentsHidden;
segmentsHidden[segmentIndex] = !segmentsHidden[segmentIndex];
return !segmentsHidden[segmentIndex];
};
const cachedSegmentProperties =
state.cachedSegmentsProperties[segmentNumber];
let visible = isSegmentVisible();
if (
cachedSegmentProperties &&
cachedSegmentProperties.visible !== visible
) {
toggleSegmentVisibility();
}
segmentList.push(
<SegmentItem
key={segmentNumber}
itemClass={`segment-item ${sameSegment && 'selected'}`}
onClick={() => setCurrentSelectedSegment()}
label={segmentLabel}
index={segmentNumber}
color={color}
visible={visible}
onVisibilityChange={newVisibility => {
if (isCornerstone()) {
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
module.setters.toggleSegmentVisibility(
element,
segmentNumber,
brushStackState.activeLabelmapIndex
);
}
if (isVTK()) {
onSegmentVisibilityChange(segmentNumber, newVisibility);
}
updateCachedSegmentsProperties(segmentNumber, {
visible: newVisibility,
});
refreshViewports();
}}
/>
);
}
return segmentList;
/*
* Let's iterate over segmentIndexes ^ above
* If meta has a match, use it to show info
* If now, add "no-meta" class
* Show default name
*/
},
[activeIndex, onSegmentItemClick, state.selectedSegment, state.isLoading]
);
const updateCachedSegmentsProperties = (segmentNumber, properties) => {
const segmentsProperties = state.cachedSegmentsProperties;
const segmentProperties = state.cachedSegmentsProperties[segmentNumber];
segmentsProperties[segmentNumber] = segmentProperties
? { ...segmentProperties, ...properties }
: properties;
updateState('cachedSegmentsProperties', segmentsProperties);
}; };
useEffect(() => { const setCurrentSelectedSegment = segmentNumber => {
updateState('cachedSegmentsProperties', []); setActiveSegment(segmentNumber);
}, [activeContexts]);
const updateState = (field, value) => { const sameSegment = state.selectedSegment === segmentNumber;
setState(state => ({ ...state, [field]: value })); if (!sameSegment) {
setState(state => ({ ...state, selectedSegment: segmentNumber }));
}
const validIndexList = [];
getActiveLabelMaps2D().forEach((labelMap2D, index) => {
if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) {
validIndexList.push(index);
}
});
const avg = array => array.reduce((a, b) => a + b) / array.length;
const average = avg(validIndexList);
const closest = validIndexList.reduce((prev, curr) => {
return Math.abs(curr - average) < Math.abs(prev - average) ? curr : prev;
});
if (isCornerstone()) {
const element = getEnabledElement();
const toolState = cornerstoneTools.getToolState(element, 'stack');
if (!toolState) return;
const imageIds = toolState.data[0].imageIds;
const imageId = imageIds[closest];
const frameIndex = imageIds.indexOf(imageId);
const SOPInstanceUID = cornerstone.metaData.get(
'SOPInstanceUID',
imageId
);
const StudyInstanceUID = cornerstone.metaData.get(
'StudyInstanceUID',
imageId
);
DICOMSegTempCrosshairsTool.addCrosshair(element, imageId, segmentNumber);
onSegmentItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex,
});
}
if (isVTK()) {
const labelMaps3D = getActiveLabelMaps3D();
const currentDisplaySet = getCurrentDisplaySet();
const frame = labelMaps3D.labelmaps2D[closest];
onSegmentItemClick({
studies,
StudyInstanceUID: currentDisplaySet.StudyInstanceUID,
displaySetInstanceUID: currentDisplaySet.displaySetInstanceUID,
SOPClassUID: getActiveViewport().sopClassUIDs[0],
SOPInstanceUID: currentDisplaySet.SOPInstanceUID,
segmentNumber,
frameIndex: closest,
frame,
});
}
};
const getColorLUTTable = () => {
const { state } = cornerstoneTools.getModule('segmentation');
const { colorLUTIndex } = getActiveLabelMaps3D();
return state.colorLutTables[colorLUTIndex];
};
const getEnabledElement = () => {
const enabledElements = cornerstone.getEnabledElements();
return enabledElements[activeIndex].element;
};
const onSegmentVisibilityChangeHandler = (isVisible, segmentNumber) => {
/** Get all labelmaps with this segmentNumber (overlapping segments) */
const { labelmaps3D } = getBrushStackState();
const possibleLabelMaps3D = labelmaps3D.filter(({ labelmaps2D }) => {
return labelmaps2D.some(({ segmentsOnLabelmap }) =>
segmentsOnLabelmap.includes(segmentNumber)
);
});
let segmentsHidden = [];
possibleLabelMaps3D.forEach(labelmap3D => {
labelmap3D.segmentsHidden[segmentNumber] = !isVisible;
segmentsHidden = [
...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]),
];
});
setState(state => ({ ...state, segmentsHidden }));
refreshSegmentations();
refreshViewports();
if (isVTK()) {
onSegmentVisibilityChange(segmentNumber, isVisible);
}
};
const getSegmentList = () => {
/*
* Newly created segments have no `meta`
* So we instead build a list of all segment indexes in use
* Then find any associated metadata
*/
const uniqueSegmentIndexes = getActiveLabelMaps2D()
.reduce((acc, labelmap2D) => {
if (labelmap2D) {
const segmentIndexes = labelmap2D.segmentsOnLabelmap;
for (let i = 0; i < segmentIndexes.length; i++) {
if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) {
acc.push(segmentIndexes[i]);
}
}
}
return acc;
}, [])
.sort((a, b) => a - b);
const labelmap3D = getActiveLabelMaps3D();
const colorLutTable = getColorLUTTable();
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
const segmentList = [];
const segmentNumbers = [];
for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
const segmentIndex = uniqueSegmentIndexes[i];
const color = colorLutTable[segmentIndex];
let segmentLabel = '(unlabeled)';
let segmentNumber = segmentIndex;
/* Meta */
if (hasLabelmapMeta) {
const segmentMeta = labelmap3D.metadata.data[segmentIndex];
if (segmentMeta) {
segmentNumber = segmentMeta.SegmentNumber;
segmentLabel = segmentMeta.SegmentLabel;
}
}
const sameSegment = state.selectedSegment === segmentNumber;
segmentNumbers.push(segmentNumber);
segmentList.push(
<SegmentItem
key={segmentNumber}
itemClass={`segment-item ${sameSegment && 'selected'}`}
onClick={setCurrentSelectedSegment}
label={segmentLabel}
index={segmentNumber}
color={color}
visible={!labelmap3D.segmentsHidden[segmentIndex]}
onVisibilityChange={onSegmentVisibilityChangeHandler}
/>
);
}
return {
items: segmentList,
numbers: segmentNumbers,
segmentsHidden: labelmap3D.segmentsHidden,
};
/*
* Let's iterate over segmentIndexes ^ above
* If meta has a match, use it to show info
* If now, add "no-meta" class
* Show default name
*/
}; };
const updateBrushSize = evt => { const updateBrushSize = evt => {
const updatedRadius = Number(evt.target.value); const updatedRadius = Number(evt.target.value);
if (updatedRadius !== brushRadius) { if (updatedRadius !== brushRadius) {
updateState('brushRadius', updatedRadius); setState(state => ({ ...state, brushRadius: updatedRadius }));
const module = cornerstoneTools.getModule('segmentation'); const module = cornerstoneTools.getModule('segmentation');
module.setters.radius(updatedRadius); module.setters.radius(updatedRadius);
} }
}; };
const decrementSegment = event => { const decrementSegment = event => {
const activeSegmentIndex = getActiveSegmentIndex();
event.preventDefault(); event.preventDefault();
if (labelmap3D.activeSegmentIndex > 1) { if (activeSegmentIndex > 1) {
labelmap3D.activeSegmentIndex--; activeSegmentIndex--;
} }
updateState('selectedSegment', labelmap3D.activeSegmentIndex); setState(state => ({ ...state, selectedSegment: activeSegmentIndex }));
updateActiveSegmentColor(); updateActiveSegmentColor();
}; };
const incrementSegment = event => { const incrementSegment = event => {
const activeSegmentIndex = getActiveSegmentIndex();
event.preventDefault(); event.preventDefault();
labelmap3D.activeSegmentIndex++; activeSegmentIndex++;
updateState('selectedSegment', labelmap3D.activeSegmentIndex); setState(state => ({ ...state, selectedSegment: activeSegmentIndex }));
updateActiveSegmentColor(); updateActiveSegmentColor();
}; };
const updateActiveSegmentColor = () => { const updateActiveSegmentColor = () => {
const color = getActiveSegmentColor(); const color = getActiveSegmentColor();
updateState('brushColor', color); setState(state => ({ ...state, brushColor: color }));
};
const getBrushStackState = () => {
const module = cornerstoneTools.getModule('segmentation');
const firstImageId = getFirstImageId();
const brushStackState = module.state.series[firstImageId];
return brushStackState;
}; };
const getActiveSegmentColor = () => { const getActiveSegmentColor = () => {
if (!state.brushStackState) { const brushStackState = getBrushStackState();
if (!brushStackState) {
return 'rgba(255, 255, 255, 1)'; return 'rgba(255, 255, 255, 1)';
} }
const module = cornerstoneTools.getModule('segmentation'); const colorLutTable = getColorLUTTable();
const colorLutTable = module.state.colorLutTables[labelmap3D.colorLUTIndex];
const color = colorLutTable[labelmap3D.activeSegmentIndex]; const color = colorLutTable[labelmap3D.activeSegmentIndex];
return `rgba(${color.join(',')})`; return `rgba(${color.join(',')})`;
}; };
@@ -520,65 +560,103 @@ const SegmentationPanel = ({
refreshViewports(); refreshViewports();
}; };
const onVisibilityChangeHandler = isVisible => {
let segmentsHidden = [];
state.segmentNumbers.forEach(segmentNumber => {
if (isVTK()) {
onSegmentVisibilityChange(segmentNumber, isVisible);
}
/** Get all labelmaps with this segmentNumber (overlapping segments) */
const { labelmaps3D } = getBrushStackState();
const possibleLabelMaps3D = labelmaps3D.filter(({ labelmaps2D }) => {
return labelmaps2D.some(({ segmentsOnLabelmap }) =>
segmentsOnLabelmap.includes(segmentNumber)
);
});
possibleLabelMaps3D.forEach(labelmap3D => {
labelmap3D.segmentsHidden[segmentNumber] = !isVisible;
segmentsHidden = [
...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]),
];
});
});
setState(state => ({ ...state, segmentsHidden }));
refreshSegmentations();
refreshViewports();
};
const disabledConfigurationFields = [ const disabledConfigurationFields = [
'outlineAlpha', 'outlineAlpha',
'shouldRenderInactiveLabelmaps', 'shouldRenderInactiveLabelmaps',
]; ];
if (state.showSegmentationSettings) {
const selectedSegmentationOption = state.labelMapList.find(
i => i.value === state.selectedSegmentation
);
if (state.showSettings) {
return ( return (
<SegmentationSettings <SegmentationSettings
disabledFields={isVTK() ? disabledConfigurationFields : []} disabledFields={isVTK() ? disabledConfigurationFields : []}
configuration={configuration} configuration={configuration}
onBack={() => updateState('showSegmentationSettings', false)} onBack={() => setState(state => ({ ...state, showSettings: false }))}
onChange={updateConfiguration} onChange={updateConfiguration}
/> />
); );
} else { } else {
return ( return (
<div <div
className={`dcmseg-segmentation-panel ${state.isDisabled && className={classNames('dcmseg-segmentation-panel', {
'disabled'}`} disabled: state.isDisabled,
})}
> >
<Icon
className="cog-icon"
name="cog"
width="25px"
height="25px"
onClick={() => updateState('showSegmentationSettings', true)}
/>
{false && ( {false && (
<form className="selector-form"> <form className="selector-form">
<BrushColorSelector <BrushColorSelector
defaultColor={brushColor} defaultColor={state.brushColor}
index={state.selectedSegment} index={state.selectedSegment}
onNext={incrementSegment} onNext={incrementSegment}
onPrev={decrementSegment} onPrev={decrementSegment}
/> />
<BrushRadius <BrushRadius
value={brushRadius} value={state.brushRadius}
onChange={updateBrushSize} onChange={updateBrushSize}
min={configuration.minRadius} min={configuration.minRadius}
max={configuration.maxRadius} max={configuration.maxRadius}
/> />
</form> </form>
)} )}
<Icon
className="cog-icon"
name="cog"
width="25px"
height="25px"
onClick={() => setState(state => ({ ...state, showSettings: true }))}
/>
<h3>Segmentations</h3> <h3>Segmentations</h3>
<div className="segmentations"> <div className="segmentations">
<SegmentationSelect <SegmentationSelect
value={state.labelmapList.find( value={selectedSegmentationOption}
i => i.value === state.selectedSegmentation
)}
formatOptionLabel={SegmentationItem} formatOptionLabel={SegmentationItem}
options={state.labelmapList} options={state.labelMapList}
/> />
</div> </div>
<ScrollableArea> <SegmentsSection
<TableList count={state.segmentList.length}
customHeader={<SegmentsHeader count={state.segmentList.length} />} isVisible={
> state.segmentsHidden.filter(isHidden => isHidden === true).length <
{state.segmentList} state.segmentNumbers.length
</TableList> }
</ScrollableArea> onVisibilityChange={onVisibilityChangeHandler}
>
<ScrollableArea>
<TableList headless>{state.segmentList}</TableList>
</ScrollableArea>
</SegmentsSection>
</div> </div>
); );
} }
@@ -613,7 +691,7 @@ SegmentationPanel.propTypes = {
SegmentationPanel.defaultProps = {}; SegmentationPanel.defaultProps = {};
/** /**
* Returns SEG Displaysets that reference the target series, sorted by dateTime * Returns SEG DisplaySets that reference the target series, sorted by dateTime
* *
* @param {string} StudyInstanceUID * @param {string} StudyInstanceUID
* @param {string} SeriesInstanceUID * @param {string} SeriesInstanceUID
@@ -637,56 +715,48 @@ const _getReferencedSegDisplaysets = (StudyInstanceUID, SeriesInstanceUID) => {
return referencedDisplaysets; return referencedDisplaysets;
}; };
/** const SegmentsSection = ({
* count,
* @param {*} firstImageId children,
* @param {*} activeSegmentIndex isVisible: defaultVisibility,
* @returns onVisibilityChange,
*/ }) => {
const _setActiveSegment = (firstImageId, segmentIndex, activeSegmentIndex, activeViewport) => { const [isVisible, setIsVisible] = useState(defaultVisibility);
if (segmentIndex === activeSegmentIndex) {
log.info(`${activeSegmentIndex} is already the active segment`);
return;
}
const { state } = cornerstoneTools.getModule('segmentation'); const onVisibilityChangeHandler = () => {
const brushStackState = state.series[firstImageId]; const newVisibility = !isVisible;
setIsVisible(newVisibility);
onVisibilityChange(newVisibility);
};
const labelmap3D = useEffect(() => {
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex]; setIsVisible(defaultVisibility);
labelmap3D.activeSegmentIndex = segmentIndex; }, [defaultVisibility]);
/**
* Activates the correct label map if clicked segment
* does not belong to the active labelmap
*/
const { StudyInstanceUID } = activeViewport;
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
const allDisplaySets = studyMetadata.getDisplaySets();
let newLabelmapIndex = brushStackState.activeLabelmapIndex;
allDisplaySets.forEach(displaySet => {
if (displaySet.labelmapSegments) {
Object.keys(displaySet.labelmapSegments).forEach(labelmapIndex => {
if (displaySet.labelmapSegments[labelmapIndex].includes(segmentIndex)) {
newLabelmapIndex = labelmapIndex;
}
});
}
});
brushStackState.activeLabelmapIndex = newLabelmapIndex;
refreshViewports();
return segmentIndex;
};
const SegmentsHeader = ({ count }) => {
return ( return (
<React.Fragment> <div className="SegmentsSection">
<div className="tableListHeaderTitle">Segments</div> <div className="header">
<div className="numberOfItems">{count}</div> <div>Segments</div>
</React.Fragment> <div className="icons">
<Icon
className={`eye-icon ${isVisible && 'expanded'}`}
name={isVisible ? 'eye' : 'eye-closed'}
width="20px"
height="20px"
onClick={onVisibilityChangeHandler}
/>
<div className="count">{count}</div>
</div>
</div>
{children}
</div>
); );
}; };
const noop = () => {};
SegmentsSection.defaultProps = {
onVisibilityChange: noop,
};
export default SegmentationPanel; export default SegmentationPanel;
@@ -21,7 +21,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
id: 'OHIFDicomSegSopClassHandler', id: 'OHIFDicomSegSopClassHandler',
type: MODULE_TYPES.SOP_CLASS_HANDLER, type: MODULE_TYPES.SOP_CLASS_HANDLER,
sopClassUIDs, sopClassUIDs,
getDisplaySetFromSeries: function ( getDisplaySetFromSeries: function(
series, series,
study, study,
dicomWebClient, dicomWebClient,
@@ -55,6 +55,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
referencedDisplaySetUID: null, // Assigned when loaded. referencedDisplaySetUID: null, // Assigned when loaded.
labelmapIndex: null, // Assigned when loaded. labelmapIndex: null, // Assigned when loaded.
isLoaded: false, isLoaded: false,
hasOverlapping: false,
SeriesDate, SeriesDate,
SeriesTime, SeriesTime,
SeriesNumber, SeriesNumber,
@@ -62,11 +63,11 @@ export default function getSopClassHandlerModule({ servicesManager }) {
metadata, metadata,
}; };
segDisplaySet.getSourceDisplaySet = function (studies) { segDisplaySet.getSourceDisplaySet = function(studies) {
return getSourceDisplaySet(studies, segDisplaySet); return getSourceDisplaySet(studies, segDisplaySet);
}; };
segDisplaySet.load = async function (referencedDisplaySet, studies) { segDisplaySet.load = async function(referencedDisplaySet, studies) {
segDisplaySet.isLoaded = true; segDisplaySet.isLoaded = true;
const { StudyInstanceUID } = referencedDisplaySet; const { StudyInstanceUID } = referencedDisplaySet;
const segArrayBuffer = await DicomLoaderService.findDicomDataPromise( const segArrayBuffer = await DicomLoaderService.findDicomDataPromise(
@@ -81,7 +82,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
StudyInstanceUID, StudyInstanceUID,
referencedDisplaySet.SeriesInstanceUID referencedDisplaySet.SeriesInstanceUID
); );
return new Promise((resolve, reject) => { return new Promise(async (resolve, reject) => {
let results; let results;
try { try {
results = _parseSeg(segArrayBuffer, imageIds); results = _parseSeg(segArrayBuffer, imageIds);
@@ -90,18 +91,47 @@ export default function getSopClassHandlerModule({ servicesManager }) {
segDisplaySet.loadError = true; segDisplaySet.loadError = true;
reject(error); reject(error);
} }
const { labelmapBufferArray, segMetadata, segmentsOnFrame, segmentsOnFrameArray } = results; const {
labelmapBufferArray,
segMetadata,
segmentsOnFrame,
segmentsOnFrameArray,
} = results;
let labelmapIndex;
if (labelmapBufferArray.length > 1) { if (labelmapBufferArray.length > 1) {
let labelmapIndexes = [];
for (let i = 0; i < labelmapBufferArray.length; ++i) { for (let i = 0; i < labelmapBufferArray.length; ++i) {
loadSegmentation(imageIds, segDisplaySet, labelmapBufferArray[i], segMetadata, segmentsOnFrame, segmentsOnFrameArray[i]); labelmapIndexes.push(
await loadSegmentation(
imageIds,
segDisplaySet,
labelmapBufferArray[i],
segMetadata,
segmentsOnFrame,
segmentsOnFrameArray[i]
)
);
} }
/**
* Since overlapping segmentations have virtual labelmaps,
* originLabelMapIndex is used in the panel to select the correct dropdown value.
*/
segDisplaySet.hasOverlapping = true;
segDisplaySet.originLabelMapIndex = labelmapIndexes[0];
labelmapIndex = labelmapIndexes[0];
console.warn('Overlapping segmentations!');
} else { } else {
loadSegmentation(imageIds, segDisplaySet, labelmapBufferArray[0], segMetadata, segmentsOnFrame, []); labelmapIndex = await loadSegmentation(
imageIds,
segDisplaySet,
labelmapBufferArray[0],
segMetadata,
segmentsOnFrame,
[]
);
} }
segDisplaySet.labelmapIndex = 0; resolve(labelmapIndex);
resolve();
}); });
}; };
@@ -34,7 +34,11 @@ export default async function loadSegmentation(
* Cache each labelmap segments. * Cache each labelmap segments.
* This data is used to determine the active label map when a given segment is activated/clicked. * This data is used to determine the active label map when a given segment is activated/clicked.
*/ */
segDisplaySet.labelmapSegments[labelmapIndex] = labelmapSegments.length ? Array.from(new Set(labelmapSegments.filter(a => !!a).reduce((a, b) => a.concat(b)))) : []; segDisplaySet.labelmapSegments[labelmapIndex] = labelmapSegments.length
? Array.from(
new Set(labelmapSegments.filter(a => !!a).reduce((a, b) => a.concat(b)))
)
: [];
segDisplaySet.labelmapIndex = labelmapIndex; segDisplaySet.labelmapIndex = labelmapIndex;
/* /*
@@ -57,6 +61,8 @@ export default async function loadSegmentation(
console.log('Segmentation loaded.'); console.log('Segmentation loaded.');
const event = new CustomEvent('extensiondicomsegmentationsegloaded'); const event = new CustomEvent('extensiondicomsegmentationsegloaded');
document.dispatchEvent(event); document.dispatchEvent(event);
return labelmapIndex;
} }
function _getNextLabelmapIndex(firstImageId) { function _getNextLabelmapIndex(firstImageId) {
@@ -37,9 +37,14 @@ export default async function setActiveLabelmap(
? brushStackState.activeLabelmapIndex ? brushStackState.activeLabelmapIndex
: undefined; : undefined;
if (displaySet.labelmapIndex === activeLabelmapIndex) { let labelmapIndex =
displaySet.hasOverlapping === true
? displaySet.originLabelMapIndex
: displaySet.labelmapIndex;
if (labelmapIndex === activeLabelmapIndex) {
log.warn(`${activeLabelmapIndex} is already the active labelmap`); log.warn(`${activeLabelmapIndex} is already the active labelmap`);
return displaySet.labelmapIndex; return labelmapIndex;
} }
if (!displaySet.isLoaded) { if (!displaySet.isLoaded) {
@@ -55,13 +60,18 @@ export default async function setActiveLabelmap(
await loadPromise; await loadPromise;
} }
labelmapIndex =
displaySet.hasOverlapping === true
? displaySet.originLabelMapIndex
: displaySet.labelmapIndex;
// This might have just been created, so need to use the non-cached value. // This might have just been created, so need to use the non-cached value.
state = cornerstoneTools.getModule('segmentation').state; state = cornerstoneTools.getModule('segmentation').state;
brushStackState = state.series[firstImageId]; brushStackState = state.series[firstImageId];
brushStackState.activeLabelmapIndex = displaySet.labelmapIndex; brushStackState.activeLabelmapIndex = labelmapIndex;
refreshViewports(); refreshViewports();
callback(); callback();
return displaySet.labelmapIndex; return labelmapIndex;
} }
+24
View File
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.2.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.2...@ohif/extension-dicom-tag-browser@0.2.3) (2021-02-25)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.1...@ohif/extension-dicom-tag-browser@0.2.2) (2021-02-05)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.0...@ohif/extension-dicom-tag-browser@0.2.1) (2021-02-05)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.1.1...@ohif/extension-dicom-tag-browser@0.2.0) (2020-12-10) # [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.1.1...@ohif/extension-dicom-tag-browser@0.2.0) (2020-12-10)
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-tag-browser", "name": "@ohif/extension-dicom-tag-browser",
"version": "0.2.0", "version": "0.2.3",
"description": "OHIF extension for checking DICOM headers.", "description": "OHIF extension for checking DICOM headers.",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -28,7 +28,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^2.6.0", "@ohif/core": "^2.6.0",
"dcmjs": "0.17.0", "dcmjs": "0.18.1",
"react": "^16.8.6" "react": "^16.8.6"
}, },
"dependencies": { "dependencies": {
@@ -3,6 +3,11 @@
margin-left: auto; margin-left: auto;
} }
.dicom-tag-browser-table-wrapper {
height: 500px;
overflow-y: scroll;
}
.dicom-tag-browser-table tr { .dicom-tag-browser-table tr {
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
@@ -11,6 +16,20 @@
white-space: nowrap; white-space: nowrap;
} }
.dicom-tag-browser-content {
overflow: hidden;
width: 100%;
padding-bottom: 50px;
}
.dicom-tag-browser-instance-range .range {
height: 20px;
}
.dicom-tag-browser-instance-range {
padding: 20px 0 20px 0;
}
.dicom-tag-browser-table td.dicom-tag-browser-table-center { .dicom-tag-browser-table td.dicom-tag-browser-table-center {
text-align: center; text-align: center;
} }
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { classes, cornerstone as OHIFCornerstone } from '@ohif/core'; import { classes, cornerstone as OHIFCornerstone } from '@ohif/core';
import { Range } from '@ohif/ui';
import dcmjs from 'dcmjs'; import dcmjs from 'dcmjs';
import DicomBrowserSelect from './DicomBrowserSelect'; import DicomBrowserSelect from './DicomBrowserSelect';
import moment from 'moment'; import moment from 'moment';
@@ -99,26 +100,35 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
let instanceSelectList = null; let instanceSelectList = null;
if (isImageStack) { if (isImageStack) {
const selectedInstanceValue = instanceList[activeInstance];
instanceSelectList = ( instanceSelectList = (
<DicomBrowserSelect <div className="dicom-tag-browser-instance-range">
value={selectedInstanceValue} <Range
formatOptionLabel={DicomBrowserSelectItem} showValue
options={instanceList} step={1}
/> min={1}
max={instanceList.length - 1}
value={activeInstance}
valueRenderer={value => <p>Instance Number: {value}</p>}
onChange={({ target }) => {
const instanceIndex = parseInt(target.value);
setActiveInstance(instanceIndex);
}}
/>
</div>
); );
} }
return ( return (
<div> <div className="dicom-tag-browser-content">
<DicomBrowserSelect <DicomBrowserSelect
value={selectedDisplaySetValue} value={selectedDisplaySetValue}
formatOptionLabel={DicomBrowserSelectItem} formatOptionLabel={DicomBrowserSelectItem}
options={displaySetList} options={displaySetList}
/> />
{instanceSelectList} {instanceSelectList}
<DicomTagTable tags={tags} meta={meta}></DicomTagTable> <div className="dicom-tag-browser-table-wrapper">
<DicomTagTable tags={tags} meta={meta}></DicomTagTable>
</div>
</div> </div>
); );
}; };
@@ -127,30 +137,28 @@ function DicomTagTable({ tags, meta }) {
const rows = getFormattedRowsFromTags(tags, meta); const rows = getFormattedRowsFromTags(tags, meta);
return ( return (
<div> <table className="dicom-tag-browser-table">
<table className="dicom-tag-browser-table"> <tbody>
<tbody> <tr>
<tr> <th className="dicom-tag-browser-table-left">Tag</th>
<th className="dicom-tag-browser-table-left">Tag</th> <th className="dicom-tag-browser-table-left">Value Representation</th>
<th className="dicom-tag-browser-table-left">Value Representation</th> <th className="dicom-tag-browser-table-left">Keyword</th>
<th className="dicom-tag-browser-table-left">Keyword</th> <th className="dicom-tag-browser-table-left">Value</th>
<th className="dicom-tag-browser-table-left">Value</th> </tr>
</tr> {rows.map((row, index) => {
{rows.map((row, index) => { const className = row.className ? row.className : null;
const className = row.className ? row.className : null;
return ( return (
<tr className={className} key={`DICOMTagRow-${index}`}> <tr className={className} key={`DICOMTagRow-${index}`}>
<td>{row[0]}</td> <td>{row[0]}</td>
<td className="dicom-tag-browser-table-center">{row[1]}</td> <td className="dicom-tag-browser-table-center">{row[1]}</td>
<td>{row[2]}</td> <td>{row[2]}</td>
<td>{row[3]}</td> <td>{row[3]}</td>
</tr> </tr>
); );
})} })}
</tbody> </tbody>
</table> </table>
</div>
); );
} }
@@ -183,12 +191,16 @@ function getFormattedRowsFromTags(tags, meta) {
} else { } else {
if (tagInfo.vr === 'xs') { if (tagInfo.vr === 'xs') {
try { try {
const dataset = metadataProvider.getStudyDataset(meta.StudyInstanceUID); const dataset = metadataProvider.getStudyDataset(
meta.StudyInstanceUID
);
const tag = dcmjs.data.Tag.fromPString(tagInfo.tag).toCleanString(); const tag = dcmjs.data.Tag.fromPString(tagInfo.tag).toCleanString();
const originalTagInfo = dataset[tag]; const originalTagInfo = dataset[tag];
tagInfo.vr = originalTagInfo.vr; tagInfo.vr = originalTagInfo.vr;
} catch (error) { } catch (error) {
console.error(`Failed to parse value representation for tag '${tagInfo.keyword}'`); console.error(
`Failed to parse value representation for tag '${tagInfo.keyword}'`
);
} }
} }
@@ -21,7 +21,7 @@ export default function getCommandsModule(servicesManager) {
const { UIModalService } = servicesManager.services; const { UIModalService } = servicesManager.services;
const WrappedDicomTagBrowser = function () { const WrappedDicomTagBrowser = function() {
return ( return (
<DicomTagBrowser <DicomTagBrowser
displaySets={displaySets} displaySets={displaySets}
@@ -34,7 +34,7 @@ export default function getCommandsModule(servicesManager) {
content: WrappedDicomTagBrowser, content: WrappedDicomTagBrowser,
title: `DICOM Tag Browser`, title: `DICOM Tag Browser`,
fullscreen: true, fullscreen: true,
showScrollbar: true noScroll: true,
}); });
}, },
}; };
+43
View File
@@ -3,6 +3,49 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.4...@ohif/extension-vtk@1.10.5) (2021-02-25)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.10.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.3...@ohif/extension-vtk@1.10.4) (2021-02-05)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.10.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.2...@ohif/extension-vtk@1.10.3) (2021-02-05)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.10.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.1...@ohif/extension-vtk@1.10.2) (2021-01-21)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.10.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.0...@ohif/extension-vtk@1.10.1) (2020-12-10)
### Bug Fixes
* panel issues and mpr button (IDC2122-IDC2117) ([32022f5](https://github.com/OHIF/Viewers/commit/32022f51f2d24f53f4c98188980db038a6dfe76c))
# [1.10.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.9.4...@ohif/extension-vtk@1.10.0) (2020-12-10) # [1.10.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.9.4...@ohif/extension-vtk@1.10.0) (2020-12-10)
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-vtk", "name": "@ohif/extension-vtk",
"version": "1.10.0", "version": "1.10.5",
"description": "OHIF extension for VTK.js", "description": "OHIF extension for VTK.js",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -35,7 +35,7 @@
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.20.1", "cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "0.17.0", "dcmjs": "0.18.1",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"i18next": "^17.0.3", "i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1", "i18next-browser-languagedetector": "^3.0.1",
@@ -53,8 +53,8 @@
"react-vtkjs-viewport": "^0.14.2" "react-vtkjs-viewport": "^0.14.2"
}, },
"devDependencies": { "devDependencies": {
"@ohif/core": "^2.12.0", "@ohif/core": "^2.12.4",
"@ohif/ui": "^1.8.0", "@ohif/ui": "^1.8.2",
"cornerstone-tools": "^4.20.1", "cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
+77 -45
View File
@@ -72,7 +72,7 @@ class OHIFVTKViewport extends Component {
}; };
static defaultProps = { static defaultProps = {
onScroll: () => { }, onScroll: () => {},
}; };
static id = 'OHIFVTKViewport'; static id = 'OHIFVTKViewport';
@@ -138,6 +138,8 @@ class OHIFVTKViewport extends Component {
SOPInstanceUID, SOPInstanceUID,
frameIndex frameIndex
) => { ) => {
const { UINotificationService } = this.props.servicesManager.services;
const stack = OHIFVTKViewport.getCornerstoneStack( const stack = OHIFVTKViewport.getCornerstoneStack(
studies, studies,
StudyInstanceUID, StudyInstanceUID,
@@ -159,11 +161,14 @@ class OHIFVTKViewport extends Component {
const { activeLabelmapIndex } = brushStackState; const { activeLabelmapIndex } = brushStackState;
const labelmap3D = brushStackState.labelmaps3D[activeLabelmapIndex]; const labelmap3D = brushStackState.labelmaps3D[activeLabelmapIndex];
if (brushStackState.labelmaps3D.length > 1 && this.props.viewportIndex === 0) { if (
const { UINotificationService } = this.props.servicesManager.services; brushStackState.labelmaps3D.length > 1 &&
this.props.viewportIndex === 0
) {
UINotificationService.show({ UINotificationService.show({
title: 'Overlapping Segmentation Found', title: 'Overlapping Segmentation Found',
message: 'Overlapping segmentations cannot be displayed when in MPR mode', message:
'Overlapping segmentations cannot be displayed when in MPR mode',
type: 'info', type: 'info',
}); });
} }
@@ -305,52 +310,79 @@ class OHIFVTKViewport extends Component {
seriesDescription: displaySet.seriesDescription, seriesDescription: displaySet.seriesDescription,
}; };
const { try {
imageDataObject, const {
labelmapDataObject, imageDataObject,
labelmapColorLUT, labelmapDataObject,
} = this.getViewportData( labelmapColorLUT,
studies, } = this.getViewportData(
StudyInstanceUID, studies,
displaySetInstanceUID, StudyInstanceUID,
SOPInstanceUID, displaySetInstanceUID,
frameIndex SOPInstanceUID,
); frameIndex
);
this.imageDataObject = imageDataObject; this.imageDataObject = imageDataObject;
/* TODO: Not currently used until we have drawing tools in vtkjs. /* TODO: Not currently used until we have drawing tools in vtkjs.
if (!labelmap) { if (!labelmap) {
labelmap = createLabelMapImageData(data); labelmap = createLabelMapImageData(data);
} */ } */
const volumeActor = this.getOrCreateVolume( const volumeActor = this.getOrCreateVolume(
imageDataObject, imageDataObject,
displaySetInstanceUID displaySetInstanceUID
); );
this.setState( this.setState(
{ {
percentComplete: 0, percentComplete: 0,
dataDetails, dataDetails,
}, },
() => { () => {
this.loadProgressively(imageDataObject); this.loadProgressively(imageDataObject);
// TODO: There must be a better way to do this. // TODO: There must be a better way to do this.
// We do this so that if all the data is available the react-vtkjs-viewport // We do this so that if all the data is available the react-vtkjs-viewport
// Will render _something_ before the volumes are set and the volume // Will render _something_ before the volumes are set and the volume
// Construction that happens in react-vtkjs-viewport locks up the CPU. // Construction that happens in react-vtkjs-viewport locks up the CPU.
setTimeout(() => { setTimeout(() => {
this.setState({ this.setState({
volumes: [volumeActor], volumes: [volumeActor],
paintFilterLabelMapImageData: labelmapDataObject, paintFilterLabelMapImageData: labelmapDataObject,
paintFilterBackgroundImageData: imageDataObject.vtkImageData, paintFilterBackgroundImageData: imageDataObject.vtkImageData,
labelmapColorLUT, labelmapColorLUT,
}); });
}, 200); }, 200);
}
);
} catch (error) {
const errorTitle = 'Failed to load 2D MPR';
console.error(errorTitle, error);
const { UINotificationService } = this.props.servicesManager.services;
if (this.props.viewportIndex === 0) {
const message = error.message.includes('buffer')
? 'Dataset is too big to display in MPR'
: error.message;
console.error(errorTitle, error);
UINotificationService.show({
title: errorTitle,
message,
type: 'error',
autoClose: false,
action: {
label: 'Exit 2D MPR',
onClick: ({ close }) => {
// context: 'ACTIVE_VIEWPORT::VTK',
close();
this.props.commandsManager.runCommand('setCornerstoneLayout');
},
},
});
} }
); this.setState({ isLoaded: true });
}
} }
componentDidMount() { componentDidMount() {
@@ -363,7 +395,7 @@ class OHIFVTKViewport extends Component {
if ( if (
displaySet.displaySetInstanceUID !== displaySet.displaySetInstanceUID !==
prevDisplaySet.displaySetInstanceUID || prevDisplaySet.displaySetInstanceUID ||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID || displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
displaySet.frameIndex !== prevDisplaySet.frameIndex displaySet.frameIndex !== prevDisplaySet.frameIndex
) { ) {
+8 -2
View File
@@ -9,7 +9,9 @@ import { version } from '../package.json';
// import loadLocales from './loadLocales'; // import loadLocales from './loadLocales';
const OHIFVTKViewport = asyncComponent(() => const OHIFVTKViewport = asyncComponent(() =>
retryImport(() => import(/* webpackChunkName: "OHIFVTKViewport" */ './OHIFVTKViewport.js')) retryImport(() =>
import(/* webpackChunkName: "OHIFVTKViewport" */ './OHIFVTKViewport.js')
)
); );
const vtkExtension = { const vtkExtension = {
@@ -21,7 +23,11 @@ const vtkExtension = {
getViewportModule({ commandsManager, servicesManager }) { getViewportModule({ commandsManager, servicesManager }) {
const ExtendedVTKViewport = props => ( const ExtendedVTKViewport = props => (
<OHIFVTKViewport {...props} servicesManager={servicesManager} /> <OHIFVTKViewport
{...props}
servicesManager={servicesManager}
commandsManager={commandsManager}
/>
); );
return withCommandsManager(ExtendedVTKViewport, commandsManager); return withCommandsManager(ExtendedVTKViewport, commandsManager);
}, },
@@ -41,14 +41,17 @@ const _isDisplaySetReconstructable = (viewportSpecificData = {}, activeViewportI
// A better heuristic would be checking 4D tags, e.g. the presence of multiple TemporalPositionIdentifier values. // A better heuristic would be checking 4D tags, e.g. the presence of multiple TemporalPositionIdentifier values.
// However, some studies (e.g. https://github.com/OHIF/Viewers/issues/2113) do not have such tags. // However, some studies (e.g. https://github.com/OHIF/Viewers/issues/2113) do not have such tags.
for (let ii = 0; ii < displaySet.numImageFrames; ++ii){ for (let ii = 0; ii < displaySet.numImageFrames; ++ii) {
const imageIdControl = displaySet.images[ii].getImageId() const image = displaySet.images[ii];
if (!image) continue;
const imageIdControl = image.getImageId()
const instanceMetadataControl = cornerstone.metaData.get('instance', imageIdControl) const instanceMetadataControl = cornerstone.metaData.get('instance', imageIdControl)
if (!instanceMetadataControl || if (!instanceMetadataControl ||
instanceMetadataControl === undefined || instanceMetadataControl === undefined ||
!instanceMetadataControl.ImagePositionPatient || !instanceMetadataControl.ImagePositionPatient ||
instanceMetadataControl.ImagePositionPatient === undefined ) { instanceMetadataControl.ImagePositionPatient === undefined) {
// if ImagePositionPatient is missing, skip the 4D datasets check. // if ImagePositionPatient is missing, skip the 4D datasets check.
// do not return false, because it could be a 3D dataset. // do not return false, because it could be a 3D dataset.
continue; continue;
@@ -58,8 +61,11 @@ const _isDisplaySetReconstructable = (viewportSpecificData = {}, activeViewportI
let yImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[1]; let yImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[1];
let zImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[2]; let zImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[2];
for (let jj = ii + 1; jj < displaySet.numImageFrames; ++jj){ for (let jj = ii + 1; jj < displaySet.numImageFrames; ++jj) {
const imageId = displaySet.images[jj].getImageId() const image = displaySet.images[jj];
if (!image) continue;
const imageId = image.getImageId()
const instanceMetadata = cornerstone.metaData.get('instance', imageId) const instanceMetadata = cornerstone.metaData.get('instance', imageId)
if (!instanceMetadata || if (!instanceMetadata ||
@@ -76,8 +82,8 @@ const _isDisplaySetReconstructable = (viewportSpecificData = {}, activeViewportI
let zImagePositionPatient = instanceMetadata.ImagePositionPatient[2]; let zImagePositionPatient = instanceMetadata.ImagePositionPatient[2];
if (xImagePositionPatientControl === xImagePositionPatient && if (xImagePositionPatientControl === xImagePositionPatient &&
yImagePositionPatientControl === yImagePositionPatient && yImagePositionPatientControl === yImagePositionPatient &&
zImagePositionPatientControl === zImagePositionPatient){ zImagePositionPatientControl === zImagePositionPatient) {
return false; return false;
} }
} }
+32
View File
@@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.12.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.3...@ohif/core@2.12.4) (2021-02-25)
**Note:** Version bump only for package @ohif/core
## [2.12.3](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.2...@ohif/core@2.12.3) (2021-02-05)
**Note:** Version bump only for package @ohif/core
## [2.12.2](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.1...@ohif/core@2.12.2) (2021-02-05)
**Note:** Version bump only for package @ohif/core
## [2.12.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.0...@ohif/core@2.12.1) (2021-01-21)
**Note:** Version bump only for package @ohif/core
# [2.12.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.11.1...@ohif/core@2.12.0) (2020-12-10) # [2.12.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.11.1...@ohif/core@2.12.0) (2020-12-10)
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/core", "name": "@ohif/core",
"version": "2.12.0", "version": "2.12.4",
"description": "Generic business logic for web-based medical imaging applications", "description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team", "author": "OHIF Core Team",
"license": "MIT", "license": "MIT",
@@ -39,7 +39,7 @@
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"ajv": "^6.10.0", "ajv": "^6.10.0",
"dcmjs": "0.17.0", "dcmjs": "0.18.1",
"dicomweb-client": "^0.6.0", "dicomweb-client": "^0.6.0",
"immer": "6.0.2", "immer": "6.0.2",
"isomorphic-base64": "^1.0.2", "isomorphic-base64": "^1.0.2",
@@ -9,7 +9,8 @@
* @property {boolean} [closeButton=true] Should the modal body render the close button. * @property {boolean} [closeButton=true] Should the modal body render the close button.
* @property {string} [title=null] Should the modal render the title independently of the body content. * @property {string} [title=null] Should the modal render the title independently of the body content.
* @property {string} [customClassName=null] The custom class to style the modal. * @property {string} [customClassName=null] The custom class to style the modal.
* @property {boolean} [showScrollbar=null] Show or hide scrollbar. * @property {boolean} [showScrollbar=false] Show or hide scrollbar.
* @property {boolean} [noScroll=false] Disable or not the scrollbar.
*/ */
const name = 'UIModalService'; const name = 'UIModalService';
@@ -29,7 +30,7 @@ const serviceImplementation = {
/** /**
* Show a new UI modal; * Show a new UI modal;
* *
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, onClose, closeButton, title, customClassName, showScrollbar } * @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, onClose, closeButton, title, customClassName, showScrollbar, noScroll }
*/ */
function _show({ function _show({
content = null, content = null,
@@ -41,7 +42,8 @@ function _show({
title = null, title = null,
fullscreen = false, fullscreen = false,
customClassName = null, customClassName = null,
showScrollbar = false showScrollbar = false,
noScroll = false,
}) { }) {
return serviceImplementation._show({ return serviceImplementation._show({
content, content,
@@ -53,7 +55,8 @@ function _show({
title, title,
fullscreen, fullscreen,
customClassName, customClassName,
showScrollbar showScrollbar,
noScroll,
}); });
} }
@@ -8,6 +8,7 @@
* @property {string} [position="bottomRight"] -"topLeft" | "topCenter | "topRight" | "bottomLeft" | "bottomCenter" | "bottomRight" * @property {string} [position="bottomRight"] -"topLeft" | "topCenter | "topRight" | "bottomLeft" | "bottomCenter" | "bottomRight"
* @property {string} [type="info"] - "info" | "error" | "warning" | "success" * @property {string} [type="info"] - "info" | "error" | "warning" | "success"
* @property {boolean} [autoClose=true] * @property {boolean} [autoClose=true]
* @property {object} [action=null]
*/ */
const name = 'UINotificationService'; const name = 'UINotificationService';
@@ -34,7 +35,7 @@ const serviceImplementation = {
* Create and show a new UI notification; returns the * Create and show a new UI notification; returns the
* ID of the created notification. * ID of the created notification.
* *
* @param {Notification} notification { title, message, duration, position, type, autoClose} * @param {Notification} notification { title, message, duration, position, type, autoClose, action}
* @returns {number} id * @returns {number} id
*/ */
function _show({ function _show({
@@ -44,6 +45,7 @@ function _show({
position = 'bottomRight', position = 'bottomRight',
type = 'info', type = 'info',
autoClose = true, autoClose = true,
action = null,
}) { }) {
return serviceImplementation._show({ return serviceImplementation._show({
title, title,
@@ -52,6 +54,7 @@ function _show({
position, position,
type, type,
autoClose, autoClose,
action,
}); });
} }
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.8.2](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.8.1...@ohif/ui@1.8.2) (2021-02-05)
**Note:** Version bump only for package @ohif/ui
## [1.8.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.8.0...@ohif/ui@1.8.1) (2021-01-21)
**Note:** Version bump only for package @ohif/ui
# [1.8.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.7.1...@ohif/ui@1.8.0) (2020-12-10) # [1.8.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.7.1...@ohif/ui@1.8.0) (2020-12-10)
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/ui", "name": "@ohif/ui",
"version": "1.8.0", "version": "1.8.2",
"description": "A set of React components for Medical Imaging Viewers", "description": "A set of React components for Medical Imaging Viewers",
"author": "OHIF Contributors", "author": "OHIF Contributors",
"license": "MIT", "license": "MIT",
@@ -32,6 +32,10 @@
max-height: 90vh; max-height: 90vh;
overflow-y: auto; overflow-y: auto;
&.noScroll &__content {
overflow: hidden;
}
&:not(.visibleScrollbar) &__content &:not(.visibleScrollbar) &__content
overflow-x: hidden; overflow-x: hidden;
scrollbar-width: none; scrollbar-width: none;
@@ -156,19 +156,50 @@
transition: all 300ms ease; transition: all 300ms ease;
} }
.sb-success { .sb-item .sb-action,
.sb-error .sb-action,
.sb-warning .sb-action,
.sb-info .sb-action,
.sb-success .sb-action {
margin-top: 10px;
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-image: none;
border: none;
color: white;
}
.sb-success,
.sb-success .sb-action {
background-color: var(--snackbar-success); background-color: var(--snackbar-success);
} }
.sb-error { .sb-error,
.sb-error .sb-action {
background-color: var(--snackbar-error); background-color: var(--snackbar-error);
} }
.sb-warning { .sb-warning,
.sb-warning .sb-action {
background-color: var(--snackbar-warning); background-color: var(--snackbar-warning);
} }
.sb-info { .sb-info,
.sb-info .sb-action {
background-color: var(--snackbar-info); background-color: var(--snackbar-info);
} }
@@ -1,10 +1,14 @@
import React, { useState, useEffect } from 'react'; import React, { useEffect } from 'react';
const SnackbarItem = ({ options, onClose }) => { const SnackbarItem = ({ options, onClose }) => {
const handleClose = () => { const handleClose = () => {
onClose(options.id); onClose(options.id);
}; };
const handleClick = () => {
options.action.onClick({ ...options, close: handleClose });
};
useEffect(() => { useEffect(() => {
if (options.autoClose) { if (options.autoClose) {
setTimeout(() => { setTimeout(() => {
@@ -24,6 +28,11 @@ const SnackbarItem = ({ options, onClose }) => {
</span> </span>
{options.title && <div className="sb-title">{options.title}</div>} {options.title && <div className="sb-title">{options.title}</div>}
{options.message && <div className="sb-message">{options.message}</div>} {options.message && <div className="sb-message">{options.message}</div>}
{options.action && (
<button className="sb-action" onClick={handleClick}>
{options.action.label}
</button>
)}
</div> </div>
); );
}; };
@@ -82,14 +82,20 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
shouldCloseOnEsc, shouldCloseOnEsc,
fullscreen, fullscreen,
closeButton, closeButton,
showScrollbar showScrollbar,
noScroll,
} = options; } = options;
return ( return (
<Provider value={{ show, hide }}> <Provider value={{ show, hide }}>
{ModalContent && ( {ModalContent && (
<Modal <Modal
className={classNames(customClassName, ModalContent.className, { 'visibleScrollbar': showScrollbar })} className={classNames(
customClassName,
ModalContent.className,
{ visibleScrollbar: showScrollbar },
{ noScroll }
)}
shouldCloseOnEsc={shouldCloseOnEsc} shouldCloseOnEsc={shouldCloseOnEsc}
isOpen={isOpen} isOpen={isOpen}
title={title} title={title}
@@ -22,6 +22,7 @@ const SnackbarProvider = ({ children, service }) => {
autoClose: true, autoClose: true,
position: 'bottomRight', position: 'bottomRight',
type: SnackbarTypes.INFO, type: SnackbarTypes.INFO,
action: null,
}; };
const [count, setCount] = useState(1); const [count, setCount] = useState(1);
+8 -1
View File
@@ -28,7 +28,13 @@ class Range extends Component {
className="range" className="range"
/> />
{this.props.showPercentage && <span>{`${this.state.value}%`}</span>} {this.props.showPercentage && <span>{`${this.state.value}%`}</span>}
{this.props.showValue && <span>{this.state.value}</span>} {this.props.showValue && (
<span>
{this.props.valueRenderer
? this.props.valueRenderer(this.state.value)
: this.state.value}
</span>
)}
</> </>
); );
} }
@@ -40,6 +46,7 @@ Range.propTypes = {
max: PropTypes.number.isRequired, max: PropTypes.number.isRequired,
step: PropTypes.number, step: PropTypes.number,
id: PropTypes.string, id: PropTypes.string,
valueRenderer: PropTypes.func,
onChange: PropTypes.func, onChange: PropTypes.func,
showPercentage: PropTypes.bool, showPercentage: PropTypes.bool,
showValue: PropTypes.bool, showValue: PropTypes.bool,
+91
View File
@@ -3,6 +3,97 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.8.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.10...@ohif/viewer@4.8.11) (2021-02-25)
**Note:** Version bump only for package @ohif/viewer
## [4.8.10](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.9...@ohif/viewer@4.8.10) (2021-02-08)
**Note:** Version bump only for package @ohif/viewer
## [4.8.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.8...@ohif/viewer@4.8.9) (2021-02-05)
**Note:** Version bump only for package @ohif/viewer
## [4.8.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.7...@ohif/viewer@4.8.8) (2021-02-05)
**Note:** Version bump only for package @ohif/viewer
## [4.8.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.6...@ohif/viewer@4.8.7) (2021-01-28)
### Bug Fixes
* **config:** Update Google Healthcare API endpoint from v1beta1 to v1 ([#2256](https://github.com/OHIF/Viewers/issues/2256)) ([83b7d9e](https://github.com/OHIF/Viewers/commit/83b7d9e8b4240a928eb43735280000e97f186d90))
## [4.8.6](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.5...@ohif/viewer@4.8.6) (2021-01-26)
**Note:** Version bump only for package @ohif/viewer
## [4.8.5](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.4...@ohif/viewer@4.8.5) (2021-01-21)
**Note:** Version bump only for package @ohif/viewer
## [4.8.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.3...@ohif/viewer@4.8.4) (2021-01-21)
**Note:** Version bump only for package @ohif/viewer
## [4.8.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.2...@ohif/viewer@4.8.3) (2021-01-21)
**Note:** Version bump only for package @ohif/viewer
## [4.8.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.1...@ohif/viewer@4.8.2) (2021-01-21)
**Note:** Version bump only for package @ohif/viewer
## [4.8.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.0...@ohif/viewer@4.8.1) (2020-12-10)
**Note:** Version bump only for package @ohif/viewer
# [4.8.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.7.2...@ohif/viewer@4.8.0) (2020-12-10) # [4.8.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.7.2...@ohif/viewer@4.8.0) (2020-12-10)
+10 -10
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/viewer", "name": "@ohif/viewer",
"version": "4.8.0", "version": "4.8.11",
"description": "OHIF Viewer", "description": "OHIF Viewer",
"author": "OHIF Contributors", "author": "OHIF Contributors",
"license": "MIT", "license": "MIT",
@@ -49,18 +49,18 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"@ohif/core": "^2.12.0", "@ohif/core": "^2.12.4",
"@ohif/extension-cornerstone": "^2.10.0", "@ohif/extension-cornerstone": "^2.10.2",
"@ohif/extension-debugging": "^0.1.6", "@ohif/extension-debugging": "^0.1.6",
"@ohif/extension-dicom-html": "^1.3.0", "@ohif/extension-dicom-html": "^1.3.2",
"@ohif/extension-dicom-microscopy": "^0.51.3", "@ohif/extension-dicom-microscopy": "^0.51.4",
"@ohif/extension-dicom-pdf": "^1.0.6", "@ohif/extension-dicom-pdf": "^1.0.6",
"@ohif/extension-dicom-rt": "^0.6.0", "@ohif/extension-dicom-rt": "^0.6.5",
"@ohif/extension-dicom-segmentation": "^0.5.0", "@ohif/extension-dicom-segmentation": "^0.5.3",
"@ohif/extension-lesion-tracker": "^0.2.1", "@ohif/extension-lesion-tracker": "^0.2.1",
"@ohif/extension-vtk": "^1.10.0", "@ohif/extension-vtk": "^1.10.5",
"@ohif/i18n": "^0.53.0", "@ohif/i18n": "^0.53.0",
"@ohif/ui": "^1.8.0", "@ohif/ui": "^1.8.2",
"@tanem/react-nprogress": "^1.1.25", "@tanem/react-nprogress": "^1.1.25",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"core-js": "^3.2.1", "core-js": "^3.2.1",
@@ -68,7 +68,7 @@
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "^4.20.1", "cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "0.17.0", "dcmjs": "0.18.1",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"dicomweb-client": "^0.4.4", "dicomweb-client": "^0.4.4",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
+1 -1
View File
@@ -1,7 +1,7 @@
window.config = { window.config = {
routerBasename: '/', routerBasename: '/',
enableGoogleCloudAdapter: true, enableGoogleCloudAdapter: true,
healthcareApiEndpoint: 'https://healthcare.googleapis.com/v1beta1', healthcareApiEndpoint: 'https://healthcare.googleapis.com/v1',
servers: { servers: {
// This is an array, but we'll only use the first entry for now // This is an array, but we'll only use the first entry for now
dicomWeb: [], dicomWeb: [],
@@ -86,7 +86,7 @@ class ToolbarRow extends Component {
// Note: This does not cleanly handle `studies` prop updating with panel open // Note: This does not cleanly handle `studies` prop updating with panel open
const isDisabled = const isDisabled =
typeof menuOption.isDisabled === 'function' && typeof menuOption.isDisabled === 'function' &&
menuOption.isDisabled(this.props.studies); menuOption.isDisabled(this.props.studies, this.props.activeViewport);
if (hasActiveContext && !isDisabled) { if (hasActiveContext && !isDisabled) {
const menuOptionEntry = { const menuOptionEntry = {
@@ -114,25 +114,30 @@ class ToolbarRow extends Component {
prevProps.activeContexts !== this.props.activeContexts; prevProps.activeContexts !== this.props.activeContexts;
const prevStudies = prevProps.studies; const prevStudies = prevProps.studies;
const prevActiveViewport = prevProps.activeViewport;
const activeViewport = this.props.activeViewport;
const studies = this.props.studies; const studies = this.props.studies;
const seriesPerStudyCount = this.seriesPerStudyCount; const seriesPerStudyCount = this.seriesPerStudyCount;
let studiesUpdated = false; let shouldUpdate = false;
if (prevStudies.length !== studies.length) { if (
studiesUpdated = true; prevStudies.length !== studies.length ||
prevActiveViewport !== activeViewport
) {
shouldUpdate = true;
} else { } else {
for (let i = 0; i < studies.length; i++) { for (let i = 0; i < studies.length; i++) {
if (studies[i].series.length !== seriesPerStudyCount[i]) { if (studies[i].series.length !== seriesPerStudyCount[i]) {
seriesPerStudyCount[i] = studies[i].series.length; seriesPerStudyCount[i] = studies[i].series.length;
studiesUpdated = true; shouldUpdate = true;
break; break;
} }
} }
} }
if (studiesUpdated) { if (shouldUpdate) {
this.updateButtonGroups(); this.updateButtonGroups();
} }
@@ -268,6 +268,9 @@ class Viewer extends Component {
{/* TOOLBAR */} {/* TOOLBAR */}
<ErrorBoundaryDialog context="ToolbarRow"> <ErrorBoundaryDialog context="ToolbarRow">
<ToolbarRow <ToolbarRow
activeViewport={
this.props.viewports[this.props.activeViewportIndex]
}
isLeftSidePanelOpen={this.state.isLeftSidePanelOpen} isLeftSidePanelOpen={this.state.isLeftSidePanelOpen}
isRightSidePanelOpen={this.state.isRightSidePanelOpen} isRightSidePanelOpen={this.state.isRightSidePanelOpen}
selectedLeftSidePanel={ selectedLeftSidePanel={
@@ -320,11 +323,11 @@ class Viewer extends Component {
activeIndex={this.props.activeViewportIndex} activeIndex={this.props.activeViewportIndex}
/> />
) : ( ) : (
<ConnectedStudyBrowser <ConnectedStudyBrowser
studies={this.state.thumbnails} studies={this.state.thumbnails}
studyMetadata={this.props.studies} studyMetadata={this.props.studies}
/> />
)} )}
</SidePanel> </SidePanel>
</ErrorBoundaryDialog> </ErrorBoundaryDialog>
@@ -347,7 +350,9 @@ class Viewer extends Component {
viewports={this.props.viewports} viewports={this.props.viewports}
studies={this.props.studies} studies={this.props.studies}
activeIndex={this.props.activeViewportIndex} activeIndex={this.props.activeViewportIndex}
activeViewport={this.props.viewports[this.props.activeViewportIndex]} activeViewport={
this.props.viewports[this.props.activeViewportIndex]
}
getActiveViewport={this._getActiveViewport} getActiveViewport={this._getActiveViewport}
/> />
)} )}
@@ -371,7 +376,7 @@ export default withDialog(Viewer);
* @param {Study[]} studies * @param {Study[]} studies
* @param {DisplaySet[]} studies[].displaySets * @param {DisplaySet[]} studies[].displaySets
*/ */
const _mapStudiesToThumbnails = function (studies) { const _mapStudiesToThumbnails = function(studies) {
return studies.map(study => { return studies.map(study => {
const { StudyInstanceUID } = study; const { StudyInstanceUID } = study;
@@ -5,10 +5,11 @@ import usePrevious from '../customHooks/usePrevious';
import ConnectedViewer from './ConnectedViewer.js'; import ConnectedViewer from './ConnectedViewer.js';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { extensionManager } from './../App.js'; import { extensionManager } from './../App.js';
import { useSnackbarContext } from '@ohif/ui'; import { useSnackbarContext, ErrorPage } from '@ohif/ui';
// Contexts // Contexts
import AppContext from '../context/AppContext'; import AppContext from '../context/AppContext';
import NotFound from '../routes/NotFound';
const { OHIFStudyMetadata, OHIFSeriesMetadata } = metadata; const { OHIFStudyMetadata, OHIFSeriesMetadata } = metadata;
const { retrieveStudiesMetadata, deleteStudyMetadataPromise } = studies; const { retrieveStudiesMetadata, deleteStudyMetadataPromise } = studies;
@@ -291,7 +292,7 @@ function ViewerRetrieveStudyData({
}) })
.catch(error => { .catch(error => {
if (error && !error.isCanceled) { if (error && !error.isCanceled) {
setError(true); setError(error);
log.error(error); log.error(error);
} }
}); });
@@ -358,13 +359,13 @@ function ViewerRetrieveStudyData({
}) })
.catch(error => { .catch(error => {
if (error && !error.isCanceled) { if (error && !error.isCanceled) {
setError(true); setError(error);
log.error(error); log.error(error);
} }
}); });
} catch (error) { } catch (error) {
if (error) { if (error) {
setError(true); setError(error);
log.error(error); log.error(error);
} }
} }
@@ -411,7 +412,12 @@ function ViewerRetrieveStudyData({
}, []); }, []);
if (error) { if (error) {
return <div>Error: {JSON.stringify(error)}</div>; const content = JSON.stringify(error);
if (content.includes('404') || content.includes('NOT_FOUND')) {
return <NotFound />;
}
return <NotFound message="Failed to retrieve study data" />;
} }
return ( return (
@@ -336,12 +336,6 @@ async function getStudyList(
} = filters; } = filters;
const sortFieldName = sort.fieldName || 'PatientName'; const sortFieldName = sort.fieldName || 'PatientName';
const sortDirection = sort.direction || 'desc'; const sortDirection = sort.direction || 'desc';
const studyDateFrom =
filters.studyDateFrom ||
moment()
.subtract(25000, 'days')
.toDate();
const studyDateTo = filters.studyDateTo || new Date();
const mappedFilters = { const mappedFilters = {
PatientID: filters.PatientID, PatientID: filters.PatientID,
@@ -350,8 +344,8 @@ async function getStudyList(
StudyDescription: filters.StudyDescription, StudyDescription: filters.StudyDescription,
ModalitiesInStudy: filters.modalities, ModalitiesInStudy: filters.modalities,
// NEVER CHANGE // NEVER CHANGE
studyDateFrom, studyDateFrom: filters.studyDateFrom,
studyDateTo, studyDateTo: filters.studyDateTo,
limit: rowsPerPage, limit: rowsPerPage,
offset: pageNumber * rowsPerPage, offset: pageNumber * rowsPerPage,
fuzzymatching: server.supportsFuzzyMatching === true, fuzzymatching: server.supportsFuzzyMatching === true,
+4 -4
View File
@@ -6619,10 +6619,10 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
dcmjs@0.17.0: dcmjs@0.18.1:
version "0.17.0" version "0.18.1"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.17.0.tgz#dbfd6db3efd2936f9bead9c902b7db396f64e9f5" resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.18.1.tgz#5323ecb2227b0a956d6ba5df40c8cf775100ad0e"
integrity sha512-7JnNqNWXqjGyspLRwKM4nn+TU/+w3iMl9boU4eHrr831M4crMWWIiSQDejpOnfDT9+opqyeaYcNkiekr7p6TFg== integrity sha512-euySbMZW/WHOXfMjf9+QYg0X8691OMGfhMtEI0D1AebuwZZXx3IaDFfc6xcyA7kBti/sU3SFuGIEhxE+9AM97w==
dependencies: dependencies:
"@babel/polyfill" "^7.8.3" "@babel/polyfill" "^7.8.3"
"@babel/runtime" "^7.8.4" "@babel/runtime" "^7.8.4"