Compare commits

..
Author SHA1 Message Date
ohif-bot 5b0b6301b9 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.2
 - @ohif/extension-dicom-html@1.3.13
 - @ohif/extension-dicom-rt@0.7.3
 - @ohif/extension-dicom-segmentation@0.7.3
 - @ohif/extension-dicom-tag-browser@0.2.13
 - @ohif/extension-vtk@1.12.5
 - @ohif/core@2.16.1
 - @ohif/ui@1.10.4
 - @ohif/viewer@4.12.4
2021-10-26 08:23:03 +00:00
Igor OctavianoandDavide Punzo 03a34a75a1 Add checks for study metadata in rt and seg panels + bump dcmjs to fix circular references issues naturalizing datasets (#2588)
Co-authored-by: Davide Punzo <punzodavide@hotmail.it>
2021-10-26 10:19:12 +02:00
ohif-bot 729272a985 chore(release): publish [skip ci]
- @ohif/extension-dicom-html@1.3.12
 - @ohif/viewer@4.12.3
2021-10-25 15:11:00 +00:00
Bill Wallace 9194ec2404 fix(OHIF):The header depth was too deep for display SR files. (#2594) 2021-10-25 17:08:10 +02:00
ohif-bot 52c95a1209 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.4
 - @ohif/ui@1.10.3
 - @ohif/viewer@4.12.2
2021-10-15 11:37:07 +00:00
Davide Punzo 8903adaea7 Re #2571: thumbnail footer remove instance number and add link icon if any derived display set is present (#2582)
* Re #2571, thumbnail footer layout modifications: remove instance number and add link icon if any derived display set is present.
2021-10-15 13:29:36 +02:00
ohif-bot 9481e2a99e chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.7.2
 - @ohif/extension-dicom-segmentation@0.7.2
 - @ohif/viewer@4.12.1
2021-10-06 21:01:00 +00:00
Igor Octaviano f783fd7650 Merge pull request #2575 from OHIF/fix/ohif-2570
OHIF-2570: Update badge number based on active ds
2021-10-06 17:53:07 -03:00
Igor Octaviano 79afee7615 Merge branch 'master' into fix/ohif-2570 2021-10-05 09:40:49 -03:00
ohif-bot 539141fe5a chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.3
 - @ohif/core@2.16.0
 - @ohif/viewer@4.12.0
2021-10-04 07:36:45 +00:00
Bill Wallace a046c6c901 feat: Added support for WADO servers built with static hosting (e.g. S3) (#2566) 2021-10-04 09:30:36 +02:00
igoroctaviano 073be065cf Update badge number based on active ds 2021-10-01 18:09:48 -03:00
ohif-bot 252536a1e6 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.1
 - @ohif/extension-dicom-html@1.3.11
 - @ohif/extension-dicom-rt@0.7.1
 - @ohif/extension-dicom-segmentation@0.7.1
 - @ohif/extension-dicom-tag-browser@0.2.12
 - @ohif/extension-vtk@1.12.2
 - @ohif/core@2.15.1
 - @ohif/viewer@4.11.2
2021-09-28 12:14:17 +00:00
Davide Punzo 53b1d71d56 fix regression not showing warnings list on thumbnail for segmentations (#2563)
* fix regression not showing warnings list on thumbnail for segmentations

* update dcmjs version
2021-09-28 14:07:29 +02:00
Erik Ziegler 702d99cb93 Add FUNDING.yml 2021-09-27 14:23:37 +02:00
Alireza bb7b78bf33 chore(github): Create config.yml for issues (#2561)
* chore(github): Create config.yml for issues

* Delete ---support-question.md
2021-09-27 14:20:38 +02:00
ohif-bot b796fc9c62 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.1
 - @ohif/ui@1.10.2
 - @ohif/viewer@4.11.1
2021-09-24 11:04:33 +00:00
Erik Ziegler 66c587b139 fix: throttle thumbnail progress updates (#2557) 2021-09-24 12:58:30 +02:00
ohif-bot f6e2e58aad chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.0
 - @ohif/extension-debugging@0.3.0
 - @ohif/extension-dicom-pdf@1.1.0
 - @ohif/extension-dicom-rt@0.7.0
 - @ohif/extension-dicom-segmentation@0.7.0
 - @ohif/extension-vtk@1.12.0
 - @ohif/core@2.15.0
 - @ohif/viewer@4.11.0
2021-09-23 16:42:56 +00:00
Erik ZieglerandAlireza 489d4b0584 feat: Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 (#2537)
* feat: bump to prerelease of WADO Image Loader for testing

* switch to released versions

* update react-vtkjs-viewport and fix requestpool usage in StudyPrefetcher

* attempt to fix e2e tests for script-tag usage

* fix e2e test error

* switch to using webpack alias in PWA to use dynamic import version of OHIF

* fix copy plugin usage

* try to fix publicpath issue

* bump to 4.0.3

Co-authored-by: Alireza <ar.sedghi@gmail.com>
2021-09-23 18:37:29 +02:00
ohif-bot 36019156da chore(release): publish [skip ci]
- @ohif/extension-vtk@1.11.18
 - @ohif/core@2.14.1
 - @ohif/ui@1.10.1
 - @ohif/viewer@4.10.1
2021-09-13 13:10:07 +00:00
Igor Octaviano f2a371fc2d Display warning messages if fails to parse sr report (#2543) 2021-09-13 15:06:30 +02:00
ohif-bot ad2f038817 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.11.0
 - @ohif/extension-vtk@1.11.17
 - @ohif/core@2.14.0
 - @ohif/ui@1.10.0
 - @ohif/viewer@4.10.0
2021-09-09 08:02:24 +00:00
Igor OctavianoandAlireza 8f2604509d feat: OHIF-1000: Study prefetcher (#2466)
* Enable study prefetcher and expose configs

* Fix progress for all images

* Enable study loading listener

* Add new default for displaying progress bar for series

* Use events instead of redux to improve performance

* Add documentation, resolve issues with different datasets

* CR Updates

* CR Updates

* CR Updates & Resolve issue when activating both stack and study prefetch

* Add upward and update display set logic

* Add new behavior to prefetching orders

* Rollback display set check and activate cache check

* Remove prefetch check

* Check if displayset has images

* Debounce check cache events

* Remove usued debounce flags

* Ignore prefetching same image

* Use new image instead of image rendered

* Continue prefetch after cache check

* Update prefetch logic after checking cache

* Remove log

* Update displayset count default to 3

* Update netlify config

Co-authored-by: Alireza <ar.sedghi@gmail.com>
2021-09-09 09:58:24 +02:00
ohif-bot ea0963d75f chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.6.14
 - @ohif/extension-dicom-segmentation@0.6.11
 - @ohif/extension-vtk@1.11.16
 - @ohif/core@2.13.15
 - @ohif/ui@1.9.3
 - @ohif/viewer@4.9.24
2021-09-03 21:01:01 +00:00
Igor Octaviano 46d5718011 Merge pull request #2491 from OHIF/feat/ohif-2482
OHIF-2482: Add badge numbers for seg and rtstruct
2021-09-03 17:57:42 -03:00
Davide Punzo f8740e04f3 Merge branch 'master' into feat/ohif-2482 2021-09-02 16:08:37 +02:00
ohif-bot 669717d27b chore(release): publish [skip ci]
- @ohif/extension-vtk@1.11.15
 - @ohif/core@2.13.14
 - @ohif/viewer@4.9.23
2021-08-30 13:04:58 +00:00
Alireza 715dfc556a fix: MPR metadata parsing for spacings (#2517)
* fix: MPR metadata parsing for spacings

* fix: OHIF Metadata provider for validNumbers for imagePlaneModule
2021-08-30 09:01:46 -04:00
ohif-bot cafb7d3f7b chore(release): publish [skip ci]
- @ohif/viewer@4.9.22
2021-08-19 13:05:04 +00:00
matthiasg 5f92f6ec7f fix: Remove internet dependency for workbox (addressing #2109) (#2151)
* Ignoring *.js.map files as generated in the extensions directories for example

* Fix for #2109. Removed internet dependency to google workbox
2021-08-19 15:00:20 +02:00
Matthias SchmiederandMatthias Schmieder eea610dfe8 fix CVE-2019-5021 caused by nginx-apline (#2514)
Co-authored-by: Matthias Schmieder <matthias.schmieder@stryker.com>
2021-08-19 13:42:04 +02:00
Erik Ziegler e8733a9bd9 Update CITATION.cff (#2504) 2021-08-04 17:16:35 +02:00
Erik Ziegler d292931073 Create CITATION.cff (#2503) 2021-08-04 15:43:10 +02:00
igoroctaviano f6a0db753a Merge branch 'master' of github.com:OHIF/Viewers into feat/ohif-2482 2021-07-29 09:19:42 -03:00
igoroctaviano 8518e179ee Extract event 2021-07-29 09:18:31 -03:00
igoroctaviano 3f40cdced7 Add badge numbers for seg and rtstruct 2021-07-28 22:15:05 -03:00
ohif-bot 4cf17185da chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.10.10
 - @ohif/viewer@4.9.21
2021-07-26 11:44:06 +00:00
b8ae92c2d4 fix: changed new image handler conditional for 0 edge case (#2338)
Co-authored-by: Michael Andersen <Michael.Andersen@rmp.uhn.ca>
Co-authored-by: Erik Ziegler <erik.sweed@gmail.com>
2021-07-26 13:35:37 +02:00
Woonchan Cho bad67020cc chore(#2483) make client-side code debuggable right from the source code (#2484) 2021-07-26 13:34:55 +02:00
ohif-bot 2afcd498f3 chore(release): publish [skip ci]
- @ohif/extension-debugging@0.2.2
 - @ohif/extension-dicom-tag-browser@0.2.11
 - @ohif/extension-vtk@1.11.14
 - @ohif/core@2.13.13
 - @ohif/viewer@4.9.20
2021-06-03 20:56:43 +00:00
Igor Octaviano ce4c3d5360 Merge pull request #2408 from OHIF/feat/idc-2374
IDC-2374: Exponential backoff and retry after 500 error
2021-06-03 17:52:49 -03:00
igoroctaviano d575e36879 Expose retryable option 2021-06-03 17:24:39 -03:00
igoroctaviano 98fcabfcb5 Resolve opening connection before previsouly setting headers 2021-06-03 17:05:45 -03:00
Davide Punzo 1e2773345a Merge branch 'feat/idc-2374' of https://github.com/OHIF/Viewers into feat/idc-2374 2021-06-03 16:25:29 +02:00
Davide Punzo f847633cf3 update dicomweb-client to version 0.8.1 2021-06-03 16:25:06 +02:00
igoroctaviano bd2ddd7702 Remove metadata check 2021-06-03 07:20:49 -03:00
igoroctaviano 26b7b34bd4 Add check for metadata 2021-06-03 07:10:33 -03:00
Davide Punzo 6f3e446a79 Merge branch 'master' into feat/idc-2374 2021-06-03 10:11:45 +02:00
ohif-bot f0d7f2e6c7 chore(release): publish [skip ci]
- @ohif/viewer@4.9.19
2021-06-03 08:07:04 +00:00
Davide Punzo c8f7abfd53 Merge branch 'master' into feat/idc-2374 2021-06-03 10:04:02 +02:00
Davide Punzo 2b29ff1820 clean back default config (#2438) 2021-06-03 10:03:49 +02:00
Davide Punzo 5627e9c919 update dicomweb-client to version 0.8.0 2021-06-03 09:37:57 +02:00
Davide Punzo 582b713b84 Merge branch 'master' into feat/idc-2374 2021-06-03 09:25:50 +02:00
ohif-bot 5eb8e58b65 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.10.9
 - @ohif/extension-dicom-html@1.3.10
 - @ohif/extension-dicom-rt@0.6.13
 - @ohif/extension-dicom-segmentation@0.6.10
 - @ohif/extension-dicom-tag-browser@0.2.10
 - @ohif/extension-vtk@1.11.13
 - @ohif/core@2.13.12
 - @ohif/viewer@4.9.18
2021-06-02 14:01:32 +00:00
Igor Octaviano 403688b18c fix(dicom-html): Add parsed dicom meta info section (#2419)
* Add dicom meta info section to dicom sr html
* Update dependency: dcmjs@0.18.8
2021-06-02 15:57:56 +02:00
ohif-bot f0d13a7acd chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.10.8
 - @ohif/extension-dicom-rt@0.6.12
 - @ohif/extension-dicom-segmentation@0.6.9
 - @ohif/extension-vtk@1.11.12
 - @ohif/core@2.13.11
 - @ohif/ui@1.9.2
 - @ohif/viewer@4.9.17
2021-05-17 16:51:58 +00:00
Davide Punzo 78f1a7f3e5 Re #2259: UI refinements for warnings (#2397)
* Re #2259: highlight series thumbnails border for active series (series in the active viewport)
* Re #2259: add the warning icon also on the active viewport as an overlay
2021-05-17 18:48:32 +02:00
ohif-bot a57ab4d12a chore(release): publish [skip ci]
- @ohif/viewer@4.9.16
2021-05-14 01:18:42 +00:00
Igor Octaviano 3b6c6374e3 Merge pull request #2409 from OHIF/feat/add-option-to-disable-servers-cache
IDC: Add option to disable local storage of servers
2021-05-13 22:15:59 -03:00
igoroctaviano 4b508b0c25 Update config property 2021-05-13 21:56:19 -03:00
igoroctaviano 1cbef867ce Add option to disable local storage cache of servers 2021-05-13 21:42:46 -03:00
igoroctaviano 05444f48d4 Improve docs remove env variables 2021-05-13 09:30:52 -03:00
igoroctaviano 2ea227ca06 Extract hook and add config and documentation 2021-05-13 09:06:11 -03:00
igoroctaviano 35aaad175c Update naming 2021-05-13 08:11:09 -03:00
igoroctaviano 863fe05907 Update naming 2021-05-13 08:11:03 -03:00
igoroctaviano 1c9560044f Implement handlers to retry metadata requests 2021-05-13 08:10:53 -03:00
ohif-bot 343f06c57d chore(release): publish [skip ci]
- @ohif/extension-dicom-html@1.3.9
 - @ohif/extension-dicom-rt@0.6.11
 - @ohif/extension-dicom-segmentation@0.6.8
 - @ohif/extension-dicom-tag-browser@0.2.9
 - @ohif/extension-vtk@1.11.11
 - @ohif/core@2.13.10
 - @ohif/viewer@4.9.15
2021-04-22 03:42:37 +00:00
dannyrb e2bb647386 Merge branch 'master' of github.com:OHIF/Viewers into ups-master 2021-04-21 23:29:54 -04:00
dannyrb a3cb9dab57 remove sitemap dep from older version 2021-04-21 23:29:41 -04:00
ohif-bot 410ab028c7 chore(release): publish [skip ci]
- @ohif/extension-dicom-html@1.3.8
 - @ohif/extension-dicom-rt@0.6.10
 - @ohif/extension-dicom-segmentation@0.6.7
 - @ohif/extension-dicom-tag-browser@0.2.8
 - @ohif/extension-vtk@1.11.10
 - @ohif/core@2.13.9
 - @ohif/viewer@4.9.14
2021-04-22 03:27:52 +00:00
dannyrb 54dd738f61 Merge branch 'master' of github.com:OHIF/Viewers into ups-master 2021-04-21 23:24:39 -04:00
dannyrb 6f16c7e48d Remove broken plugin? Seems to have broken child dep 2021-04-21 23:24:28 -04:00
ohif-bot 82ddd95cf0 chore(release): publish [skip ci]
- @ohif/extension-dicom-html@1.3.7
 - @ohif/extension-dicom-rt@0.6.9
 - @ohif/extension-dicom-segmentation@0.6.6
 - @ohif/extension-dicom-tag-browser@0.2.7
 - @ohif/extension-vtk@1.11.9
 - @ohif/core@2.13.8
 - @ohif/viewer@4.9.13
2021-04-21 14:14:00 +00:00
Davide Punzo 2b658fae7e IDC2345: update dcmjs to version 0.18.5 (#2378) 2021-04-21 16:10:49 +02:00
ohif-bot a822151e92 chore(release): publish [skip ci]
- @ohif/extension-dicom-html@1.3.6
 - @ohif/extension-vtk@1.11.8
 - @ohif/core@2.13.7
 - @ohif/viewer@4.9.12
2021-04-21 11:17:09 +00:00
Igor Octaviano 0ae49c6fdc IDC-2348: Add acquisition storage sr sopclass to sr html ext (#2352)
* Add acquisition context storage sr sop to html ext
2021-04-21 13:12:54 +02:00
ohif-bot 1d069d2da3 chore(release): publish [skip ci]
- @ohif/extension-dicom-segmentation@0.6.5
 - @ohif/extension-vtk@1.11.7
 - @ohif/core@2.13.6
 - @ohif/viewer@4.9.11
2021-04-21 10:22:55 +00:00
Igor Octaviano 274342a5e1 Merge pull request #2369 from Punzo/IDC2361
Re #2361: fix seg combobox filling
2021-04-21 07:19:32 -03:00
Davide Punzo 21a5d1bfc4 update 2021-04-21 10:40:02 +02:00
Davide Punzo e421bc7df9 update 2021-04-20 16:39:04 +02:00
Davide Punzo e41ba2bfde IDC2361: fix seg combobox filling 2021-04-19 19:42:30 +02:00
ohif-bot d2e75b0f20 chore(release): publish [skip ci]
- @ohif/extension-dicom-segmentation@0.6.4
 - @ohif/extension-vtk@1.11.6
 - @ohif/core@2.13.5
 - @ohif/viewer@4.9.10
2021-04-16 18:18:27 +00:00
Davide Punzo 07b272e024 IDC2345: implement thumbnails footnotes UI for segmentation series inconsistencies warning (#2364)
* IDC2345: implement thumbnails footnotes UI for segmentation series inconsistencies warning
2021-04-16 20:14:04 +02:00
ohif-bot a08caa9bdb chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.10.7
 - @ohif/extension-dicom-html@1.3.5
 - @ohif/extension-dicom-rt@0.6.8
 - @ohif/extension-dicom-segmentation@0.6.3
 - @ohif/extension-dicom-tag-browser@0.2.6
 - @ohif/extension-vtk@1.11.5
 - @ohif/core@2.13.4
 - @ohif/viewer@4.9.9
2021-04-15 13:13:50 +00:00
Igor Octaviano 29afddb3a6 Merge pull request #2363 from OHIF/fix/window-level-issues
fix(MetadataProvider): Update metadata provider to resolve issues after dicomweb update/new return value
2021-04-15 10:10:48 -03:00
igoroctaviano f85f856734 Comment cache 2021-04-15 09:40:37 -03:00
igoroctaviano c6bc19c002 Rollback circle ci test 2021-04-15 09:29:50 -03:00
igoroctaviano 538555f000 Merge branch 'master' of github.com:OHIF/Viewers into fix/window-level-issues 2021-04-15 09:17:54 -03:00
igoroctaviano 8681036b21 fix(wl): fix metadata provider window level issues after dicomweb update (string vs number) 2021-04-15 09:15:42 -03:00
Davide Punzo e55b909ca6 IDC2345: Update dcmjs to version 0.18.4 (#2362) 2021-04-15 13:33:13 +02:00
Davide Punzo 4bb36704ee IDC2346: fix images sorting (#2358) 2021-04-14 19:43:58 +02:00
Davide Punzo 9bbdb35d88 IDC2259: do not check uniform spacing in the 4th dimension for 4D datasets (#2356) 2021-04-13 12:57:28 +02:00
qqalexqq 2f443fa2ef fix: Update Dockerfile node version - fix missing dependencies (#2349)
There were some utilities missing: python, make, g++. Also node version was too old - current node builds without problems.
2021-04-05 09:47:57 +02:00
ohif-bot 6d7d7da287 chore(release): publish [skip ci]
- @ohif/extension-dicom-segmentation@0.6.2
 - @ohif/viewer@4.9.8
2021-03-31 10:24:08 +00:00
Davide Punzo 1f6751f95b IDC2332 (#2341)
1) fix the loading of the segmentations when filtering study with ?seriesInstanceUID= syntax
2) add time in the dates of the items of the segmentation combobox list
3) order segmentation combobox list in reverse time order
2021-03-31 12:20:34 +02:00
ohif-bot 92081a097d chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.10.6
 - @ohif/viewer@4.9.7
2021-03-26 18:38:58 +00:00
Igor Octaviano ba1a33c3ac Merge pull request #2335 from OHIF/idc-2334
IDC-2334: Resolve choppy scrolling in 2D mode
2021-03-26 15:36:07 -03:00
igoroctaviano 193ff12b6e Fix scroll speed 2021-03-26 13:27:51 -03:00
ohif-bot 3296ffaa07 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.11.4
 - @ohif/core@2.13.3
 - @ohif/ui@1.9.1
 - @ohif/viewer@4.9.6
2021-03-26 10:35:50 +00:00
Davide Punzo 0456d92462 IDC 2259: add warning UI for series inconsistencies (#2331)
IDC 2259: add warning UI for series inconsistencies

remove VTKMPRToolbarButton component memoization (it is not anymore necessary)
2021-03-26 11:32:10 +01:00
ohif-bot db3b15e537 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.11.3
 - @ohif/core@2.13.2
 - @ohif/viewer@4.9.5
2021-03-25 16:16:14 +00:00
Igor OctavianoandDavide Punzo 6e723ebced fix: IDC-2288: Update toolbar tabs after derived displayset loaded (#2307)
* Update toolbar tabs after derived displayset loaded

* trigger CI

Co-authored-by: Davide Punzo <punzodavide@hotmail.it>
2021-03-25 17:12:20 +01:00
ohif-bot a6a231c81e chore(release): publish [skip ci]
- @ohif/extension-debugging@0.2.1
 - @ohif/viewer@4.9.4
2021-03-19 18:54:21 +00:00
Igor Octaviano 7c2bd9b76a Merge pull request #2321 from OHIF/bugfix/idc-2006
bug(IDC-2006): Fix bug introduced with new logger service which broke debugger mailto functionality
2021-03-19 15:51:01 -03:00
igoroctaviano 5dfa8d465d Force pipeline 2021-03-19 15:12:37 -03:00
igoroctaviano fc5301e904 bug(debugger):Fix bug that breaks mailto introduced by logger service 2021-03-18 07:40:59 -03:00
ohif-bot 88e5a7e54a chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.10.5
 - @ohif/viewer@4.9.3
2021-03-15 20:12:36 +00:00
Igor Octaviano 5d24bb4b73 Merge pull request #2315 from OHIF/igoroctaviano/react-cornerstone-viewport-4.0.5
Update dependency: react-cornerstone-viewport@4.0.5
2021-03-15 17:09:16 -03:00
igoroctaviano 03a4056179 Update dependency: react-cornerstone-viewport@4.0.5
react-cornerstone-viewport
  * @ohif/extension-cornerstone: 4.0.4 → 4.0.5
2021-03-15 16:42:24 -03:00
ohif-bot ec76bc3b0b chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.10.4
 - @ohif/extension-dicom-html@1.3.4
 - @ohif/extension-dicom-rt@0.6.7
 - @ohif/extension-dicom-segmentation@0.6.1
 - @ohif/extension-dicom-tag-browser@0.2.5
 - @ohif/extension-vtk@1.11.2
 - @ohif/core@2.13.1
 - @ohif/viewer@4.9.2
2021-03-09 19:43:21 +00:00
Davide Punzo b1070acaca Update dependency: dcmjs@0.18.3 (#2302) 2021-03-09 20:38:19 +01:00
ohif-bot 7b5beb8f98 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.11.1
 - @ohif/viewer@4.9.1
2021-03-09 15:55:47 +00:00
97410e12b9 Added memoization on VTKMPRToolbarButton component to avoid unnecesary rebuilds (#2274)
* Added memoization on VTKMPRToolbarButton component to avoid unnecessary rebuilds

* update isDisplaySetReconstructable for float precision

Co-authored-by: Rodrigo Cáceres Vicencio <rcaceres@nursoft.cl>
Co-authored-by: Davide Punzo <punzodavide@hotmail.it>
2021-03-09 16:52:56 +01:00
182 changed files with 15611 additions and 912 deletions

No files matched your search

+35 -27
View File
@@ -48,12 +48,12 @@ jobs:
- checkout:
post:
- git fetch --all
- restore_cache:
name: Restore Yarn and Cypress Package Cache
keys:
# when lock file changes, use increasingly general patterns to restore cache
- yarn-packages-{{ checksum "yarn.lock" }}
- yarn-packages-
# - restore_cache:
# name: Restore Yarn and Cypress Package Cache
# keys:
# # when lock file changes, use increasingly general patterns to restore cache
# - yarn-packages-{{ checksum "yarn.lock" }}
# - yarn-packages-
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile
@@ -107,12 +107,12 @@ jobs:
post:
- git fetch --all
- restore_cache:
name: Restore Yarn and Cypress Package Cache
keys:
# when lock file changes, use increasingly general patterns to restore cache
- yarn-packages-{{ checksum "yarn.lock" }}
- yarn-packages-
# - restore_cache:
# name: Restore Yarn and Cypress Package Cache
# keys:
# # when lock file changes, use increasingly general patterns to restore cache
# - yarn-packages-{{ checksum "yarn.lock" }}
# - yarn-packages-
- run:
name: Install Dependencies
@@ -143,12 +143,12 @@ jobs:
- checkout:
post:
- git fetch --all
- restore_cache:
name: Restore Yarn and Cypress Package Cache
keys:
# when lock file changes, use increasingly general patterns to restore cache
- yarn-packages-{{ checksum "yarn.lock" }}
- yarn-packages-
# - restore_cache:
# name: Restore Yarn and Cypress Package Cache
# keys:
# # when lock file changes, use increasingly general patterns to restore cache
# - yarn-packages-{{ checksum "yarn.lock" }}
# - yarn-packages-
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile
@@ -237,11 +237,11 @@ jobs:
post:
- git fetch --all
# Use increasingly general patterns to restore cache
- restore_cache:
name: Restore Yarn and Cypress Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- yarn-packages-
# - restore_cache:
# name: Restore Yarn and Cypress Package Cache
# keys:
# - yarn-packages-{{ checksum "yarn.lock" }}
# - yarn-packages-
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile
@@ -346,7 +346,9 @@ workflows:
record: true
store_artifacts: true
working_directory: platform/viewer
build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build
build:
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
yarn run build
start: yarn run test:e2e:serve
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
wait-on: 'http://localhost:3000'
@@ -372,7 +374,9 @@ workflows:
record: true
store_artifacts: true
working_directory: platform/viewer
build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build:package
build:
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
yarn run build:package
start: yarn run test:e2e:serve
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
wait-on: 'http://localhost:3000'
@@ -403,7 +407,9 @@ workflows:
yarn: true
store_artifacts: false
working_directory: platform/viewer
build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build
build:
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
yarn run build
# start server --> verify running --> percy + chrome + cypress
command: yarn run test:e2e:dist
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
@@ -480,7 +486,9 @@ workflows:
yarn: true
store_artifacts: false
working_directory: platform/viewer
build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build
build:
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
yarn run build
# start server --> verify running --> percy + chrome + cypress
command: yarn run test:e2e:dist
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
+1
View File
@@ -0,0 +1 @@
custom: https://giving.massgeneral.org/ohif
@@ -1,18 +0,0 @@
---
name: "\U0001F917 Support Question"
about: "I have a question \U0001F4AC"
title: ''
labels: 'Community: Question :question:, Triage :white_flag:'
assignees: ''
---
> :hand: We are a small team with limited resources. Your question is much more
> likely to be answered if it is
> [a good question](https://stackoverflow.com/help/how-to-ask)
**Description**
Questions can often be answered by our documentation. Unable to find an answer
in our docs? We'll try to help. In the meantime, if you answer your own
question, please respond with the answer here so that others may benefit as
well. Better yet, open a PR to expand our docs ^\_^
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: 🤗 Support Question
url: https://community.ohif.org/
about: Please use our forum if you have questions or need help.
+5
View File
@@ -9,6 +9,10 @@ src/version.js
junit.xml
coverage/
.docz/
*.js.map
.yarn
platform/docs/.docusaurus
platform/ui/storybook-static
# YALC (for Erik)
.yalc
@@ -34,3 +38,4 @@ screenshots/
# Locize settings
.locize
+15
View File
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}
+24
View File
@@ -0,0 +1,24 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Ziegler"
given-names: "Erik"
orcid: "https://orcid.org/0000-0003-1857-8129"
- family-names: "Urban"
given-names: "Trinity"
- family-names: "Brown"
given-names: "Danny"
- family-names: "Petts"
given-names: "James"
- family-names: "Pieper"
given-names: "Steve D"
- family-names: "Lewis"
given-names: "Rob"
- family-names: "Hafey"
given-names: "Chris"
- family-names: "Harris"
given-names: "Gordon J"
title: "Open Health Imaging Foundation Viewer: An Extensible Open-Source Framework for Building Web-Based Imaging Applications to Support Cancer Research"
doi: 10.1200/CCI.19.00131
version: 1.0.0
url: "https://github.com/OHIF/Viewers"
+3 -2
View File
@@ -21,7 +21,7 @@
# Stage 1: Build the application
# docker build -t ohif/viewer:latest .
FROM node:10.16.3-slim as builder
FROM node:15.13.0-slim as builder
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
@@ -39,6 +39,7 @@ COPY package.json /usr/src/app/package.json
COPY postcss.config.js /usr/src/app/postcss.config.js
COPY yarn.lock /usr/src/app/yarn.lock
RUN apt-get update && apt-get install -y python make g++
# Run the install before copying the rest of the files
RUN yarn config set workspaces-experimental true
RUN yarn install
@@ -52,7 +53,7 @@ RUN yarn run build
# Stage 2: Bundle the built application into a Docker container
# which runs Nginx using Alpine Linux
FROM nginx:1.15.5-alpine
FROM nginx:1.21.1-alpine
RUN apk add --no-cache bash
RUN rm -rf /etc/nginx/conf.d
COPY .docker/Viewer-v2.x /etc/nginx/conf.d
+2 -2
View File
@@ -8,7 +8,6 @@
"-fontsettings",
"github",
"ga",
"sitemap",
"anchors",
"versions"
],
@@ -24,7 +23,8 @@
"token": "UA-110573590-2"
},
"sitemap": {
"hostname": "https://docs.ohif.org"
"hostname": "https://docs.ohif.org",
"todo": "sitemap plugin removed; causing build errors"
},
"versions": {
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
+39 -3
View File
@@ -67,6 +67,33 @@ window.config = {
extensions: [],
showStudyList: true,
filterQueryParam: false,
/**
* Disable caching of servers configuration.
*
* There will be no effect if you update the servers property of this config
* while your application is running because this property is cached in local storage.
*/
disableServersCache: false,
/**
* OHIF's study prefetcher configuration.
*
* @param {boolean} enabled Whether to enable/disable OHIF's study prefetcher
* @param {('all'|'closest'|'downward'|'upward'|'topdown')} order Fetching order: all display sets, the closest ones, downward or top down fashion based on the currently selected display set
* @param {number} displaySetCount How much display sets should be prefetched at once (note: this attribute is ignored if order was set to 'all')
* @param {boolean} preventCache Prevent images to be cached in Cornerstone Tools's request pool manager
* @param {number} prefetchDisplaySetsTimeout Prefetch timeout
* @param {boolean} displayProgress Whether to display or not the progress bar in the display set
* @param {boolean} includeActiveDisplaySet Include or not the active display set while prefetching
*/
studyPrefetcher: {
enabled: true,
order: 'all',
displaySetCount: 1,
preventCache: false,
prefetchDisplaySetsTimeout: 300,
displayProgress: false,
includeActiveDisplaySet: true,
},
servers: {
dicomWeb: [
{
@@ -81,7 +108,14 @@ window.config = {
},
],
},
// Supported Keys: https://craig.is/killing/mice
/**
* Hotkey definitions.
* Supported Keys: https://craig.is/killing/mice
*
* @param {string} commandName
* @param {string} label
* @param {array} keys
*/
hotkeys: [
{ commandName: 'rotateViewportCW', label: 'Rotate Right', keys: ['r'] },
{ commandName: 'rotateViewportCCW', label: 'Rotate Left', keys: ['l'] },
@@ -97,14 +131,16 @@ window.config = {
keys: ['v'],
},
],
/* Configuration passed to the bundled cornerstone extension
/**
* Configuration passed to the bundled cornerstone extension
*
* The cornerstone extension is currently tightly coupled to the platform.
* Until we're able to decouple it, this key will serve as a workaround to
* pass it configuration.
*
* @param {boolean} hideHandles Whether to show/hide annotation "handles"
*/
cornerstoneExtensionConfig: {
/* Whether to show/hide annotation "handles" */
hideHandles: true,
},
};
+37
View File
@@ -0,0 +1,37 @@
# Viewer: Study Prefetcher & Stack Prefetch
Prefetching the stack of images of the active viewport is enabled by default for
cornerstone viewports via cornerstone tools's stack prefetch functionality.
In order to have a more customizable alternative for prefetching, OHIF provides
a built-in study prefetcher functionality which allows the user to set the order
in which the display sets are prefetched, the granularity, the timeout and
whether or not display a progress bar in the display set (close to the thumbnail
image) in the study browser.
You can customize these options via the viewer's configuration:
```js
window.config = {
/**
* OHIF's study prefetcher configuration.
*
* @param {boolean} enabled Whether to enable/disable OHIF's study prefetcher
* @param {('all'|'closest'|'downward'|'upward'|'topdown')} order Fetching order: all display sets, the closest ones, downward or top down fashion based on the currently selected display set
* @param {number} displaySetCount How much display sets should be prefetched at once (note: this attribute is ignored if order was set to 'all')
* @param {boolean} preventCache Prevent images to be cached in Cornerstone Tools's request pool manager
* @param {number} prefetchDisplaySetsTimeout Prefetch timeout
* @param {boolean} displayProgress Whether to display or not the progress bar in the display set
* @param {boolean} includeActiveDisplaySet Include or not the active display set while prefetching
*/
studyPrefetcher: {
enabled: true,
order: 'all',
displaySetCount: 1,
preventCache: false,
prefetchDisplaySetsTimeout: 300,
displayProgress: false,
includeActiveDisplaySet: true,
},
};
```
+2 -2
View File
@@ -8,7 +8,6 @@
"-fontsettings",
"github",
"ga",
"sitemap",
"anchors",
"versions"
],
@@ -24,7 +23,8 @@
"token": "UA-110573590-2"
},
"sitemap": {
"hostname": "https://docs.ohif.org"
"hostname": "https://docs.ohif.org",
"todo": "sitemap plugin removed; causing build errors"
},
"versions": {
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
+100
View File
@@ -3,6 +3,106 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.12.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.1...@ohif/extension-cornerstone@2.12.2) (2021-10-26)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.12.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.0...@ohif/extension-cornerstone@2.12.1) (2021-09-28)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [2.12.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.11.0...@ohif/extension-cornerstone@2.12.0) (2021-09-23)
### Features
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
# [2.11.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.10...@ohif/extension-cornerstone@2.11.0) (2021-09-09)
### Features
* OHIF-1000: Study prefetcher ([#2466](https://github.com/OHIF/Viewers/issues/2466)) ([8f26045](https://github.com/OHIF/Viewers/commit/8f2604509d24092b721f1333289d838820d68cac))
## [2.10.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.9...@ohif/extension-cornerstone@2.10.10) (2021-07-26)
### Bug Fixes
* changed new image handler conditional for 0 edge case ([#2338](https://github.com/OHIF/Viewers/issues/2338)) ([b8ae92c](https://github.com/OHIF/Viewers/commit/b8ae92c2d4f733b3ca89826118029685650b396a))
## [2.10.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.8...@ohif/extension-cornerstone@2.10.9) (2021-06-02)
### Bug Fixes
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
## [2.10.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.7...@ohif/extension-cornerstone@2.10.8) (2021-05-17)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.10.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.6...@ohif/extension-cornerstone@2.10.7) (2021-04-15)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.10.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.5...@ohif/extension-cornerstone@2.10.6) (2021-03-26)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.10.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.4...@ohif/extension-cornerstone@2.10.5) (2021-03-15)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.10.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.3...@ohif/extension-cornerstone@2.10.4) (2021-03-09)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.10.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.2...@ohif/extension-cornerstone@2.10.3) (2021-03-03)
**Note:** Version bump only for package @ohif/extension-cornerstone
+23 -2
View File
@@ -85,12 +85,32 @@ Tools can be configured through extension configuration using the tools key:
...
```
## Stack prefetch configuration
Stack prefetch can be configured through extension configuration using the
stackPrefetch key:
```js
...
cornerstoneExtensionConfig: {
stackPrefetch: {
enabled: true,
maxImagesToPrefetch: Infinity,
preserveExistingPool: false,
maxSimultaneousRequests: 20,
},
},
...
```
## Annotate Tools Configuration
*We currently support one property for annotation tools.*
_We currently support one property for annotation tools._
### Hide handles
This extension configuration allows you to toggle on/off handle rendering for all annotate tools:
This extension configuration allows you to toggle on/off handle rendering for
all annotate tools:
```js
...
@@ -119,3 +139,4 @@ This extension configuration allows you to toggle on/off handle rendering for al
[cornerstone-tools]: https://github.com/cornerstonejs/cornerstoneTools
[cornerstone]: https://github.com/cornerstonejs/cornerstone
<!-- prettier-ignore-end -->
```
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "2.10.3",
"version": "2.12.2",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -32,12 +32,12 @@
"peerDependencies": {
"@ohif/core": "^0.50.0",
"@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8",
"cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "0.18.2",
"dicom-parser": "^1.8.3",
"cornerstone-core": "^2.5.0",
"cornerstone-math": "^0.1.9",
"cornerstone-tools": "^6.0.0",
"cornerstone-wado-image-loader": "^4.0.3",
"dcmjs": "0.18.10",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
"react": "^16.8.6",
@@ -51,6 +51,6 @@
"classnames": "^2.2.6",
"lodash.merge": "^4.6.2",
"lodash.throttle": "^4.1.1",
"react-cornerstone-viewport": "4.0.4"
"react-cornerstone-viewport": "4.1.1"
}
}
@@ -53,7 +53,11 @@ const mapStateToProps = (state, ownProps) => {
// Currently justing using escape hatch + commands
// activeTool: activeButton && activeButton.command,
...dataFromStore,
isStackPrefetchEnabled: isActive,
isStackPrefetchEnabled: ownProps.hasOwnProperty('isStackPrefetchEnabled')
? ownProps.isStackPrefetchEnabled
: ownProps.stackPrefetch
? ownProps.stackPrefetch.enabled
: isActive,
isPlaying,
frameRate,
//stack: viewportSpecificData.stack,
@@ -1,5 +1,6 @@
import React, { Component } from 'react';
import OHIFCornerstoneViewportOverlay from './components/OHIFCornerstoneViewportOverlay';
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
import OHIF from '@ohif/core';
import PropTypes from 'prop-types';
@@ -14,6 +15,7 @@ class OHIFCornerstoneViewport extends Component {
static defaultProps = {
customProps: {},
isStackPrefetchEnabled: true,
};
static propTypes = {
@@ -22,6 +24,8 @@ class OHIFCornerstoneViewport extends Component {
viewportIndex: PropTypes.number,
children: PropTypes.node,
customProps: PropTypes.object,
stackPrefetch: PropTypes.object,
isStackPrefetchEnabled: PropTypes.bool,
};
static id = 'OHIFCornerstoneViewport';
@@ -179,7 +183,7 @@ class OHIFCornerstoneViewport extends Component {
if (
displaySet.displaySetInstanceUID !==
prevDisplaySet.displaySetInstanceUID ||
prevDisplaySet.displaySetInstanceUID ||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
displaySet.frameIndex !== prevDisplaySet.frameIndex
) {
@@ -194,6 +198,7 @@ class OHIFCornerstoneViewport extends Component {
return null;
}
const { viewportIndex } = this.props;
const { inconsistencyWarnings } = this.props.viewportData.displaySet;
const {
imageIds,
currentImageIdIndex,
@@ -219,7 +224,7 @@ class OHIFCornerstoneViewport extends Component {
const { displaySet } = this.props.viewportData;
const { StudyInstanceUID } = displaySet;
if (currentImageIdIndex > 0) {
if (currentImageIdIndex >= 0) {
this.props.onNewImage({
StudyInstanceUID,
SOPInstanceUID: sopInstanceUid,
@@ -229,18 +234,29 @@ class OHIFCornerstoneViewport extends Component {
}
};
const warningsOverlay = props => {
return (
<OHIFCornerstoneViewportOverlay
{...props}
inconsistencyWarnings={inconsistencyWarnings}
/>
);
};
return (
<>
<ConnectedCornerstoneViewport
viewportIndex={viewportIndex}
imageIds={imageIds}
imageIdIndex={currentImageIdIndex}
onNewImage={newImageHandler}
onNewImageDebounceTime={700}
onNewImageDebounced={newImageHandler}
onNewImageDebounceTime={300}
viewportOverlayComponent={warningsOverlay}
stackPrefetch={this.props.stackPrefetch}
isStackPrefetchEnabled={this.props.isStackPrefetchEnabled}
// ~~ Connected (From REDUX)
// frameRate={frameRate}
// isPlaying={false}
// isStackPrefetchEnabled={true}
// onElementEnabled={() => {}}
// setViewportActive{() => {}}
{...this.props.customProps}
+8 -1
View File
@@ -268,6 +268,7 @@ const commandsModule = ({ servicesManager }) => {
SOPInstanceUID,
frameIndex,
activeViewportIndex,
refreshViewports = true,
}) => {
const study = studyMetadataManager.get(StudyInstanceUID);
@@ -278,6 +279,10 @@ const commandsModule = ({ servicesManager }) => {
);
});
if (!displaySet) {
return;
}
displaySet.SOPInstanceUID = SOPInstanceUID;
displaySet.frameIndex = frameIndex;
@@ -285,7 +290,9 @@ const commandsModule = ({ servicesManager }) => {
setViewportSpecificData(activeViewportIndex, displaySet)
);
refreshCornerstoneViewports();
if (refreshViewports) {
refreshCornerstoneViewports();
}
},
};
@@ -0,0 +1,69 @@
.imageViewerViewport.empty ~ .OHIFCornerstoneViewportOverlay {
display: none;
}
.OHIFCornerstoneViewportOverlay {
color: #9ccef9;
}
.OHIFCornerstoneViewportOverlay .overlay-element {
position: absolute;
font-weight: 400;
text-shadow: 1px 1px #000;
pointer-events: none;
}
.OHIFCornerstoneViewportOverlay .top-left {
top: 20px;
left: 20px;
}
.OHIFCornerstoneViewportOverlay .top-center {
top: 20px;
padding-top: 20px;
width: 100%;
text-align: center;
}
.OHIFCornerstoneViewportOverlay .top-right {
top: 20px;
right: 20px;
text-align: right;
}
.OHIFCornerstoneViewportOverlay .bottom-left {
bottom: 20px;
left: 20px;
}
.OHIFCornerstoneViewportOverlay .bottom-left2 {
bottom: 140px;
left: 20px;
}
.OHIFCornerstoneViewportOverlay .bottom-right {
bottom: 20px;
right: 20px;
text-align: right;
}
.OHIFCornerstoneViewportOverlay.controlsVisible .topright,
.OHIFCornerstoneViewportOverlay.controlsVisible .bottomright {
right: calc(20px + 19px);
}
.OHIFCornerstoneViewportOverlay .warning {
position: absolute;
font-weight: 400;
text-shadow: 1px 1px #000;
pointer-events: inherit;
}
.OHIFCornerstoneViewportOverlay .warning svg {
opacity: 1;
color: #e29e4a;
fill: #e29e4a;
stroke: #e29e4a;
width: 32px;
height: 28px;
}
.OHIFCornerstoneViewportOverlay svg {
color: #9ccef9;
fill: #9ccef9;
stroke: #9ccef9;
background-color: transparent;
margin: 2px;
width: 18px;
height: 18px;
}
@@ -0,0 +1,167 @@
import { PureComponent } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import './OHIFCornerstoneViewportOverlay.css';
import {
isValidNumber,
formatNumberPrecision,
formatDICOMDate,
formatDICOMTime,
formatPN,
getCompression
} from '../utils/formatStudy';
import classNames from 'classnames';
import { Icon } from '@ohif/ui/src/elements/Icon';
import { Tooltip } from '@ohif/ui/src/components/tooltip';
import { OverlayTrigger } from '@ohif/ui/src/components/overlayTrigger';
class OHIFCornerstoneViewportOverlay extends PureComponent {
static propTypes = {
scale: PropTypes.number.isRequired,
windowWidth: PropTypes.oneOfType([
PropTypes.number.isRequired,
PropTypes.string.isRequired,
]),
windowCenter: PropTypes.oneOfType([
PropTypes.number.isRequired,
PropTypes.string.isRequired,
]),
imageId: PropTypes.string.isRequired,
imageIndex: PropTypes.number.isRequired,
stackSize: PropTypes.number.isRequired,
inconsistencyWarnings: PropTypes.array.isRequired
};
render() {
const { imageId, scale, windowWidth, windowCenter, inconsistencyWarnings } = this.props;
if (!imageId) {
return null;
}
const zoomPercentage = formatNumberPrecision(scale * 100, 0);
const seriesMetadata =
cornerstone.metaData.get('generalSeriesModule', imageId) || {};
const imagePlaneModule =
cornerstone.metaData.get('imagePlaneModule', imageId) || {};
const { rows, columns, sliceThickness, sliceLocation } = imagePlaneModule;
const { seriesNumber, seriesDescription } = seriesMetadata;
const generalStudyModule =
cornerstone.metaData.get('generalStudyModule', imageId) || {};
const { studyDate, studyTime, studyDescription } = generalStudyModule;
const patientModule =
cornerstone.metaData.get('patientModule', imageId) || {};
const { patientId, patientName } = patientModule;
const generalImageModule =
cornerstone.metaData.get('generalImageModule', imageId) || {};
const { instanceNumber } = generalImageModule;
const cineModule = cornerstone.metaData.get('cineModule', imageId) || {};
const { frameTime } = cineModule;
const frameRate = formatNumberPrecision(1000 / frameTime, 1);
const compression = getCompression(imageId);
const wwwc = `W: ${
windowWidth.toFixed ? windowWidth.toFixed(0) : windowWidth
} L: ${windowWidth.toFixed ? windowCenter.toFixed(0) : windowCenter}`;
const imageDimensions = `${columns} x ${rows}`;
const { imageIndex, stackSize } = this.props;
const inconsistencyWarningsOn = inconsistencyWarnings && inconsistencyWarnings.length !== 0 ? true : false;
const getWarningContent = (warningList) => {
if (Array.isArray(warningList)) {
const listedWarnings = warningList.map((warn, index) => {
return <li key={index}>{warn}</li>;
});
return <ol>{listedWarnings}</ol>;
} else {
return <React.Fragment>{warningList}</React.Fragment>;
}
};
const getWarningInfo = (seriesNumber, inconsistencyWarnings) => {
return(
<React.Fragment>
{inconsistencyWarnings.length != 0 ? (
<OverlayTrigger
key={seriesNumber}
placement="left"
overlay={
<Tooltip
placement="left"
className="in tooltip-warning"
id="tooltip-left"
>
<div className="warningTitle">Series Inconsistencies</div>
<div className="warningContent">{getWarningContent(inconsistencyWarnings)}</div>
</Tooltip>
}
>
<div className={classNames('warning')}>
<span className="warning-icon">
<Icon name="exclamation-triangle" />
</span>
</div>
</OverlayTrigger>
) : (
<React.Fragment></React.Fragment>
)}
</React.Fragment>
);
};
const normal = (
<React.Fragment>
<div className="top-left overlay-element">
<div>{formatPN(patientName)}</div>
<div>{patientId}</div>
</div>
<div className="top-right overlay-element">
<div>{studyDescription}</div>
<div>
{formatDICOMDate(studyDate)} {formatDICOMTime(studyTime)}
</div>
</div>
<div className="bottom-right overlay-element">
<div>Zoom: {zoomPercentage}%</div>
<div>{wwwc}</div>
<div className="compressionIndicator">{compression}</div>
</div>
<div className="bottom-left2 warning">
<div>{inconsistencyWarningsOn ? getWarningInfo(seriesNumber, inconsistencyWarnings) : ''}</div>
</div>
<div className="bottom-left overlay-element">
<div>{seriesNumber >= 0 ? `Ser: ${seriesNumber}` : ''}</div>
<div>
{stackSize > 1
? `Img: ${instanceNumber} ${imageIndex}/${stackSize}`
: ''}
</div>
<div>
{frameRate >= 0 ? `${formatNumberPrecision(frameRate, 2)} FPS` : ''}
<div>{imageDimensions}</div>
<div>
{isValidNumber(sliceLocation)
? `Loc: ${formatNumberPrecision(sliceLocation, 2)} mm `
: ''}
{sliceThickness
? `Thick: ${formatNumberPrecision(sliceThickness, 2)} mm`
: ''}
</div>
<div>{seriesDescription}</div>
</div>
</div>
</React.Fragment>
);
return <div className="OHIFCornerstoneViewportOverlay">{normal}</div>;
}
}
export default OHIFCornerstoneViewportOverlay;
+18 -2
View File
@@ -36,13 +36,29 @@ export default {
preRegistration({ servicesManager, configuration = {} }) {
init({ servicesManager, configuration });
},
getViewportModule({ commandsManager }) {
getViewportModule({ commandsManager, appConfig }) {
const ExtendedOHIFCornerstoneViewport = props => {
/**
* TODO: This appears to be used to set the redux parameters for
* the viewport when new images are loaded. It's very ugly
* and we should remove it.
*/
const onNewImageHandler = jumpData => {
/** Do not trigger all viewports to render unnecessarily */
jumpData.refreshViewports = false;
commandsManager.runCommand('jumpToImage', jumpData);
};
const { studyPrefetcher } = appConfig;
const isStackPrefetchEnabled =
studyPrefetcher && !studyPrefetcher.enabled;
return (
<OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />
<OHIFCornerstoneViewport
{...props}
onNewImage={onNewImageHandler}
isStackPrefetchEnabled={isStackPrefetchEnabled}
/>
);
};
+9 -2
View File
@@ -37,7 +37,14 @@ export default function init({ servicesManager, configuration }) {
}
};
const { csToolsConfig } = configuration;
const {
csToolsConfig,
stackPrefetch = {
maxImagesToPrefetch: Infinity,
preserveExistingPool: false,
maxSimultaneousRequests: 20,
},
} = configuration;
const metadataProvider = OHIF.cornerstone.metadataProvider;
cornerstone.metaData.addProvider(
@@ -52,7 +59,7 @@ export default function init({ servicesManager, configuration }) {
autoResizeViewports: false,
};
initCornerstoneTools(defaultCsToolsConfig);
initCornerstoneTools({ ...defaultCsToolsConfig, ...stackPrefetch });
const toolsGroupedByType = {
touch: [csTools.PanMultiTouchTool, csTools.ZoomTouchPinchTool],
@@ -37,8 +37,8 @@ export default function(configuration = {}) {
// Configure stack prefetch
cornerstoneTools.stackPrefetch.setConfiguration({
maxImagesToPrefetch: Infinity,
preserveExistingPool: false,
maxSimultaneousRequests: 20,
maxImagesToPrefetch: configuration.maxImagesToPrefetch,
preserveExistingPool: configuration.preserveExistingPool,
maxSimultaneousRequests: configuration.maxSimultaneousRequests,
});
}
@@ -0,0 +1,109 @@
import moment from 'moment';
/**
* Checks if value is valid.
*
* @param {number} value
* @returns {boolean} is valid.
*/
function isValidNumber(value) {
return typeof value === 'number' && !isNaN(value);
}
/**
* Formats number precision.
*
* @param {number} number
* @param {number} precision
* @returns {number} formatted number.
*/
function formatNumberPrecision(number, precision) {
if (number !== null) {
return parseFloat(number).toFixed(precision);
}
}
/**
* Formats DICOM date.
*
* @param {string} date
* @param {string} strFormat
* @returns {string} formatted date.
*/
function formatDICOMDate(date, strFormat = 'MMM D, YYYY') {
return moment(date, 'YYYYMMDD').format(strFormat);
}
/**
* DICOM Time is stored as HHmmss.SSS, where:
* HH 24 hour time:
* m mm 0..59 Minutes
* s ss 0..59 Seconds
* S SS SSS 0..999 Fractional seconds
*
* Goal: '24:12:12'
*
* @param {*} time
* @param {string} strFormat
* @returns {string} formatted name.
*/
function formatDICOMTime(time, strFormat = 'HH:mm:ss') {
return moment(time, 'HH:mm:ss').format(strFormat);
}
/**
* Formats a patient name for display purposes
*
* @param {string} name
* @returns {string} formatted name.
*/
function formatPN(name) {
if (!name) {
return;
}
// Convert the first ^ to a ', '. String.replace() only affects
// the first appearance of the character.
const commaBetweenFirstAndLast = name.replace('^', ', ');
// Replace any remaining '^' characters with spaces
const cleaned = commaBetweenFirstAndLast.replace(/\^/g, ' ');
// Trim any extraneous whitespace
return cleaned.trim();
}
/**
* Gets compression type
*
* @param {number} imageId
* @returns {string} comrpession type.
*/
function getCompression(imageId) {
const generalImageModule =
cornerstone.metaData.get('generalImageModule', imageId) || {};
const {
lossyImageCompression,
lossyImageCompressionRatio,
lossyImageCompressionMethod,
} = generalImageModule;
if (lossyImageCompression === '01' && lossyImageCompressionRatio !== '') {
const compressionMethod = lossyImageCompressionMethod || 'Lossy: ';
const compressionRatio = formatNumberPrecision(
lossyImageCompressionRatio,
2
);
return compressionMethod + compressionRatio + ' : 1';
}
return 'Lossless / Uncompressed';
}
export { isValidNumber,
formatNumberPrecision,
formatDICOMDate,
formatDICOMTime,
formatPN,
getCompression
};
+27
View File
@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [0.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.2.2...@ohif/extension-debugging@0.3.0) (2021-09-23)
### Features
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
## [0.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.2.1...@ohif/extension-debugging@0.2.2) (2021-06-03)
**Note:** Version bump only for package @ohif/extension-debugging
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.2.0...@ohif/extension-debugging@0.2.1) (2021-03-19)
**Note:** Version bump only for package @ohif/extension-debugging
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.1.6...@ohif/extension-debugging@0.2.0) (2021-03-04)
+3 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-debugging",
"version": "0.2.0",
"version": "0.3.0",
"description": "OHIF extension for debugging.",
"author": "OHIF",
"license": "MIT",
@@ -28,13 +28,12 @@
},
"peerDependencies": {
"@ohif/core": "^2.6.0",
"dicom-parser": "^1.8.3",
"dicomweb-client": "^0.5.2"
"dicom-parser": "^1.8.9"
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"detect-browser": "5.1.1",
"dicomweb-client": "^0.6.0",
"dicomweb-client": "^0.8.1",
"file-saver": "^2.0.2",
"jszip": "^3.2.2"
}
+3 -3
View File
@@ -1,5 +1,5 @@
import OHIF from '@ohif/core';
import { ToolbarButton, useLogger } from '@ohif/ui';
import { useLogger } from '@ohif/ui';
import {
save,
@@ -97,7 +97,7 @@ export function getCommands(context, servicesManager, extensionManager) {
const { UIModalService } = servicesManager.services;
const WrappedDebugReportModal = function() {
const { state } = useLogger();
const { state: loggerState } = useLogger();
return (
<DebugReportModal
viewports={viewports}
@@ -106,7 +106,7 @@ export function getCommands(context, servicesManager, extensionManager) {
extensionManager={extensionManager}
mailTo={state.mailTo}
debugModalMessage={state.debugModalMessage}
errors={state.errors}
errors={loggerState.errors}
/>
);
};
+1 -1
View File
@@ -1,3 +1,3 @@
const state = { mailTo: undefined, message: undefined };
const state = { mailTo: undefined, debugModalMessage: undefined };
export default state;
+83
View File
@@ -3,6 +3,89 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.12...@ohif/extension-dicom-html@1.3.13) (2021-10-26)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.11...@ohif/extension-dicom-html@1.3.12) (2021-10-25)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.10...@ohif/extension-dicom-html@1.3.11) (2021-09-28)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.9...@ohif/extension-dicom-html@1.3.10) (2021-06-02)
### Bug Fixes
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
## [1.3.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.8...@ohif/extension-dicom-html@1.3.9) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.7...@ohif/extension-dicom-html@1.3.8) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.6...@ohif/extension-dicom-html@1.3.7) (2021-04-21)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.5...@ohif/extension-dicom-html@1.3.6) (2021-04-21)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.4...@ohif/extension-dicom-html@1.3.5) (2021-04-15)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.3...@ohif/extension-dicom-html@1.3.4) (2021-03-09)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.2...@ohif/extension-dicom-html@1.3.3) (2021-03-03)
**Note:** Version bump only for package @ohif/extension-dicom-html
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-html",
"version": "1.3.3",
"version": "1.3.13",
"description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF",
"license": "MIT",
@@ -29,7 +29,7 @@
},
"peerDependencies": {
"@ohif/core": "^0.50.0",
"dcmjs": "0.18.2",
"dcmjs": "0.18.10",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
+13 -2
View File
@@ -160,7 +160,7 @@ const getContentSequence = (data, level = 1) => {
const root = [];
if (header) {
const HeaderDynamicLevel = `h${level}`;
const HeaderDynamicLevel = `h${Math.min(level, 6)}`;
root.push(<HeaderDynamicLevel key={header}>{header}</HeaderDynamicLevel>);
}
@@ -168,6 +168,16 @@ const getContentSequence = (data, level = 1) => {
Object.keys(data).forEach(key => {
const value = data[key];
if (key === '_meta') {
const HeaderDynamicLevel = `h3`;
root.push(<hr key={root.length} />);
root.push(
<HeaderDynamicLevel key="Metadata">
DICOM File Meta Information
</HeaderDynamicLevel>
);
}
let content;
if (value instanceof Object) {
content = getContentSequence(value, level + 1);
@@ -207,7 +217,7 @@ class DicomHtmlViewport extends Component {
};
componentDidMount() {
const dataSet = this.setContentFromByteArray(this.props.byteArray);
this.setContentFromByteArray(this.props.byteArray);
}
setContentFromByteArray(byteArray) {
@@ -215,6 +225,7 @@ class DicomHtmlViewport extends Component {
const dicomData = DicomMessage.readFile(arrayBuffer);
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
dataset._meta = DicomMetaDictionary.naturalizeDataset(dataset._meta);
const mainData = getMainData(dataset);
const contentSequence = getContentSequence(dataset);
@@ -9,6 +9,7 @@ const SOP_CLASS_UIDS = {
MAMMOGRAPHY_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.50',
CHEST_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.65',
X_RAY_RADIATION_DOSE_SR: '1.2.840.10008.5.1.4.1.1.88.67',
ACQUISITION_CONTEXT_SR_STORAGE: '1.2.840.10008.5.1.4.1.1.88.71',
};
const sopClassUIDs = Object.values(SOP_CLASS_UIDS);
+11
View File
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.6...@ohif/extension-dicom-pdf@1.1.0) (2021-09-23)
### Features
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
## [1.0.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.5...@ohif/extension-dicom-pdf@1.0.6) (2020-09-10)
**Note:** Version bump only for package @ohif/extension-dicom-pdf
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-pdf",
"version": "1.0.6",
"version": "1.1.0",
"description": "OHIF extension for Dicom PDF",
"author": "OHIF",
"license": "MIT",
@@ -29,7 +29,7 @@
},
"peerDependencies": {
"@ohif/core": "^0.50.0",
"dicom-parser": "^1.8.3",
"dicom-parser": "^1.8.9",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
+102
View File
@@ -3,6 +3,108 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.7.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.2...@ohif/extension-dicom-rt@0.7.3) (2021-10-26)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.7.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.1...@ohif/extension-dicom-rt@0.7.2) (2021-10-06)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.7.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.0...@ohif/extension-dicom-rt@0.7.1) (2021-09-28)
**Note:** Version bump only for package @ohif/extension-dicom-rt
# [0.7.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.14...@ohif/extension-dicom-rt@0.7.0) (2021-09-23)
### Features
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
## [0.6.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.13...@ohif/extension-dicom-rt@0.6.14) (2021-09-03)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.12...@ohif/extension-dicom-rt@0.6.13) (2021-06-02)
### Bug Fixes
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
## [0.6.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.11...@ohif/extension-dicom-rt@0.6.12) (2021-05-17)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.10...@ohif/extension-dicom-rt@0.6.11) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.9...@ohif/extension-dicom-rt@0.6.10) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.8...@ohif/extension-dicom-rt@0.6.9) (2021-04-21)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.7...@ohif/extension-dicom-rt@0.6.8) (2021-04-15)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.6...@ohif/extension-dicom-rt@0.6.7) (2021-03-09)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.6.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.5...@ohif/extension-dicom-rt@0.6.6) (2021-03-03)
**Note:** Version bump only for package @ohif/extension-dicom-rt
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-rt",
"version": "0.6.6",
"version": "0.7.3",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF",
"license": "MIT",
@@ -29,9 +29,9 @@
},
"peerDependencies": {
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.20.1",
"dcmjs": "0.18.2",
"cornerstone-core": "^2.5.0",
"cornerstone-tools": "^6.0.0",
"dcmjs": "0.18.10",
"gl-matrix": "^3.3.0",
"prop-types": "^15.6.2",
"react": "^16.8.6",
@@ -71,8 +71,8 @@ const OHIFDicomRTStructSopClassHandler = {
}
}
rtStructDisplaySet.getSourceDisplaySet = function (studies) {
return getSourceDisplaySet(studies, rtStructDisplaySet);
rtStructDisplaySet.getSourceDisplaySet = function (studies, activateLabelMap = true) {
return getSourceDisplaySet(studies, rtStructDisplaySet, activateLabelMap);
};
rtStructDisplaySet.load = function (referencedDisplaySet, studies) {
+7 -31
View File
@@ -1,38 +1,14 @@
export default function getSourceDisplaySet(studies, rtStructDisplaySet) {
const referencedDisplaySet = _getReferencedDisplaySet(
import { metadata } from '@ohif/core';
export default function getSourceDisplaySet(studies, rtStructDisplaySet, activateLabelMap = true) {
const referencedDisplaySet = metadata.StudyMetadata.getReferencedDisplaySet(
rtStructDisplaySet,
studies
);
rtStructDisplaySet.load(referencedDisplaySet, studies);
if (activateLabelMap) {
rtStructDisplaySet.load(referencedDisplaySet, studies);
}
return referencedDisplaySet;
}
const _getReferencedDisplaySet = (rtStructDisplaySet, studies) => {
let allDisplaySets = [];
studies.forEach(study => {
allDisplaySets = allDisplaySets.concat(study.displaySets);
});
const otherDisplaySets = allDisplaySets.filter(
ds => ds.displaySetInstanceUID !== rtStructDisplaySet.displaySetInstanceUID
);
const ReferencedSeriesSequence = Array.isArray(
rtStructDisplaySet.metadata.ReferencedSeriesSequence
)
? rtStructDisplaySet.metadata.ReferencedSeriesSequence
: [rtStructDisplaySet.metadata.ReferencedSeriesSequence];
const referencedSeriesInstanceUIDs = ReferencedSeriesSequence.map(
ReferencedSeries => ReferencedSeries.SeriesInstanceUID
);
const referencedDisplaySet = otherDisplaySets.find(ds =>
referencedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID)
);
return referencedDisplaySet;
};
+45 -9
View File
@@ -1,11 +1,12 @@
import React from 'react';
import { utils } from '@ohif/core';
import init from './init.js';
import sopClassHandlerModule from './OHIFDicomRTStructSopClassHandler';
import id from './id.js';
import RTPanel from './components/RTPanel/RTPanel';
import { version } from '../package.json';
import { utils } from '@ohif/core';
const { studyMetadataManager } = utils;
export default {
@@ -42,29 +43,60 @@ export default {
);
};
const RTPanelTabChangedEvent = 'rt-panel-tab-updated';
/**
* Trigger's an event to update the state of the panel's RoundedButtonGroup.
*
* This is required to avoid extension state
* coupling with the viewer's ToolbarRow component.
*
* @param {object} data
*/
const triggerRTPanelUpdatedEvent = data => {
const event = new CustomEvent(RTPanelTabChangedEvent, {
detail: data,
});
document.dispatchEvent(event);
};
const onRTStructsLoaded = ({ detail }) => {
const { rtStructDisplaySet, referencedDisplaySet } = detail;
const studyMetadata = studyMetadataManager.get(
rtStructDisplaySet.StudyInstanceUID
);
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
referencedSeriesInstanceUID: referencedDisplaySet.SeriesInstanceUID,
Modality: 'RTSTRUCT',
});
triggerRTPanelUpdatedEvent({
badgeNumber: referencedDisplaysets.length,
target: 'rt-panel',
});
};
document.addEventListener('extensiondicomrtrtloaded', onRTStructsLoaded);
return {
menuOptions: [
{
icon: 'list',
label: 'RTSTRUCT',
target: 'rt-panel',
stateEvent: RTPanelTabChangedEvent,
isDisabled: (studies, activeViewport) => {
if (!studies) {
return true;
}
if (activeViewport) {
const study = studies.find(
s => s.StudyInstanceUID === activeViewport.StudyInstanceUID
);
const ds = study.displaySets.find(
ds =>
ds.displaySetInstanceUID ===
activeViewport.displaySetInstanceUID
);
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
if (!studyMetadata) {
return;
}
const referencedDisplaySets = studyMetadata.getDerivedDatasets({
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
Modality: 'RTSTRUCT',
@@ -75,6 +107,10 @@ export default {
['RTSTRUCT'].includes(ds.Modality)
)
) {
triggerRTPanelUpdatedEvent({
badgeNumber: referencedDisplaySets.length,
target: 'rt-panel',
});
return false;
}
}
+15 -7
View File
@@ -90,11 +90,11 @@ export default async function loadRTStruct(
const sopInstanceUID = ContourImageSequence
? ContourImageSequence.ReferencedSOPInstanceUID
: _getClosestSOPInstanceUID(
ContourData,
ContourGeometricType,
NumberOfContourPoints,
imageIdSopInstanceUidPairs
);
ContourData,
ContourGeometricType,
NumberOfContourPoints,
imageIdSopInstanceUidPairs
);
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
if (!imageId) {
@@ -171,7 +171,13 @@ export default async function loadRTStruct(
* allows us to easily watch the module or the rtstruct loading process in any other component
* without subscribing to external events.
*/
const event = new CustomEvent('extensiondicomrtrtloaded');
const event = new CustomEvent('extensiondicomrtrtloaded', {
detail: {
rtStructDisplaySet,
referencedDisplaySet,
studies,
},
});
document.dispatchEvent(event);
}
@@ -299,7 +305,9 @@ const _getImageId = (imageIdSopInstanceUidPairs, sopInstanceUID) => {
imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID
);
return imageIdSopInstanceUidPairsEntry ? imageIdSopInstanceUidPairsEntry.imageId : null;
return imageIdSopInstanceUidPairsEntry
? imageIdSopInstanceUidPairsEntry.imageId
: null;
};
function _getImageIdSopInstanceUidPairsForDisplaySet(
+126
View File
@@ -3,6 +3,132 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.7.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.2...@ohif/extension-dicom-segmentation@0.7.3) (2021-10-26)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.7.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.1...@ohif/extension-dicom-segmentation@0.7.2) (2021-10-06)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.7.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.0...@ohif/extension-dicom-segmentation@0.7.1) (2021-09-28)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
# [0.7.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.11...@ohif/extension-dicom-segmentation@0.7.0) (2021-09-23)
### Features
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
## [0.6.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.10...@ohif/extension-dicom-segmentation@0.6.11) (2021-09-03)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.6.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.9...@ohif/extension-dicom-segmentation@0.6.10) (2021-06-02)
### Bug Fixes
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
## [0.6.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.8...@ohif/extension-dicom-segmentation@0.6.9) (2021-05-17)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.6.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.7...@ohif/extension-dicom-segmentation@0.6.8) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.6.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.6...@ohif/extension-dicom-segmentation@0.6.7) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.6.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.5...@ohif/extension-dicom-segmentation@0.6.6) (2021-04-21)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.6.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.4...@ohif/extension-dicom-segmentation@0.6.5) (2021-04-21)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.6.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.3...@ohif/extension-dicom-segmentation@0.6.4) (2021-04-16)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.6.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.2...@ohif/extension-dicom-segmentation@0.6.3) (2021-04-15)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.6.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.1...@ohif/extension-dicom-segmentation@0.6.2) (2021-03-31)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.0...@ohif/extension-dicom-segmentation@0.6.1) (2021-03-09)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
# [0.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.4...@ohif/extension-dicom-segmentation@0.6.0) (2021-03-04)
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-segmentation",
"version": "0.6.0",
"version": "0.7.3",
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF",
"license": "MIT",
@@ -29,9 +29,9 @@
},
"peerDependencies": {
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.20.1",
"dcmjs": "0.18.2",
"cornerstone-core": "^2.5.0",
"cornerstone-tools": "^6.0.0",
"dcmjs": "0.18.10",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
@@ -155,7 +155,9 @@ const SegmentationPanel = ({
const brushStackState = getBrushStackState();
brushStackState.activeLabelmapIndex = newLabelmapIndex;
setState(state => ({ ...state, selectedSegmentation }));
if (selectedSegmentation) {
setState(state => ({ ...state, selectedSegmentation }));
}
refreshViewports();
@@ -189,6 +191,10 @@ const SegmentationPanel = ({
'extensiondicomsegmentationsegloaded',
refreshSegmentations
);
document.addEventListener(
'extensiondicomsegmentationsegselected',
updateSegmentationComboBox
);
/*
* These are specific to each element;
@@ -207,6 +213,10 @@ const SegmentationPanel = ({
'extensiondicomsegmentationsegloaded',
refreshSegmentations
);
document.removeEventListener(
'extensiondicomsegmentationsegselected',
updateSegmentationComboBox
);
cornerstoneTools.store.state.enabledElements.forEach(enabledElement =>
enabledElement.removeEventListener(
'cornerstonetoolslabelmapmodified',
@@ -214,7 +224,32 @@ const SegmentationPanel = ({
)
);
};
}, [activeIndex, viewports]);
}, [
activeIndex,
updateSegmentationComboBox,
viewports,
]);
const updateSegmentationComboBox = e => {
const index = e.detail.activatedLabelmapIndex;
if (index !== -1) {
setState(state => ({ ...state, selectedSegmentation: index }));
} else {
cleanSegmentationComboBox();
}
};
const cleanSegmentationComboBox = () => {
setState(state => ({
...state,
segmentsHidden: [],
segmentNumbers: [],
labelMapList: [],
segmentList: [],
isDisabled: true,
selectedSegmentation: -1,
}));
};
const refreshSegmentations = () => {
const activeViewport = getActiveViewport();
@@ -277,7 +312,11 @@ const SegmentationPanel = ({
activeViewport.SeriesInstanceUID
);
return referencedSegDisplaysets.map((displaySet, index) => {
const filteredReferencedSegDisplaysets = referencedSegDisplaysets.filter(
segDisplay => segDisplay.loadError !== true
);
return filteredReferencedSegDisplaysets.map((displaySet, index) => {
const {
labelmapIndex,
originLabelMapIndex,
@@ -289,7 +328,7 @@ const SegmentationPanel = ({
/* Map to display representation */
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
const displayDate = date.format('ddd, MMM Do YYYY');
const displayDate = date.format('ddd, MMM Do YYYY, h:mm:ss a');
const displayDescription = displaySet.SeriesDescription;
return {
@@ -709,7 +748,7 @@ const _getReferencedSegDisplaysets = (StudyInstanceUID, SeriesInstanceUID) => {
referencedDisplaysets.sort((a, b) => {
const aNumber = Number(`${a.SeriesDate}${a.SeriesTime}`);
const bNumber = Number(`${b.SeriesDate}${b.SeriesTime}`);
return aNumber - bNumber;
return bNumber - aNumber;
});
return referencedDisplaysets;
@@ -3,7 +3,7 @@ import Select from 'react-select';
const SegmentationSelect = ({ value, formatOptionLabel, options }) => (
<Select
value={value}
value={value === undefined ? null : value}
formatOptionLabel={formatOptionLabel}
styles={segmentationSelectStyles}
options={options}
@@ -55,6 +55,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
referencedDisplaySetUID: null, // Assigned when loaded.
labelmapIndex: null, // Assigned when loaded.
isLoaded: false,
loadError: false,
hasOverlapping: false,
SeriesDate,
SeriesTime,
@@ -63,8 +64,8 @@ export default function getSopClassHandlerModule({ servicesManager }) {
metadata,
};
segDisplaySet.getSourceDisplaySet = function(studies) {
return getSourceDisplaySet(studies, segDisplaySet);
segDisplaySet.getSourceDisplaySet = function(studies, activateLabelMap = true, onDisplaySetLoadFailureHandler) {
return getSourceDisplaySet(studies, segDisplaySet, activateLabelMap, onDisplaySetLoadFailureHandler);
};
segDisplaySet.load = async function(referencedDisplaySet, studies) {
@@ -82,57 +83,50 @@ export default function getSopClassHandlerModule({ servicesManager }) {
StudyInstanceUID,
referencedDisplaySet.SeriesInstanceUID
);
return new Promise(async (resolve, reject) => {
let results;
try {
results = _parseSeg(segArrayBuffer, imageIds);
} catch (error) {
segDisplaySet.isLoaded = false;
segDisplaySet.loadError = true;
reject(error);
}
const {
labelmapBufferArray,
segMetadata,
segmentsOnFrame,
segmentsOnFrameArray,
} = results;
let labelmapIndex;
if (labelmapBufferArray.length > 1) {
let labelmapIndexes = [];
for (let i = 0; i < labelmapBufferArray.length; ++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 {
labelmapIndex = await loadSegmentation(
imageIds,
segDisplaySet,
labelmapBufferArray[0],
segMetadata,
segmentsOnFrame,
[]
const results = await _parseSeg(segArrayBuffer, imageIds);
if (results === undefined) {
return;
}
const {
labelmapBufferArray,
segMetadata,
segmentsOnFrame,
segmentsOnFrameArray,
} = results;
let labelmapIndex;
if (labelmapBufferArray.length > 1) {
let labelmapIndexes = [];
for (let i = 0; i < labelmapBufferArray.length; ++i) {
labelmapIndexes.push(
await loadSegmentation(
imageIds,
segDisplaySet,
labelmapBufferArray[i],
segMetadata,
segmentsOnFrame,
segmentsOnFrameArray[i]
)
);
}
resolve(labelmapIndex);
});
/**
* Since overlapping segments 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 segments!');
} else {
labelmapIndex = await loadSegmentation(
imageIds,
segDisplaySet,
labelmapBufferArray[0],
segMetadata,
segmentsOnFrame,
[]
);
}
};
return segDisplaySet;
@@ -1,88 +1,16 @@
import setActiveLabelmap from './utils/setActiveLabelMap';
import { classes } from '@ohif/core';
import { metadata } from '@ohif/core';
const { ImageSet } = classes;
export default function getSourceDisplaySet(studies, segDisplaySet, activateLabelMap = true, onDisplaySetLoadFailureHandler) {
const referencedDisplaySet = metadata.StudyMetadata.getReferencedDisplaySet(segDisplaySet, studies);
export default function getSourceDisplaySet(studies, segDisplaySet) {
const referencedDisplaySet = _getReferencedDisplaySet(segDisplaySet, studies);
setActiveLabelmap(referencedDisplaySet, studies, segDisplaySet);
return referencedDisplaySet;
}
const _getReferencedDisplaySet = (segDisplaySet, studies) => {
let allDisplaySets = [];
studies.forEach(study => {
allDisplaySets = allDisplaySets.concat(study.displaySets);
});
const otherDisplaySets = allDisplaySets.filter(
ds => ds.displaySetInstanceUID !== segDisplaySet.displaySetInstanceUID
);
const { metadata } = segDisplaySet;
let referencedSeriesInstanceUIDs;
if (metadata.ReferencedSeriesSequence) {
const ReferencedSeriesSequence = _toArray(
metadata.ReferencedSeriesSequence
);
referencedSeriesInstanceUIDs = ReferencedSeriesSequence.map(
ReferencedSeries => ReferencedSeries.SeriesInstanceUID
);
} else {
const { PerFrameFunctionalGroupsSequence } = metadata;
let SourceImageSequence;
if (metadata.SourceImageSequence) {
SourceImageSequence = metadata.SourceImageSequence;
} else {
const firstFunctionalGroups = _toArray(
PerFrameFunctionalGroupsSequence
)[0];
const { DerivationImageSequence } = firstFunctionalGroups;
SourceImageSequence = DerivationImageSequence;
}
const firstSourceImage = _toArray(SourceImageSequence)[0];
const { ReferencedSOPInstanceUID } = firstSourceImage;
referencedSeriesInstanceUIDs = _findReferencedSeriesInstanceUIDsFromSOPInstanceUID(
otherDisplaySets,
ReferencedSOPInstanceUID
);
let activatedLabelmapPromise;
if (activateLabelMap) {
activatedLabelmapPromise = setActiveLabelmap(referencedDisplaySet, studies, segDisplaySet, undefined, onDisplaySetLoadFailureHandler);
}
const referencedDisplaySet = otherDisplaySets.find(ds =>
referencedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID)
);
return referencedDisplaySet;
};
const _findReferencedSeriesInstanceUIDsFromSOPInstanceUID = (
displaySets,
SOPInstanceUID
) => {
const imageSets = displaySets.filter(ds => ds instanceof ImageSet);
for (let i = 0; i < imageSets.length; i++) {
const { images } = imageSets[i];
for (let j = 0; j < images.length; j++) {
if (images[j].SOPInstanceUID === SOPInstanceUID) {
return [images[j].getData().metadata.SeriesInstanceUID];
}
}
return {
referencedDisplaySet : referencedDisplaySet,
activatedLabelmapPromise : activatedLabelmapPromise
}
};
function _toArray(arrayOrObject) {
return Array.isArray(arrayOrObject) ? arrayOrObject : [arrayOrObject];
}
+59 -1
View File
@@ -1,9 +1,12 @@
import React from 'react';
import OHIF from '@ohif/core';
import init from './init.js';
import toolbarModule from './toolbarModule.js';
import getSopClassHandlerModule from './getOHIFDicomSegSopClassHandler.js';
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
import { version } from '../package.json';
const { studyMetadataManager } = OHIF.utils;
export default {
/**
@@ -79,13 +82,51 @@ export default {
);
};
const SegmentationPanelTabUpdatedEvent = 'segmentation-panel-tab-updated';
/**
* Trigger's an event to update the state of the panel's RoundedButtonGroup.
*
* This is required to avoid extension state
* coupling with the viewer's ToolbarRow component.
*
* @param {object} data
*/
const triggerSegmentationPanelTabUpdatedEvent = data => {
const event = new CustomEvent(SegmentationPanelTabUpdatedEvent, {
detail: data,
});
document.dispatchEvent(event);
};
const onSegmentationsLoaded = ({ detail }) => {
const { segDisplaySet, segMetadata } = detail;
const studyMetadata = studyMetadataManager.get(
segDisplaySet.StudyInstanceUID
);
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
referencedSeriesInstanceUID: segMetadata.seriesInstanceUid,
Modality: 'SEG',
});
triggerSegmentationPanelTabUpdatedEvent({
badgeNumber: referencedDisplaysets.length,
target: 'segmentation-panel',
});
};
document.addEventListener(
'extensiondicomsegmentationsegloaded',
onSegmentationsLoaded
);
return {
menuOptions: [
{
icon: 'list',
label: 'Segmentations',
target: 'segmentation-panel',
isDisabled: studies => {
stateEvent: SegmentationPanelTabUpdatedEvent,
isDisabled: (studies, activeViewport) => {
if (!studies) {
return true;
}
@@ -98,6 +139,23 @@ export default {
const series = study.series[j];
if (series.Modality === 'SEG') {
if (activeViewport) {
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
if (!studyMetadata) {
return;
}
const referencedDS = studyMetadata.getDerivedDatasets({
referencedSeriesInstanceUID:
activeViewport.SeriesInstanceUID,
Modality: 'SEG',
});
triggerSegmentationPanelTabUpdatedEvent({
badgeNumber: referencedDS.length,
target: 'segmentation-panel',
});
}
return false;
}
}
@@ -59,7 +59,16 @@ export default async function loadSegmentation(
* without subscribing to external events.
*/
console.log('Segmentation loaded.');
const event = new CustomEvent('extensiondicomsegmentationsegloaded');
const event = new CustomEvent('extensiondicomsegmentationsegloaded', {
detail: {
imageIds,
segDisplaySet,
labelmapBuffer,
segMetadata,
segmentsOnFrame,
labelmapSegments,
},
});
document.dispatchEvent(event);
return labelmapIndex;
@@ -20,7 +20,7 @@ export default async function setActiveLabelmap(
displaySet,
callback = () => {},
onDisplaySetLoadFailure = err => {
throw new Error(err.message);
console.error(err.message);
}
) {
const studyMetadata = studyMetadataManager.get(
@@ -48,16 +48,15 @@ export default async function setActiveLabelmap(
}
if (!displaySet.isLoaded) {
const loadPromise = displaySet.load(referencedDisplaySet, studies);
loadPromise.catch(error => {
try {
await displaySet.load(referencedDisplaySet, studies);
} catch (error) {
displaySet.isLoaded = false;
displaySet.loadError = true;
onDisplaySetLoadFailure(error);
// Return old index.
return activeLabelmapIndex;
});
await loadPromise;
return -1;
}
}
labelmapIndex =
@@ -67,8 +66,11 @@ export default async function setActiveLabelmap(
// This might have just been created, so need to use the non-cached value.
state = cornerstoneTools.getModule('segmentation').state;
brushStackState = state.series[firstImageId];
brushStackState.activeLabelmapIndex = labelmapIndex;
if (brushStackState) {
brushStackState.activeLabelmapIndex = labelmapIndex;
}
refreshViewports();
callback();
+75
View File
@@ -3,6 +3,81 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.2.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.12...@ohif/extension-dicom-tag-browser@0.2.13) (2021-10-26)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.11...@ohif/extension-dicom-tag-browser@0.2.12) (2021-09-28)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.10...@ohif/extension-dicom-tag-browser@0.2.11) (2021-06-03)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.9...@ohif/extension-dicom-tag-browser@0.2.10) (2021-06-02)
### Bug Fixes
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
## [0.2.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.8...@ohif/extension-dicom-tag-browser@0.2.9) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.7...@ohif/extension-dicom-tag-browser@0.2.8) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.6...@ohif/extension-dicom-tag-browser@0.2.7) (2021-04-21)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.5...@ohif/extension-dicom-tag-browser@0.2.6) (2021-04-15)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.4...@ohif/extension-dicom-tag-browser@0.2.5) (2021-03-09)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.3...@ohif/extension-dicom-tag-browser@0.2.4) (2021-03-03)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-tag-browser",
"version": "0.2.4",
"version": "0.2.13",
"description": "OHIF extension for checking DICOM headers.",
"author": "OHIF",
"license": "MIT",
@@ -28,12 +28,12 @@
},
"peerDependencies": {
"@ohif/core": "^2.6.0",
"dcmjs": "0.18.2",
"dcmjs": "0.18.10",
"react": "^16.8.6"
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"dicomweb-client": "^0.6.0",
"dicomweb-client": "^0.8.1",
"moment": "2.24.0",
"react-select": "^3.0.8"
}
+201
View File
@@ -3,6 +3,207 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.12.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.4...@ohif/extension-vtk@1.12.5) (2021-10-26)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.12.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.3...@ohif/extension-vtk@1.12.4) (2021-10-15)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.12.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.2...@ohif/extension-vtk@1.12.3) (2021-10-04)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.12.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.1...@ohif/extension-vtk@1.12.2) (2021-09-28)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.12.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.0...@ohif/extension-vtk@1.12.1) (2021-09-24)
**Note:** Version bump only for package @ohif/extension-vtk
# [1.12.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.18...@ohif/extension-vtk@1.12.0) (2021-09-23)
### Features
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
## [1.11.18](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.17...@ohif/extension-vtk@1.11.18) (2021-09-13)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.17](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.16...@ohif/extension-vtk@1.11.17) (2021-09-09)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.15...@ohif/extension-vtk@1.11.16) (2021-09-03)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.14...@ohif/extension-vtk@1.11.15) (2021-08-30)
### Bug Fixes
* MPR metadata parsing for spacings ([#2517](https://github.com/OHIF/Viewers/issues/2517)) ([715dfc5](https://github.com/OHIF/Viewers/commit/715dfc556a3883de31d74012dcc99e431d91f57d))
## [1.11.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.13...@ohif/extension-vtk@1.11.14) (2021-06-03)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.12...@ohif/extension-vtk@1.11.13) (2021-06-02)
### Bug Fixes
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
## [1.11.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.11...@ohif/extension-vtk@1.11.12) (2021-05-17)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.10...@ohif/extension-vtk@1.11.11) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.9...@ohif/extension-vtk@1.11.10) (2021-04-22)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.8...@ohif/extension-vtk@1.11.9) (2021-04-21)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.7...@ohif/extension-vtk@1.11.8) (2021-04-21)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.6...@ohif/extension-vtk@1.11.7) (2021-04-21)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.5...@ohif/extension-vtk@1.11.6) (2021-04-16)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.4...@ohif/extension-vtk@1.11.5) (2021-04-15)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.3...@ohif/extension-vtk@1.11.4) (2021-03-26)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.2...@ohif/extension-vtk@1.11.3) (2021-03-25)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.1...@ohif/extension-vtk@1.11.2) (2021-03-09)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.11.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.0...@ohif/extension-vtk@1.11.1) (2021-03-09)
**Note:** Version bump only for package @ohif/extension-vtk
# [1.11.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.6...@ohif/extension-vtk@1.11.0) (2021-03-04)
+12 -12
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-vtk",
"version": "1.11.0",
"version": "1.12.5",
"description": "OHIF extension for VTK.js",
"author": "OHIF",
"license": "MIT",
@@ -32,11 +32,11 @@
"@ohif/core": "^0.50.0",
"@ohif/i18n": "^0.50.0",
"@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "0.18.2",
"dicom-parser": "^1.8.3",
"cornerstone-core": "^2.5.0",
"cornerstone-tools": "^6.0.0",
"cornerstone-wado-image-loader": "^4.0.3",
"dcmjs": "0.18.10",
"dicom-parser": "^1.8.9",
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
"prop-types": "^15.7.2",
@@ -50,14 +50,14 @@
"dependencies": {
"@babel/runtime": "^7.5.5",
"lodash.throttle": "^4.1.1",
"react-vtkjs-viewport": "^0.14.2"
"react-vtkjs-viewport": "^0.14.4"
},
"devDependencies": {
"@ohif/core": "^2.13.0",
"@ohif/ui": "^1.9.0",
"cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3",
"@ohif/core": "^2.16.1",
"@ohif/ui": "^1.10.4",
"cornerstone-tools": "^6.0.0",
"cornerstone-wado-image-loader": "^4.0.3",
"dicom-parser": "^1.8.9",
"gh-pages": "^2.0.1",
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
+3 -3
View File
@@ -72,7 +72,7 @@ class OHIFVTKViewport extends Component {
};
static defaultProps = {
onScroll: () => {},
onScroll: () => { },
};
static id = 'OHIFVTKViewport';
@@ -271,7 +271,7 @@ class OHIFVTKViewport extends Component {
volumeMapper.setSampleDistance(sampleDistance);
// Be generous to surpress warnings, as the logging really hurts performance.
// Be generous to suppress warnings, as the logging really hurts performance.
// TODO: maybe we should auto adjust samples to 1000.
volumeMapper.setMaximumSamplesPerRay(4000);
@@ -398,7 +398,7 @@ class OHIFVTKViewport extends Component {
if (
displaySet.displaySetInstanceUID !==
prevDisplaySet.displaySetInstanceUID ||
prevDisplaySet.displaySetInstanceUID ||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
displaySet.frameIndex !== prevDisplaySet.frameIndex
) {
@@ -31,63 +31,7 @@ const _isDisplaySetReconstructable = (viewportSpecificData = {}, activeViewportI
if (!displaySet) {
return false;
}
// 2D MPR is not currently available for 4D datasets.
// Assuming that slices at different time have the same position, here we just check if
// there are multiple slices for the same ImagePositionPatient and disable MPR.
// 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.
for (let ii = 0; ii < displaySet.numImageFrames; ++ii) {
const image = displaySet.images[ii];
if (!image) continue;
const imageIdControl = image.getImageId()
const instanceMetadataControl = cornerstone.metaData.get('instance', imageIdControl)
if (!instanceMetadataControl ||
instanceMetadataControl === undefined ||
!instanceMetadataControl.ImagePositionPatient ||
instanceMetadataControl.ImagePositionPatient === undefined) {
// if ImagePositionPatient is missing, skip the 4D datasets check.
// do not return false, because it could be a 3D dataset.
continue;
}
let xImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[0];
let yImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[1];
let zImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[2];
for (let jj = ii + 1; jj < displaySet.numImageFrames; ++jj) {
const image = displaySet.images[jj];
if (!image) continue;
const imageId = image.getImageId()
const instanceMetadata = cornerstone.metaData.get('instance', imageId)
if (!instanceMetadata ||
instanceMetadata === undefined ||
!instanceMetadata.ImagePositionPatient ||
instanceMetadata.ImagePositionPatient === undefined) {
// if ImagePositionPatient is missing, skip the 4D datasets check.
// do not return false, because it could be a 3D dataset.
continue;
}
let xImagePositionPatient = instanceMetadata.ImagePositionPatient[0];
let yImagePositionPatient = instanceMetadata.ImagePositionPatient[1];
let zImagePositionPatient = instanceMetadata.ImagePositionPatient[2];
if (xImagePositionPatientControl === xImagePositionPatient &&
yImagePositionPatientControl === yImagePositionPatient &&
zImagePositionPatientControl === zImagePositionPatient) {
return false;
}
}
}
};
return displaySet.isReconstructable;
};
+189
View File
@@ -3,6 +3,195 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.16.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.0...@ohif/core@2.16.1) (2021-10-26)
**Note:** Version bump only for package @ohif/core
# [2.16.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.15.1...@ohif/core@2.16.0) (2021-10-04)
### Features
* Added support for WADO servers built with static hosting (e.g. S3) ([#2566](https://github.com/OHIF/Viewers/issues/2566)) ([a046c6c](https://github.com/OHIF/Viewers/commit/a046c6c9017a84e5ead71d5113b5e892333b4130))
## [2.15.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.15.0...@ohif/core@2.15.1) (2021-09-28)
**Note:** Version bump only for package @ohif/core
# [2.15.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.14.1...@ohif/core@2.15.0) (2021-09-23)
### Features
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
## [2.14.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.14.0...@ohif/core@2.14.1) (2021-09-13)
**Note:** Version bump only for package @ohif/core
# [2.14.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.15...@ohif/core@2.14.0) (2021-09-09)
### Features
* OHIF-1000: Study prefetcher ([#2466](https://github.com/OHIF/Viewers/issues/2466)) ([8f26045](https://github.com/OHIF/Viewers/commit/8f2604509d24092b721f1333289d838820d68cac))
## [2.13.15](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.14...@ohif/core@2.13.15) (2021-09-03)
**Note:** Version bump only for package @ohif/core
## [2.13.14](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.13...@ohif/core@2.13.14) (2021-08-30)
### Bug Fixes
* MPR metadata parsing for spacings ([#2517](https://github.com/OHIF/Viewers/issues/2517)) ([715dfc5](https://github.com/OHIF/Viewers/commit/715dfc556a3883de31d74012dcc99e431d91f57d))
## [2.13.13](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.12...@ohif/core@2.13.13) (2021-06-03)
**Note:** Version bump only for package @ohif/core
## [2.13.12](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.11...@ohif/core@2.13.12) (2021-06-02)
### Bug Fixes
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
## [2.13.11](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.10...@ohif/core@2.13.11) (2021-05-17)
**Note:** Version bump only for package @ohif/core
## [2.13.10](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.9...@ohif/core@2.13.10) (2021-04-22)
**Note:** Version bump only for package @ohif/core
## [2.13.9](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.8...@ohif/core@2.13.9) (2021-04-22)
**Note:** Version bump only for package @ohif/core
## [2.13.8](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.7...@ohif/core@2.13.8) (2021-04-21)
**Note:** Version bump only for package @ohif/core
## [2.13.7](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.6...@ohif/core@2.13.7) (2021-04-21)
**Note:** Version bump only for package @ohif/core
## [2.13.6](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.5...@ohif/core@2.13.6) (2021-04-21)
**Note:** Version bump only for package @ohif/core
## [2.13.5](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.4...@ohif/core@2.13.5) (2021-04-16)
**Note:** Version bump only for package @ohif/core
## [2.13.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.3...@ohif/core@2.13.4) (2021-04-15)
### Bug Fixes
* **wl:** fix metadata provider window level issues after dicomweb update (string vs number) ([8681036](https://github.com/OHIF/Viewers/commit/8681036b2133cfc325658691a256a86b5e36705f))
## [2.13.3](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.2...@ohif/core@2.13.3) (2021-03-26)
**Note:** Version bump only for package @ohif/core
## [2.13.2](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.1...@ohif/core@2.13.2) (2021-03-25)
### Bug Fixes
* IDC-2288: Update toolbar tabs after derived displayset loaded ([#2307](https://github.com/OHIF/Viewers/issues/2307)) ([6e723eb](https://github.com/OHIF/Viewers/commit/6e723ebced58852007ca553792c740976cfc4201))
## [2.13.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.0...@ohif/core@2.13.1) (2021-03-09)
**Note:** Version bump only for package @ohif/core
# [2.13.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.5...@ohif/core@2.13.0) (2021-03-04)
+8 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/core",
"version": "2.13.0",
"version": "2.16.1",
"description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team",
"license": "MIT",
@@ -31,21 +31,22 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage"
},
"peerDependencies": {
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.20.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3"
"cornerstone-core": "^2.5.0",
"cornerstone-tools": "^6.0.0",
"cornerstone-wado-image-loader": "^4.0.3",
"dicom-parser": "^1.8.9"
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"ajv": "^6.10.0",
"dcmjs": "0.18.2",
"dicomweb-client": "^0.6.0",
"dcmjs": "0.18.10",
"dicomweb-client": "^0.8.1",
"immer": "6.0.2",
"isomorphic-base64": "^1.0.2",
"lodash.clonedeep": "^4.5.0",
"lodash.merge": "^4.6.1",
"mousetrap": "^1.6.3",
"retry": "^0.12.0",
"validate.js": "^0.12.0"
}
}
@@ -6,6 +6,7 @@ import parseDicomStructuredReport from './parseDicomStructuredReport';
import parseMeasurementsData from './parseMeasurementsData';
import getAllDisplaySets from './utils/getAllDisplaySets';
import errorHandler from '../errorHandler';
import getXHRRetryRequestHook from '../utils/xhrRetryRequestHook';
const VERSION_NAME = 'dcmjs-0.0';
const TRANSFER_SYNTAX_UID = '1.2.840.10008.1.2.1';
@@ -23,6 +24,7 @@ const retrieveMeasurementFromSR = async (series, studies, serverUrl) => {
url: serverUrl,
headers: DICOMWeb.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
requestHooks: [getXHRRetryRequestHook()],
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -74,6 +76,7 @@ const stowSRFromMeasurements = async (measurements, serverUrl) => {
url: serverUrl,
headers: DICOMWeb.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
requestHooks: [getXHRRetryRequestHook()],
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -1,7 +1,10 @@
import dcmjs from 'dcmjs';
import classes from '../classes';
import findInstanceMetadataBySopInstanceUID from './utils/findInstanceMetadataBySopInstanceUid';
const { LogManager } = classes;
/**
* Function to parse the part10 array buffer that comes from a DICOM Structured report into measurementData
* measurementData format is a viewer specific format to be stored into the redux and consumed by other components
@@ -20,9 +23,21 @@ const parseDicomStructuredReport = (part10SRArrayBuffer, displaySets) => {
);
const { MeasurementReport } = dcmjs.adapters.Cornerstone;
const storedMeasurementByToolType = MeasurementReport.generateToolState(
dataset
);
let storedMeasurementByToolType;
try {
storedMeasurementByToolType = MeasurementReport.generateToolState(dataset);
} catch (error) {
const seriesDescription = dataset.SeriesDescription || '';
LogManager.publish(LogManager.EVENTS.OnLog, {
title: `Failed to parse ${seriesDescription} measurement report`,
type: 'warning',
message: error.message || '',
notify: true,
});
return;
}
const measurementData = {};
let measurementNumber = 0;
@@ -77,7 +92,6 @@ const parseDicomStructuredReport = (part10SRArrayBuffer, displaySets) => {
/**
* Function to create imagePath with all imageData related
*
* @param {string} StudyInstanceUID
* @param {string} SeriesInstanceUID
* @param {string} SOPInstanceUID
+18
View File
@@ -0,0 +1,18 @@
import PubSub from './PubSub';
/** Log Events */
export const LogEvents = Object.freeze({
OnLog: 'onLog',
});
/**
* Log manager that implements pub/sub.
* This manager can be used to send logs across different packages
* using previously registered events.
*/
class LogManager extends PubSub {
EVENTS = LogEvents;
}
/** Singleton */
export default new LogManager();
+17 -14
View File
@@ -4,6 +4,7 @@ import dicomParser from 'dicom-parser';
import getPixelSpacingInformation from '../utils/metadataProvider/getPixelSpacingInformation';
import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPaletteColorLookupTableData';
import fetchOverlayData from '../utils/metadataProvider/fetchOverlayData';
import validNumber from '../utils/metadataProvider/validNumber';
class MetadataProvider {
constructor() {
@@ -230,26 +231,26 @@ class MetadataProvider {
let columnCosines;
if (PixelSpacing) {
rowPixelSpacing = PixelSpacing[0];
columnPixelSpacing = PixelSpacing[1];
rowPixelSpacing = validNumber(PixelSpacing[0]);
columnPixelSpacing = validNumber(PixelSpacing[1]);
}
if (ImageOrientationPatient) {
rowCosines = ImageOrientationPatient.slice(0, 3);
columnCosines = ImageOrientationPatient.slice(3, 6);
rowCosines = validNumber(ImageOrientationPatient.slice(0, 3));
columnCosines = validNumber(ImageOrientationPatient.slice(3, 6));
}
metadata = {
frameOfReferenceUID: instance.FrameOfReferenceUID,
rows: instance.Rows,
columns: instance.Columns,
imageOrientationPatient: ImageOrientationPatient,
imageOrientationPatient: validNumber(ImageOrientationPatient),
rowCosines,
columnCosines,
imagePositionPatient: instance.ImagePositionPatient,
sliceThickness: instance.SliceThickness,
sliceLocation: instance.SliceLocation,
pixelSpacing: PixelSpacing,
imagePositionPatient: validNumber(instance.ImagePositionPatient),
sliceThickness: validNumber(instance.SliceThickness),
sliceLocation: validNumber(instance.SliceLocation),
pixelSpacing: validNumber(PixelSpacing),
rowPixelSpacing,
columnPixelSpacing,
};
@@ -284,7 +285,7 @@ class MetadataProvider {
break;
case WADO_IMAGE_LOADER_TAGS.VOI_LUT_MODULE:
const { WindowCenter, WindowWidth } = instance;
let { WindowCenter, WindowWidth } = instance;
const windowCenter = Array.isArray(WindowCenter)
? WindowCenter
@@ -294,15 +295,17 @@ class MetadataProvider {
: [WindowWidth];
metadata = {
windowCenter,
windowWidth,
windowCenter: validNumber(windowCenter),
windowWidth: validNumber(windowWidth),
};
break;
case WADO_IMAGE_LOADER_TAGS.MODALITY_LUT_MODULE:
const rescaleSlope = validNumber(instance.RescaleSlope);
const rescaleIntercept = validNumber(instance.RescaleIntercept);
metadata = {
rescaleIntercept: instance.RescaleIntercept,
rescaleSlope: instance.RescaleSlope,
rescaleIntercept,
rescaleSlope,
rescaleType: instance.RescaleType,
};
break;
+75
View File
@@ -0,0 +1,75 @@
const _subscriptions = Symbol('subscriptions');
const _lastSubscriptionId = Symbol('lastSubscriptionId');
/**
* Class to implement publish/subscribe pattern
*
* @class
* @classdesc Pub/sub mechanism
*/
export default class PubSub {
constructor() {
this[_subscriptions] = {};
this[_lastSubscriptionId] = 0;
}
/**
* Subscribe to event
*
* @param {string} eventName Event name
* @param {Function} callback Callback function
* @returns {void}
*/
subscribe(eventName, callback) {
if (eventName === undefined) {
throw new Error('Event name is required');
}
if (typeof callback !== 'function') {
throw new Error('Callback must be a function');
}
if (!this[_subscriptions].hasOwnProperty(eventName)) {
this[_subscriptions][eventName] = {};
}
const subscriptionId = `sub${this[_lastSubscriptionId]++}`;
this[_subscriptions][eventName][subscriptionId] = callback;
}
/**
* Removes a subscription
*
* @param {string} eventName Event name
* @param {Function} [callback] Callback function
* @returns {void}
*/
unsubscribe(eventName, callback) {
const callbacks = this[_subscriptions][eventName] || {};
for (let subscriptionId in callbacks) {
if (!callback) {
delete callbacks[subscriptionId];
} else if (callbacks[subscriptionId] === callback) {
delete callbacks[subscriptionId];
}
}
}
/**
* Publish event to all subscriptions
*
* @param {String} eventName Event name
* @param {any} [payload] Data that will be published
* @returns {void}
*/
publish(eventName, ...payload) {
if (eventName === undefined) {
throw new Error('Event name is required');
}
const callbacks = this[_subscriptions][eventName] || {};
for (let subscriptionId in callbacks) {
callbacks[subscriptionId](...payload);
}
}
}
+118 -31
View File
@@ -1,16 +1,14 @@
import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import {
clearStudyLoadingProgress,
setStudyLoadingProgress,
} from '../redux/actions';
import debounce from 'lodash.debounce';
import StackManager from '../utils/StackManager';
import { StudyPrefetcher } from './StudyPrefetcher';
class BaseLoadingListener {
constructor(stack, options = {}) {
this.id = BaseLoadingListener.getNewId();
this.stack = stack;
this.startListening();
this.statsItemsLimit = options.statsItemsLimit || 2;
this.stats = {
items: [],
@@ -60,6 +58,9 @@ class BaseLoadingListener {
}
_getProgressId() {
/**
* TODO: The id key should be configurable.
*/
const displaySetInstanceUID = this.stack.displaySetInstanceUID;
return 'StackProgress:' + displaySetInstanceUID;
}
@@ -96,11 +97,18 @@ class BaseLoadingListener {
class DICOMFileLoadingListener extends BaseLoadingListener {
constructor(stack, options) {
super(stack, options);
this.imageLoadProgressEventHandler = this._imageLoadProgressEventHandle.bind(
this
);
this._dataSetUrl = this._getDataSetUrl(stack);
this._lastLoaded = 0;
// Check how many instances has already been download (cached)
this._checkCachedData();
this.startListening();
}
_checkCachedData() {
@@ -127,10 +135,6 @@ class DICOMFileLoadingListener extends BaseLoadingListener {
startListening() {
const imageLoadProgressEventName = this._getImageLoadProgressEventName();
this.imageLoadProgressEventHandler = this._imageLoadProgressEventHandle.bind(
this
);
this.stopListening();
cornerstone.events.addEventListener(
@@ -200,17 +204,57 @@ class DICOMFileLoadingListener extends BaseLoadingListener {
}
}
const StudyLoadingListenerEvents = {
OnProgress: 'StudyLoadingListenerEvents.OnProgress',
};
function promiseState(promise, callback) {
// Symbols and RegExps are never content-equal
var uniqueValue = window['Symbol'] ? Symbol('unique') : /unique/;
function notifyPendingOrResolved(value) {
if (value === uniqueValue) {
return callback('pending');
} else {
return callback('fulfilled');
}
}
function notifyRejected(reason) {
return callback('rejected');
}
var race = [promise, Promise.resolve(uniqueValue)];
Promise.race(race).then(notifyPendingOrResolved, notifyRejected);
}
class StackLoadingListener extends BaseLoadingListener {
constructor(stack, options = {}) {
options.statsItemsLimit = 20;
super(stack, options);
this.imageLoadedEventHandler = this._imageLoadedEventHandler.bind(this);
this.imageCachePromiseRemovedEventHandler = this._imageCachePromiseRemovedEventHandler.bind(
this
);
this.imageDataMap = this._convertImageIdsArrayToMap(stack.imageIds);
this.framesStatus = this._createArray(stack.imageIds.length, false);
this.loadedCount = 0;
// Check how many instances has already been download (cached)
this._checkCachedData();
this._debouncedSetProgressData = debounce((...args) => {
this._setProgressData(...args);
/** After checking cache, continue prefetch */
const studyPrefetcher = StudyPrefetcher.getInstance();
studyPrefetcher.prefetch(studyPrefetcher.getElement());
}, 300);
const debounced = true;
this._checkCachedData(debounced);
this.startListening();
}
_convertImageIdsArrayToMap(imageIds) {
@@ -238,18 +282,37 @@ class StackLoadingListener extends BaseLoadingListener {
return array;
}
_checkCachedData() {
// const imageIds = this.stack.imageIds;
// TODO: No way to check status of Promise.
/*for(let i = 0; i < imageIds.length; i++) {
const imageId = imageIds[i];
/**
* Check if image id is cached in cornerstone.
*
* @param {string} imageId
* @returns
*/
isImageCached(imageId) {
const image = cornerstone.imageCache.imageCache[imageId];
return image && image.sizeInBytes;
}
const imagePromise = cornerstone.imageCache.getImageLoadObject(imageId).promise;
_checkCachedData(debounced = false) {
const imageIds = this.stack.imageIds;
if (imagePromise && (imagePromise.state() === 'resolved')) {
this._updateFrameStatus(imageId, true);
}
}*/
for (let i = 0; i < imageIds.length; i++) {
const imageId = imageIds[i];
const imageObject = cornerstone.imageCache.getImageLoadObject(imageId);
if (this.isImageCached(imageId)) {
this._updateFrameStatus(imageId, true, debounced);
}
if (imageObject && imageObject.promise) {
promiseState(imageObject.promise, state => {
if (state === 'fulfilled') {
this._updateFrameStatus(imageId, true, debounced);
}
});
}
}
}
_getImageLoadedEventName() {
@@ -272,11 +335,6 @@ class StackLoadingListener extends BaseLoadingListener {
const imageLoadedEventName = this._getImageLoadedEventName();
const imageCachePromiseRemovedEventName = this._getImageCachePromiseRemoveEventName();
this.imageLoadedEventHandler = this._imageLoadedEventHandler.bind(this);
this.imageCachePromiseRemovedEventHandler = this._imageCachePromiseRemovedEventHandler.bind(
this
);
this.stopListening();
cornerstone.events.addEventListener(
@@ -303,7 +361,7 @@ class StackLoadingListener extends BaseLoadingListener {
);
}
_updateFrameStatus(imageId, loaded) {
_updateFrameStatus(imageId, loaded, debounced) {
const imageData = this.imageDataMap.get(imageId);
if (!imageData || imageData.loaded === loaded) {
@@ -318,7 +376,7 @@ class StackLoadingListener extends BaseLoadingListener {
imageData.loaded = loaded;
this.framesStatus[imageData.index] = loaded;
this.loadedCount += loaded ? 1 : -1;
this._updateProgress();
this._updateProgress(debounced);
}
_setProgressData(progressId, progressData) {
@@ -339,7 +397,7 @@ class StackLoadingListener extends BaseLoadingListener {
);
}
_updateProgress() {
_updateProgress(debounced) {
const totalFramesCount = this.stack.imageIds.length;
const loadedFramesCount = this.loadedCount;
const loadingFramesCount = totalFramesCount - loadedFramesCount;
@@ -357,6 +415,11 @@ class StackLoadingListener extends BaseLoadingListener {
framesStatus: this.framesStatus,
};
if (debounced) {
this._debouncedSetProgressData(progressId, progressData);
return;
}
this._setProgressData(progressId, progressData);
}
@@ -371,11 +434,13 @@ class StackLoadingListener extends BaseLoadingListener {
}
progressBar += ']';
log.info(`${displaySetInstanceUID}: ${progressBar}`);
console.info(`${displaySetInstanceUID}: ${progressBar}`);
}
}
class StudyLoadingListener {
static events = StudyLoadingListenerEvents;
constructor(options) {
this.listeners = {};
this.options = options;
@@ -454,14 +519,36 @@ class StudyLoadingListener {
_getSchema(stack) {
const imageId = stack.imageIds[0];
if (!imageId) {
return;
}
const colonIndex = imageId.indexOf(':');
return imageId.substring(0, colonIndex);
}
// Singleton
static getInstance(options) {
/**
* TODO: Use a different alternative without the use of events.
*/
const DEFAULT_OPTIONS = {
_setProgressData: (progressId, progressData) => {
const event = new CustomEvent(StudyLoadingListenerEvents.OnProgress, {
detail: { progressId, progressData },
});
document.dispatchEvent(event);
},
_clearProgressById: progressId => {
const event = new CustomEvent(StudyLoadingListenerEvents.OnProgress, {
detail: { progressId, percentComplete: 0 },
});
document.dispatchEvent(event);
},
};
if (!StudyLoadingListener._instance) {
StudyLoadingListener._instance = new StudyLoadingListener(options);
StudyLoadingListener._instance = new StudyLoadingListener(
options || DEFAULT_OPTIONS
);
}
return StudyLoadingListener._instance;
+322 -55
View File
@@ -1,14 +1,28 @@
import log from '../log.js';
import OHIFError from './OHIFError';
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import getImageId from '../utils/getImageId.js';
const noop = () => {};
export class StudyPrefetcher {
constructor(studies) {
options = {
order: 'closest',
displaySetCount: 1,
onImageCached: noop,
requestType: 'prefetch',
preventCache: false,
prefetchDisplaySetsTimeout: 300,
includeActiveDisplaySet: false,
};
constructor(studies, options) {
this.studies = studies || [];
this.prefetchDisplaySetsTimeout = 300;
this.lastActiveViewportElement = null;
if (options) {
this.options = { ...this.options, ...options };
this.options.requestType = 'prefetch';
}
cornerstone.events.addEventListener(
'cornerstoneimagecachefull.StudyPrefetcher',
@@ -16,6 +30,9 @@ export class StudyPrefetcher {
);
}
/**
* Remove previously set event listeners and stop prefetching.
*/
destroy() {
this.stopPrefetching();
cornerstone.events.removeEventListener(
@@ -24,95 +41,167 @@ export class StudyPrefetcher {
);
}
static getInstance() {
/**
* Get StudyPrefetcher singleton instance.
*
* @param {array} studies
* @param {object} options
* @returns
*/
static getInstance(studies = [], options) {
if (!StudyPrefetcher.instance) {
StudyPrefetcher.instance = new StudyPrefetcher([]);
StudyPrefetcher.instance = new StudyPrefetcher(studies, options);
}
if (options) {
this.options = { ...this.options, ...options };
this.options.requestType = 'prefetch';
}
return StudyPrefetcher.instance;
}
/**
* OHIF study metadata instances.
*
* @param {array} studies
*/
setStudies(studies) {
this.stopPrefetching();
this.studies = studies;
}
prefetch() {
/**
* Get previously prefetched element.
*
* @returns {HTMLElement} Previously prefetched element.
*/
getElement() {
return this.element;
}
/**
* Prefetch related display sets based on cornerstone viewport element
* with previously set options.
*
* @param {*} element
* @param {string} displaySetInstanceUID the display set instance uid
* @returns
*/
prefetch(element, displaySetInstanceUID) {
if (!this.studies || !this.studies.length) {
return;
}
this.element = element;
this.enabledElement = cornerstone.getEnabledElement(element);
this.stopPrefetching();
this.prefetchDisplaySets();
this.prefetchDisplaySets(displaySetInstanceUID);
}
/**
* Stop prefetching images.
*/
stopPrefetching() {
cornerstoneTools.requestPoolManager.clearRequestStack('prefetch');
cornerstone.imageLoadPoolManager.clearRequestStack('prefetch');
}
prefetchDisplaySetsAsync(timeout) {
timeout = timeout || this.prefetchDisplaySetsTimeout;
/**
* Prefetch display sets async.
*
* @param {HTMLElement} element cornerstone viewport element
* @param {number} timeout
*/
prefetchDisplaySetsAsync(element, timeout) {
this.enabledElement = cornerstone.getEnabledElement(element);
timeout = timeout || this.options.prefetchDisplaySetsTimeout;
clearTimeout(this.prefetchDisplaySetsHandler);
this.prefetchDisplaySetsHandler = setTimeout(() => {
this.prefetchDisplaySets();
this.prefetchDisplaySets(element);
}, timeout);
}
prefetchDisplaySets() {
// TODO: Allow passing in config
let config = {
order: 'closest',
displaySetCount: 1,
};
const displaySetsToPrefetch = this.getDisplaySetsToPrefetch(config);
/**
* Extract all image ids from all display sets to be fetched and
* call method to add images to request pool manager.
*
* @param {string} displaySetInstanceUID the display set instance uid
*/
prefetchDisplaySets(displaySetInstanceUID) {
const displaySetsToPrefetch = this.getDisplaySetsToPrefetch(
displaySetInstanceUID
);
const imageIds = this.getImageIdsFromDisplaySets(displaySetsToPrefetch);
this.prefetchImageIds(imageIds);
}
/**
* Add image ids to request pool manager.
*
* @param {array} imageIds
*/
prefetchImageIds(imageIds) {
const nonCachedImageIds = this.filterCachedImageIds(imageIds);
const requestPoolManager = cornerstoneTools.requestPoolManager;
const requestType = 'prefetch';
const preventCache = false;
const noop = () => {};
const imageLoadPoolManager = cornerstone.imageLoadPoolManager;
let requestFn;
if (this.options.preventCache) {
requestFn = id => cornerstone.loadImage(id);
} else {
requestFn = id => cornerstone.loadAndCacheImage(id);
}
nonCachedImageIds.forEach(imageId => {
requestPoolManager.addRequest(
{},
imageId,
requestType,
preventCache,
noop,
noop
imageLoadPoolManager.addRequest(
requestFn.bind(this, imageId),
this.options.requestType,
{
imageId,
}
);
});
requestPoolManager.startGrabbing();
}
/**
* Get study by cornerstone image instance.
*
* @param {object} image
* @returns
*/
getStudy(image) {
const StudyInstanceUID = cornerstone.metaData.get(
'StudyInstanceUID',
image.imageId
);
return OHIF.viewer.Studies.find(
study => study.StudyInstanceUID === StudyInstanceUID
const studies = this.studies;
return studies.find(
study => study.getData().StudyInstanceUID === StudyInstanceUID
);
}
/**
* Get study series by cornerstone image instance.
*
* @param {object} study OHIF study instance
* @param {object} image cornerstone image instance object
* @returns
*/
getSeries(study, image) {
const SeriesInstanceUID = cornerstone.metaData.get(
'SeriesInstanceUID',
image.imageId
);
const studyMetadata = OHIF.viewerbase.getStudyMetadata(study);
return studyMetadata.getSeriesByUID(SeriesInstanceUID);
return study.getSeriesByUID(SeriesInstanceUID);
}
/**
* Get sop instance by cornerstone image instance.
*
* @param {array} series
* @param {object} image
* @returns
*/
getInstance(series, image) {
const instanceMetadata = cornerstone.metaData.get(
'instance',
@@ -121,7 +210,33 @@ export class StudyPrefetcher {
return series.getInstanceByUID(instanceMetadata.SOPInstanceUID);
}
getActiveDisplaySet(displaySets, instance) {
/**
* Returns the display set with given uid.
*
* @param {string} displaySetInstanceUID the display set instance uid
* @returns {object} displaySet
*/
getDisplaySetByUID(displaySetInstanceUID) {
let displaySet;
this.studies.forEach(study => {
const ds = study.displaySets.find(
ds => ds.displaySetInstanceUID === displaySetInstanceUID
);
if (ds) {
displaySet = ds;
}
});
return displaySet;
}
/**
* Get display set by SOPInstanceUID.
*
* @param {array} displaySets
* @param {object} instance
* @returns
*/
getDisplaySetBySOPInstanceUID(displaySets, instance) {
return displaySets.find(displaySet => {
return displaySet.images.some(displaySetImage => {
return displaySetImage.SOPInstanceUID === instance.SOPInstanceUID;
@@ -129,25 +244,48 @@ export class StudyPrefetcher {
});
}
getDisplaySetsToPrefetch(config) {
/**
* Get active viewport image based on cornerstone viewport element.
* @returns
*/
getActiveViewportImage() {
if (!this.enabledElement) {
return;
}
return this.enabledElement.image;
}
/**
* Prefetch display sets based on cornerstone viewport element image.
*
* @param {string} displaySetInstanceUID the display set instance uid
* @returns {array} displaySets
*/
getDisplaySetsToPrefetch(displaySetInstanceUID) {
const image = this.getActiveViewportImage();
if (!image || !config || !config.displaySetCount) {
if (!image) {
return [];
}
/*const study = this.getStudy(image);
const study = this.getStudy(image);
const series = this.getSeries(study, image);
const instance = this.getInstance(series, image);*/
const instance = this.getInstance(series, image);
const displaySets = study.displaySets;
const activeDisplaySet = null; //this.getActiveDisplaySet(displaySets, instance);
const activeDisplaySet = displaySetInstanceUID
? this.getDisplaySetByUID(displaySetInstanceUID)
: this.getDisplaySetBySOPInstanceUID(displaySets, instance);
const prefetchMethodMap = {
topdown: 'getFirstDisplaySets',
downward: 'getNextDisplaySets',
upward: 'getPreviousDisplaySets',
closest: 'getClosestDisplaySets',
all: 'getAllDisplaySets',
};
const prefetchOrder = config.order;
const prefetchOrder = this.options.order;
const methodName = prefetchMethodMap[prefetchOrder];
const getDisplaySets = this[methodName];
@@ -163,18 +301,59 @@ export class StudyPrefetcher {
this,
displaySets,
activeDisplaySet,
config.displaySetCount
this.options.displaySetCount,
this.options.includeActiveDisplaySet
);
}
getFirstDisplaySets(displaySets, activeDisplaySet, displaySetCount) {
/**
* Get all display sets.
*
* @param {array} displaySets
* @param {object} activeDisplaySet
* @param {number} displaySetCount
* @param {boolean} includeActiveDisplaySet
* @returns
*/
getAllDisplaySets(
displaySets,
activeDisplaySet,
displaySetCount,
includeActiveDisplaySet
) {
const length = displaySets.length;
const selectedDisplaySets = [];
for (let i = 0; i < length; i++) {
const displaySet = displaySets[i];
selectedDisplaySets.push(displaySet);
}
return selectedDisplaySets;
}
/**
* Get all display sets in order after the active display set.
*
* @param {array} displaySets
* @param {object} activeDisplaySet
* @param {number} displaySetCount
* @param {boolean} includeActiveDisplaySet
* @returns
*/
getFirstDisplaySets(
displaySets,
activeDisplaySet,
displaySetCount,
includeActiveDisplaySet
) {
const length = displaySets.length;
const selectedDisplaySets = [];
for (let i = 0; i < length && displaySetCount; i++) {
const displaySet = displaySets[i];
if (displaySet !== activeDisplaySet) {
if (includeActiveDisplaySet || displaySet !== activeDisplaySet) {
selectedDisplaySets.push(displaySet);
displaySetCount--;
}
@@ -183,21 +362,78 @@ export class StudyPrefetcher {
return selectedDisplaySets;
}
getNextDisplaySets(displaySets, activeDisplaySet, displaySetCount) {
/**
* Get all display sets before the active display set.
*
* @param {array} displaySets
* @param {object} activeDisplaySet
* @param {number} displaySetCount
* @param {boolean} includeActiveDisplaySet
* @returns
*/
getPreviousDisplaySets(
displaySets,
activeDisplaySet,
displaySetCount,
includeActiveDisplaySet
) {
const activeDisplaySetIndex = displaySets.indexOf(activeDisplaySet);
const begin = activeDisplaySetIndex + 1;
const end = Math.min(begin + displaySetCount, displaySets.length);
const end = includeActiveDisplaySet
? activeDisplaySetIndex + 1
: activeDisplaySetIndex;
const previousDisplaySets = displaySets.slice(0, end);
return previousDisplaySets.reverse().slice(0, displaySetCount);
}
/**
* Get all display sets after the active display set.
*
* @param {array} displaySets
* @param {object} activeDisplaySet
* @param {number} displaySetCount
* @param {boolean} includeActiveDisplaySet
* @returns
*/
getNextDisplaySets(
displaySets,
activeDisplaySet,
displaySetCount,
includeActiveDisplaySet
) {
const activeDisplaySetIndex = displaySets.indexOf(activeDisplaySet);
const begin = includeActiveDisplaySet
? activeDisplaySetIndex
: activeDisplaySetIndex + 1;
const end = Math.min(begin + displaySetCount, displaySets.length);
return displaySets.slice(begin, end);
}
getClosestDisplaySets(displaySets, activeDisplaySet, displaySetCount) {
/**
* Get all display set closest to the active display set.
*
* @param {array} displaySets
* @param {object} activeDisplaySet
* @param {number} displaySetCount
* @param {boolean} includeActiveDisplaySet
* @returns
*/
getClosestDisplaySets(
displaySets,
activeDisplaySet,
displaySetCount,
includeActiveDisplaySet
) {
const activeDisplaySetIndex = displaySets.indexOf(activeDisplaySet);
const length = displaySets.length;
const selectedDisplaySets = [];
let left = activeDisplaySetIndex - 1;
let right = activeDisplaySetIndex + 1;
if (includeActiveDisplaySet) {
selectedDisplaySets.push(displaySets[activeDisplaySetIndex]);
displaySetCount--;
}
while ((left >= 0 || right < length) && displaySetCount) {
if (left >= 0) {
selectedDisplaySets.push(displaySets[left]);
@@ -215,6 +451,12 @@ export class StudyPrefetcher {
return selectedDisplaySets;
}
/**
* Get all image ids from display sets.
*
* @param {array} displaySets
* @returns {array} image ids
*/
getImageIdsFromDisplaySets(displaySets) {
let imageIds = [];
@@ -225,9 +467,19 @@ export class StudyPrefetcher {
return imageIds;
}
/**
* Get all image ids from a given display set.
*
* @param {array} displaySet
* @returns
*/
getImageIdsFromDisplaySet(displaySet) {
const imageIds = [];
if (!displaySet.images || displaySet.images.length < 1) {
return [];
}
// TODO: This duplicates work done by the stack manager
displaySet.images.forEach(image => {
const numFrames = image.numFrames;
@@ -245,15 +497,30 @@ export class StudyPrefetcher {
return imageIds;
}
/**
* Filter cached image ids from a set of image ids.
*
* @param {array} imageIds
* @returns {array} images not cached
*/
filterCachedImageIds(imageIds) {
return imageIds.filter(imageId => !this.isImageCached(imageId));
}
/**
* Check if image id is cached in cornerstone.
*
* @param {string} imageId
* @returns
*/
isImageCached(imageId) {
const image = cornerstone.imageCache.imageCache[imageId];
return image && image.sizeInBytes;
}
/**
* Warns that cache is full and stops prefetching.
*/
cacheFullHandler = () => {
log.warn('Cache full');
this.stopPrefetching();
+4
View File
@@ -4,6 +4,8 @@ import CommandsManager from './CommandsManager.js';
import { DICOMFileLoadingListener } from './StudyLoadingListener';
import HotkeysManager from './HotkeysManager.js';
import ImageSet from './ImageSet';
import LogManager from './LogManager';
import PubSub from './PubSub';
import MetadataProvider from './MetadataProvider';
import OHIFError from './OHIFError.js';
import { OHIFStudyMetadataSource } from './OHIFStudyMetadataSource';
@@ -36,7 +38,9 @@ const classes = {
MetadataProvider,
CommandsManager,
HotkeysManager,
LogManager,
ImageSet,
PubSub,
StudyPrefetcher,
StudyLoadingListener,
StackLoadingListener,
@@ -9,11 +9,12 @@ import { SeriesMetadata } from './SeriesMetadata';
import { api } from 'dicomweb-client';
// - createStacks
import { isImage } from '../../utils/isImage';
import isDisplaySetReconstructable from '../../utils/isDisplaySetReconstructable';
import { isDisplaySetReconstructable, isSpacingUniform } from '../../utils/isDisplaySetReconstructable';
import errorHandler from '../../errorHandler';
import isLowPriorityModality from '../../utils/isLowPriorityModality';
import getXHRRetryRequestHook from '../../utils/xhrRetryRequestHook';
export class StudyMetadata extends Metadata {
class StudyMetadata extends Metadata {
constructor(data, uid) {
super(data, uid);
// Initialize Private Properties
@@ -226,6 +227,56 @@ export class StudyMetadata extends Metadata {
displaySets.map(displaySet => this._derivedDisplaySets.push(displaySet));
}
/**
* Returns the source display set of the derivated display set.
* @param {object} derivatedDisplaySet
* @param {array[StudyMetadata]} studies
* @return {object} source display set.
*/
static getReferencedDisplaySet(derivatedDisplaySet, studies) {
let allDisplaySets = [];
studies.forEach(study => {
allDisplaySets = allDisplaySets.concat(study.displaySets);
});
const otherDisplaySets = allDisplaySets.filter(
ds => ds.displaySetInstanceUID !== derivatedDisplaySet.displaySetInstanceUID
);
const { metadata } = derivatedDisplaySet;
let referencedSeriesInstanceUIDs = _findReferencedSeriesInstanceUIDsFromSourceImageSequence
(metadata, otherDisplaySets);
let noReferencedSeriesAvailable = !referencedSeriesInstanceUIDs ||
referencedSeriesInstanceUIDs.length === 0;
if (noReferencedSeriesAvailable) {
referencedSeriesInstanceUIDs =
_findReferencedSeriesInstanceUIDsFromReferencedSeriesSequence
(metadata);
}
noReferencedSeriesAvailable = !referencedSeriesInstanceUIDs ||
referencedSeriesInstanceUIDs.length === 0;
if (noReferencedSeriesAvailable) {
referencedSeriesInstanceUIDs =
_findReferencedSeriesInstanceUIDsFromReferencedImageSequence
(metadata, otherDisplaySets);
}
const referencedSeriesAvailable = referencedSeriesInstanceUIDs &&
referencedSeriesInstanceUIDs.length !== 0;
if (referencedSeriesAvailable) {
const referencedDisplaySet = otherDisplaySets.find(ds =>
referencedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID)
);
;
return referencedDisplaySet;
}
};
/**
* Returns a list of derived datasets in the study, filtered by the given filter.
* @param {object} filter An object containing search filters
@@ -252,20 +303,7 @@ export class StudyMetadata extends Metadata {
if (referencedSeriesInstanceUID) {
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
displaySet => {
if (!displaySet.metadata.ReferencedSeriesSequence) {
return false;
}
const ReferencedSeriesSequence = Array.isArray(
displaySet.metadata.ReferencedSeriesSequence
)
? displaySet.metadata.ReferencedSeriesSequence
: [displaySet.metadata.ReferencedSeriesSequence];
return ReferencedSeriesSequence.some(
ReferencedSeries =>
ReferencedSeries.SeriesInstanceUID === referencedSeriesInstanceUID
);
return StudyMetadata.getReferencedDisplaySet(displaySet, [this]).SeriesInstanceUID === referencedSeriesInstanceUID;
}
);
}
@@ -273,7 +311,7 @@ export class StudyMetadata extends Metadata {
if (referencedFrameOfReferenceUID) {
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
displaySet =>
displaySet.ReferencedFrameOfReferenceUID ===
displaySet.FrameOfReferenceUID ===
referencedFrameOfReferenceUID
);
}
@@ -459,6 +497,12 @@ export class StudyMetadata extends Metadata {
}
}
if (this._displaySets.some(ds =>
ds.displaySetInstanceUID === displaySet.displaySetInstanceUID)
) {
return;
}
this._displaySets.splice(insertIndex, 0, displaySet);
this.displaySets = this._displaySets;
}
@@ -767,6 +811,16 @@ const isMultiFrame = instance => {
return instance.getTagValue('NumberOfFrames') > 1;
};
/**
* Creates a display set for a series.
* Checks if a series is reconstructable to a 3D volume.
* If reconstructable, the frames are sorted.
*
* @param {SeriesMetadata} series The series metadata object from which the display sets will be created
* @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
*
* @returns {Object} imageSet.
*/
const makeDisplaySet = (series, instances) => {
const instance = instances[0];
const imageSet = new ImageSet(instances);
@@ -786,7 +840,7 @@ const makeDisplaySet = (series, instances) => {
isMultiFrame: isMultiFrame(instance),
});
// Sort the images in this series if needed
// Sort the images in this series by instanceNumber
const shallSort = true; //!OHIF.utils.ObjectPath.get(Meteor, 'settings.public.ui.sortSeriesByIncomingOrder');
if (shallSort) {
imageSet.sortBy((a, b) => {
@@ -804,18 +858,32 @@ const makeDisplaySet = (series, instances) => {
imageSet.getImage(0).getTagValue('InstanceNumber')
);
const isReconstructable = isDisplaySetReconstructable(instances);
imageSet.isReconstructable = isReconstructable.value;
const displayReconstructableInfo = isDisplaySetReconstructable(instances);
imageSet.isReconstructable = displayReconstructableInfo.value;
let displaySpacingInfo = undefined;
if (shallSort && imageSet.isReconstructable) {
// sort images by image position
imageSet.sortByImagePositionPatient();
// check if the spacing is uniform and update isReconstructable
const datasetIs4D = displayReconstructableInfo.reconstructionIssues.find
(issue => issue === ReconstructionIssues.DATASET_4D);
displaySpacingInfo = isSpacingUniform(imageSet.images, datasetIs4D);
imageSet.isReconstructable = displaySpacingInfo.isUniform;
if (displaySpacingInfo.missingFrames) {
// TODO -> This is currently unused, but may be used for reconstructing
// Volumes with gaps later on.
imageSet.missingFrames = displaySpacingInfo.missingFrames;
}
}
if (isReconstructable.missingFrames) {
// TODO -> This is currently unused, but may be used for reconstructing
// Volumes with gaps later on.
imageSet.missingFrames = isReconstructable.missingFrames;
if (!imageSet.displayReconstructableInfo) {
// It is not reconstrabale Save type of warning
imageSet.reconstructionIssues = displaySpacingInfo ?
displayReconstructableInfo.reconstructionIssues.concat(displaySpacingInfo.reconstructionIssues) :
displayReconstructableInfo.reconstructionIssues;
}
return imageSet;
@@ -878,6 +946,7 @@ function _getDisplaySetFromSopClassModule(
url: study.getData().wadoRoot,
headers,
errorInterceptor,
requestHooks: [getXHRRetryRequestHook()],
});
let displaySet = plugin.getDisplaySetFromSeries(
@@ -892,3 +961,147 @@ function _getDisplaySetFromSopClassModule(
}
return displaySet;
}
/**
* Returns the referenced series instance UIDs by searching the information in the
* ReferencedSeriesSequence.
* @param {object} derivatedDisplaySet.metadata
* @return {array[string]} referenced series instance UIDs.
*/
function _findReferencedSeriesInstanceUIDsFromReferencedSeriesSequence (
metadata,
) {
if (!metadata.ReferencedSeriesSequence) {
return;
}
let referencedSeriesInstanceUIDs;
const ReferencedSeriesSequence = _toArray(
metadata.ReferencedSeriesSequence
);
referencedSeriesInstanceUIDs = ReferencedSeriesSequence.map(
ReferencedSeries => ReferencedSeries.SeriesInstanceUID
);
return referencedSeriesInstanceUIDs;
};
/**
* Returns the referenced series instance UIDs by searching the information in the
* ReferencedImageSequence.
* @param {object} derivatedDisplaySet.metadata
* @param {array[object]} displaysets
* @return {array[string]} referenced series instance UIDs.
*/
function _findReferencedSeriesInstanceUIDsFromReferencedImageSequence (
metadata,
displaySets
) {
if (!metadata.ReferencedImageSequence) {
return;
}
let referencedSeriesInstanceUIDs;
const referencedImageArray = _toArray(metadata.ReferencedImageSequence);
for (let i = 0; i < referencedImageArray.length; i++) {
const { ReferencedSOPInstanceUID } = referencedImageArray[i];
if (!ReferencedSOPInstanceUID) {
continue;
}
referencedSeriesInstanceUIDs = _findReferencedSeriesInstanceUIDsFromSOPInstanceUID(
displaySets,
ReferencedSOPInstanceUID
);
if (referencedSeriesInstanceUIDs && referencedSeriesInstanceUIDs.length !== 0) {
break;
}
}
return referencedSeriesInstanceUIDs;
};
/**
* Returns the referenced series instance UIDs by searching the information in the
* SourceImageSequence.
* @param {object} derivatedDisplaySet.metadata
* @param {array[object]} displaysets
* @return {array[string]} referenced series instance UIDs.
*/
function _findReferencedSeriesInstanceUIDsFromSourceImageSequence (
metadata,
displaySets
) {
let SourceImageSequence;
if (metadata.SourceImageSequence) {
SourceImageSequence = metadata.SourceImageSequence;
} else {
const { PerFrameFunctionalGroupsSequence } = metadata;
const firstFunctionalGroups = _toArray(
PerFrameFunctionalGroupsSequence
)[0];
if (firstFunctionalGroups) {
const { DerivationImageSequence } = firstFunctionalGroups;
SourceImageSequence = DerivationImageSequence;
}
}
if (!SourceImageSequence) {
return;
}
const sourceImageArray = _toArray(SourceImageSequence);
let referencedSeriesInstanceUIDs;
for (let i = 0; i < sourceImageArray.length; i++) {
const { ReferencedSOPInstanceUID } = sourceImageArray[i];
referencedSeriesInstanceUIDs = _findReferencedSeriesInstanceUIDsFromSOPInstanceUID(
displaySets,
ReferencedSOPInstanceUID
);
if (referencedSeriesInstanceUIDs && referencedSeriesInstanceUIDs.length !== 0) {
break;
}
}
return referencedSeriesInstanceUIDs;
};
/**
* Returns the referenced series instance UIDs by searching the information in the
* SOPInstanceUID of the displaySets.
* @param {array[object]} displaysets
* @param {string} SOPInstanceUID
* @return {array[string]} referenced series instance UIDs.
*/
function _findReferencedSeriesInstanceUIDsFromSOPInstanceUID (
displaySets,
SOPInstanceUID
) {
const imageSets = displaySets.filter(ds => ds instanceof ImageSet);
for (let i = 0; i < imageSets.length; i++) {
const { images } = imageSets[i];
if (!images) {
continue;
}
for (let j = 0; j < images.length; j++) {
const image = images[j];
if (!image) {
continue;
}
if (image.SOPInstanceUID === SOPInstanceUID) {
return [image.getData().metadata.SeriesInstanceUID];
}
}
}
};
function _toArray(arrayOrObject) {
return Array.isArray(arrayOrObject) ? arrayOrObject : [arrayOrObject];
}
export {StudyMetadata};
+11
View File
@@ -0,0 +1,11 @@
const ReconstructionIssues = {
DATASET_4D: 'datasetis4D',
VARYING_IMAGESDIMENSIONS: 'imagesdimensionsvarying',
VARYING_IMAGESCOMPONENTS: 'imagescomponentsvarying',
VARYING_IMAGESORIENTATION: 'imagesorientationvarying',
MISSING_FRAMES: 'missingframes',
IRREGULAR_SPACING: 'irregularspacing',
MULTIFFRAMES: 'multiframe',
};
export {ReconstructionIssues};
-13
View File
@@ -78,17 +78,6 @@ export const setActiveViewportSpecificData = viewportSpecificData => ({
/**
* NOT-VIEWPORT
*/
export const setStudyLoadingProgress = (progressId, progressData) => ({
type: 'SET_STUDY_LOADING_PROGRESS',
progressId,
progressData,
});
export const clearStudyLoadingProgress = progressId => ({
type: 'CLEAR_STUDY_LOADING_PROGRESS',
progressId,
});
export const setUserPreferences = state => ({
type: SET_USER_PREFERENCES,
state,
@@ -134,8 +123,6 @@ const actions = {
/**
* NOT-VIEWPORT
*/
setStudyLoadingProgress,
clearStudyLoadingProgress,
setUserPreferences,
setExtensionData,
setTimepoints,
-2
View File
@@ -11,8 +11,6 @@ describe('actions', () => {
'setLayout',
'clearViewportSpecificData',
'setActiveViewportSpecificData',
'setStudyLoadingProgress',
'clearStudyLoadingProgress',
'setUserPreferences',
'setExtensionData',
'setTimepoints',
@@ -1,5 +1,4 @@
import extensions from './extensions';
import loading from './loading';
import preferences from './preferences';
import servers from './servers';
import studies from './studies';
@@ -8,7 +7,6 @@ import viewports from './viewports';
const reducers = {
extensions,
loading,
preferences,
servers,
studies,
@@ -1,33 +0,0 @@
import cloneDeep from 'lodash.clonedeep';
const defaultState = {
progress: {},
lastUpdated: null,
};
const loading = (state = defaultState, action) => {
let progress;
let lastUpdated;
switch (action.type) {
case 'SET_STUDY_LOADING_PROGRESS':
progress = cloneDeep(state).progress;
progress[action.progressId] = action.progressData;
// This is a workaround so we can easily identify changes
// to the progress object without doing deep comparison.
lastUpdated = new Date().getTime();
return Object.assign({}, state, { progress, lastUpdated });
case 'CLEAR_STUDY_LOADING_PROGRESS':
progress = cloneDeep(state).progress;
delete progress[action.progressId];
lastUpdated = new Date().getTime();
return Object.assign({}, state, { progress, lastUpdated });
default:
return state;
}
};
export default loading;
@@ -260,7 +260,6 @@ class MeasurementService {
const sourceInfo = this._getSourceInfo(source);
if (!definition) {
console.log('TEST');
log.warn('No source definition provided. Exiting early.');
return;
}
@@ -0,0 +1,85 @@
import { api } from 'dicomweb-client';
/**
* An implementation of the static wado client, that fetches data from
* a static response rather than actually doing real queries. This allows
* fast encoding of test data, but because it is static, anything actually
* performing searches doesn't work. This version fixes the query issue
* by manually implementing a query option.
*/
export default class StaticWadoClient extends api.DICOMwebClient {
static filterKeys = {
StudyInstanceUID: '0020000D',
PatientName: '00100010',
PatientID: '00100020',
StudyDescription: '00081030',
ModalitiesInStudy: '00080061',
};
constructor(qidoConfig) {
super(qidoConfig);
this.staticWado = qidoConfig.staticWado;
this.extendMetadataWithInstances = qidoConfig.extendMetadataWithInstances;
}
async retrieveSeriesMetadata(options) {
if (!this.extendMetadataWithInstances)
return super.retrieveSeriesMetadata(options);
const results = await Promise.all([
super.retrieveSeriesMetadata(options),
this.searchForInstances(options),
]);
const metadata = results[0];
const instances = results[1];
return metadata.map(item => {
const sopUID = item['00080018'].Value[0];
const instance = instances.find(
instance => instance['00080018'].Value[0] == sopUID
);
Object.assign(item, instance);
return item;
});
}
async searchForStudies(options) {
if (!this.staticWado) return super.searchForStudies(options);
let searchResult = await super.searchForStudies(options);
const { queryParams } = options;
if (!queryParams) return searchResult;
const filtered = searchResult.filter(study => {
for (const key of Object.keys(StaticWadoClient.filterKeys)) {
if (!this.filterItem(key, queryParams, study)) return false;
}
return true;
});
return filtered;
}
filterItem(key, queryParams, study) {
const altKey = StaticWadoClient.filterKeys[key] || key;
if (!queryParams) return true;
const testValue = queryParams[key] || queryParams[altKey];
if (!testValue) return true;
const valueElem = study[key] || study[altKey];
if (!valueElem) return false;
const value = valueElem.Value;
return this.eqItem(testValue, value);
}
eqItem(testValue, value) {
if (testValue.filter) {
return (
testValue.filter(testItem => this.eqItem(testItem, value)).length > 0
);
}
if (value === testValue) return true;
if (typeof value == 'string') {
return value.indexOf(testValue) != -1;
}
if (value.Alphabetic) return this.eqItem(testValue, value.Alphabetic);
if (value.filter)
return value.filter(item => this.eqItem(testValue, item)).length > 0;
return false;
}
}
@@ -1,7 +1,9 @@
import { api } from 'dicomweb-client';
import StaticWadoClient from './StaticWadoClient';
import DICOMWeb from '../../../DICOMWeb/';
import errorHandler from '../../../errorHandler';
import getXHRRetryRequestHook from '../../../utils/xhrRetryRequestHook';
/**
* Creates a QIDO date string for a date range query
@@ -114,13 +116,18 @@ function resultDataToStudies(resultData) {
}
export default function Studies(server, filter) {
const { staticWado } = server;
const config = {
...server,
url: server.qidoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
requestHooks: [getXHRRetryRequestHook()],
};
const dicomWeb = new api.DICOMwebClient(config);
const dicomWeb = staticWado
? new StaticWadoClient(config)
: new api.DICOMwebClient(config);
server.qidoSupportsIncludeField =
server.qidoSupportsIncludeField === undefined
? true
@@ -1,4 +1,4 @@
import { api } from 'dicomweb-client';
import StaticWadoClient from '../qido/StaticWadoClient';
import dcmjs from 'dcmjs';
import DICOMWeb from '../../../DICOMWeb/';
import RetrieveMetadataLoader from './retrieveMetadataLoader';
@@ -10,6 +10,7 @@ import {
} from './studyInstanceHelpers';
import errorHandler from '../../../errorHandler';
import { getXHRRetryRequestHook } from '../../../utils/xhrRetryRequestHook';
const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary;
@@ -72,10 +73,12 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
configLoad() {
const { server } = this;
const client = new api.DICOMwebClient({
const client = new StaticWadoClient({
...server,
url: server.qidoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
requestHooks: [getXHRRetryRequestHook()],
});
this.client = client;
@@ -4,6 +4,7 @@ import { createStudyFromSOPInstanceList } from './studyInstanceHelpers';
import RetrieveMetadataLoader from './retrieveMetadataLoader';
import errorHandler from '../../../errorHandler';
import getXHRRetryRequestHook from '../../../utils/xhrRetryRequestHook';
/**
* Class for sync load of study metadata.
@@ -61,6 +62,7 @@ export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
url: server.wadoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
requestHooks: [getXHRRetryRequestHook()],
});
this.client = client;
@@ -4,6 +4,7 @@ import { api } from 'dicomweb-client';
import DICOMWeb from '../DICOMWeb';
import errorHandler from '../errorHandler';
import getXHRRetryRequestHook from './xhrRetryRequestHook';
const getImageId = imageObj => {
if (!imageObj) {
@@ -66,6 +67,7 @@ const wadorsRetriever = (
url,
headers,
errorInterceptor,
requestHooks: [getXHRRetryRequestHook()],
};
const dicomWeb = new api.DICOMwebClient(config);
+3
View File
@@ -18,6 +18,7 @@ import isDicomUid from './isDicomUid';
import resolveObjectPath from './resolveObjectPath';
import * as hierarchicalListUtils from './hierarchicalListUtils';
import * as progressTrackingUtils from './progressTrackingUtils';
import xhrRetryRequestHook from './xhrRetryRequestHook';
const utils = {
guid,
@@ -40,6 +41,7 @@ const utils = {
resolveObjectPath,
hierarchicalListUtils,
progressTrackingUtils,
xhrRetryRequestHook,
};
export {
@@ -63,6 +65,7 @@ export {
resolveObjectPath,
hierarchicalListUtils,
progressTrackingUtils,
xhrRetryRequestHook,
};
export default utils;
+1
View File
@@ -23,6 +23,7 @@ describe('Top level exports', () => {
'resolveObjectPath',
'hierarchicalListUtils',
'progressTrackingUtils',
'xhrRetryRequestHook',
].sort();
const exports = Object.keys(utils.default).sort();
@@ -1,9 +1,14 @@
import { ReconstructionIssues } from './../enums.js';
/**
* Checks if a series is reconstructable to a 3D volume.
*
* @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
* @param {Object[]} An array of `OHIFInstanceMetadata` objects.
*
* @returns {Object} value, reconstructionIssues.
*/
export default function isDisplaySetReconstructable(instances) {
function isDisplaySetReconstructable(instances) {
if (!instances.length) {
return { value: false };
}
@@ -23,31 +28,45 @@ export default function isDisplaySetReconstructable(instances) {
}
if (isMultiframe) {
return processMultiframe(instances[0]);
return processMultiframe();
} else {
return processSingleframe(instances);
}
}
function processMultiframe(instance) {
//TODO: deal with multriframe checks! return false for now as can't reconstruct.
return { value: false };
/**
* Process reconstructable multiframes checks
* TODO: deal with multriframe checks! return false for now as can't reconstruct.
* *
* @returns {Object} value and reconstructionIssues.
*/
function processMultiframe() {
const reconstructionIssues = [ReconstructionIssues.MULTIFRAMES];
return { value: false, reconstructionIssues };
}
/**
* Process reconstructable single frame checks
*
* @param {Object[]} An array of `OHIFInstanceMetadata` objects.
*
* @returns {Object} value and reconstructionIssues.
*/
function processSingleframe(instances) {
const n = instances.length;
const firstImage = instances[0].getData().metadata;
const firstImageRows = firstImage.Rows;
const firstImageColumns = firstImage.Columns;
const firstImageSamplesPerPixel = firstImage.SamplesPerPixel;
const firstImageOrientationPatient = firstImage.ImageOrientationPatient;
const firstImagePositionPatient = firstImage.ImagePositionPatient;
const reconstructionIssues = [];
// Can't reconstruct if we:
// -- Have a different dimensions within a displaySet.
// -- Have a different number of components within a displaySet.
// -- Have different orientations within a displaySet.
for (let i = 1; i < instances.length; i++) {
const instance = instances[i].getData().metadata;
for (let ii = 1; ii < n; ++ii) {
const instance = instances[ii].getData().metadata;
const {
Rows,
Columns,
@@ -55,67 +74,146 @@ function processSingleframe(instances) {
ImageOrientationPatient,
} = instance;
if (
Rows !== firstImageRows ||
Columns !== firstImageColumns ||
SamplesPerPixel !== firstImageSamplesPerPixel ||
!_isSameOrientation(ImageOrientationPatient, firstImageOrientationPatient)
) {
return { value: false };
if (Rows !== firstImageRows || Columns !== firstImageColumns) {
reconstructionIssues.push(ReconstructionIssues.VARYING_IMAGESDIMENSIONS);
} else if (SamplesPerPixel !== firstImageSamplesPerPixel) {
reconstructionIssues.push(ReconstructionIssues.VARYING_IMAGESCOMPONENTS);
} else if (!_isSameArray(ImageOrientationPatient, firstImageOrientationPatient)) {
reconstructionIssues.push(ReconstructionIssues.VARYING_IMAGESORIENTATION);
}
if (reconstructionIssues.length !== 0) {
break;
}
}
// check if dataset is 4D
if (_isDataset4D(instances)) {
reconstructionIssues.push(ReconstructionIssues.DATASET_4D);
}
return { value: reconstructionIssues.length === 0 ? true : false, reconstructionIssues };
}
/**
* Check is the spacing is uniform.
* The input metadata array has to be ordered by image position.
*
* @param {Object[]} An array of `OHIFInstanceMetadata` objects.
* @param {boolean} is the dataset 4D.
*
* @returns {Object} isUniform, reconstructionIssues and missingFrames
*/
function isSpacingUniform(instances, datasetIs4D) {
const n = instances.length;
const firstImage = instances[0].getData().metadata;
const firstImagePositionPatient = firstImage.ImagePositionPatient;
const reconstructionIssues = [];
let missingFrames = 0;
// Check if frame spacing is approximately equal within a spacingTolerance.
// If spacing is on a uniform grid but we are missing frames,
// Allow reconstruction, but pass back the number of missing frames.
if (instances.length > 2) {
const lastIpp = instances[instances.length - 1].getData().metadata
if (n > 2) {
const lastIpp = instances[n - 1].getData().metadata
.ImagePositionPatient;
// We can't reconstruct if we are missing ImagePositionPatient values
if (!firstImagePositionPatient || !lastIpp) {
return { value: false };
}
if (firstImagePositionPatient && lastIpp) {
const averageSpacingBetweenFrames =
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
(n - 1);
const averageSpacingBetweenFrames =
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
(instances.length - 1);
let previousImagePositionPatient = firstImagePositionPatient;
let previousImagePositionPatient = firstImagePositionPatient;
for (let ii = 1; ii < n; ++ii) {
const instance = instances[ii].getData().metadata;
const { ImagePositionPatient } = instance;
for (let i = 1; i < instances.length; i++) {
const instance = instances[i].getData().metadata;
const { ImagePositionPatient } = instance;
const spacingBetweenFrames = _getPerpendicularDistance(
ImagePositionPatient,
previousImagePositionPatient
);
const spacingBetweenFrames = _getPerpendicularDistance(
ImagePositionPatient,
previousImagePositionPatient
);
const spacingIssue = _getSpacingIssue(
spacingBetweenFrames,
averageSpacingBetweenFrames
);
if (spacingIssue) {
const issue = spacingIssue.issue;
if (issue === reconstructionIssues.MISSING_FRAMES) {
missingFrames += spacingIssue.missingFrames;
} else if (issue === reconstructionIssues.IRREGULAR_SPACING) {
return { value: false };
if (datasetIs4D && spacingBetweenFrames < 1.e-3) {
// the dataset is 4D, if the distance is zero, means that we are
// checking the 4th dimension. Do not return, since we want still to
// check the 3rd dimension spacing.
continue;
}
}
previousImagePositionPatient = ImagePositionPatient;
const spacingIssue = _getSpacingIssue(
spacingBetweenFrames,
averageSpacingBetweenFrames
);
if (spacingIssue) {
const issue = spacingIssue.issue;
if (issue === ReconstructionIssues.MISSING_FRAMES) {
missingFrames += spacingIssue.missingFrames;
} else if (issue === ReconstructionIssues.IRREGULAR_SPACING) {
reconstructionIssues.push(issue);
break;
}
}
previousImagePositionPatient = ImagePositionPatient;
}
}
}
return { value: true, missingFrames };
return { isUniform: reconstructionIssues.length === 0 ? true : false, missingFrames, reconstructionIssues };
}
function _isSameOrientation(iop1, iop2) {
/**
* Check if 4D dataset.
*
* Assuming that slices at different time have the same position, here we just check if
* there are multiple slices for the same ImagePositionPatient and disable MPR.
*
* 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.
*
* @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
*
* @returns {boolean} dataset4D value.
*/
function _isDataset4D(instances) {
const n = instances.length;
for (let ii = 0; ii < n; ++ii) {
const instanceMetadataControl = instances[ii].getData().metadata;
if (
!instanceMetadataControl ||
instanceMetadataControl === undefined ||
!instanceMetadataControl.ImagePositionPatient ||
instanceMetadataControl.ImagePositionPatient === undefined
) {
continue;
}
for (let jj = ii + 1; jj < n; ++jj) {
const instanceMetadata = instances[jj].getData().metadata;
if (
!instanceMetadata ||
instanceMetadata === undefined ||
!instanceMetadata.ImagePositionPatient ||
instanceMetadata.ImagePositionPatient === undefined
) {
continue;
}
if (_isSameArray(instanceMetadataControl.ImagePositionPatient, instanceMetadata.ImagePositionPatient)) {
return true;
}
}
}
return false;
}
function _isSameArray(iop1, iop2) {
if (iop1 === undefined || !iop2 === undefined) {
return;
}
@@ -156,12 +254,12 @@ function _getSpacingIssue(spacing, averageSpacing) {
if (errorForEachSpacing < spacingTolerance * averageSpacing) {
return {
issue: reconstructionIssues.MISSING_FRAMES,
issue: ReconstructionIssues.MISSING_FRAMES,
missingFrames: numberOfSpacings - 1,
};
}
return { issue: reconstructionIssues.IRREGULAR_SPACING };
return { issue: ReconstructionIssues.IRREGULAR_SPACING };
}
function _getPerpendicularDistance(a, b) {
@@ -173,7 +271,5 @@ function _getPerpendicularDistance(a, b) {
}
const constructableModalities = ['MR', 'CT', 'PT', 'NM'];
const reconstructionIssues = {
MISSING_FRAMES: 'missingframes',
IRREGULAR_SPACING: 'irregularspacing',
};
export {isDisplaySetReconstructable, isSpacingUniform};
@@ -45,13 +45,13 @@ import studyMetadataManager from './studyMetadataManager';
* @param {string} referencedDisplaySet.studyInstanceUid
* @param {Array} referencedDisplaySet.sopClassUids
* @param {Study[]} studies Collection of studies
* @param {object} logger
* @param {object} snackbar
* @returns void
*/
const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
async function loadAndCacheDerivedDisplaySets(referencedDisplaySet, studies, logger, snackbar) {
const { StudyInstanceUID, SeriesInstanceUID } = referencedDisplaySet;
const promises = [];
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
if (!studyMetadata) {
@@ -80,11 +80,10 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
});
// For each type, see if any are loaded, if not load the most recent.
Object.keys(displaySetsPerModality).forEach(key => {
await Promise.all(Object.keys(displaySetsPerModality).map(async (key) => {
const displaySets = displaySetsPerModality[key];
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
if (isLoaded) {
return;
}
@@ -107,12 +106,34 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
}
});
recentDisplaySet.isLoading = true;
try {
if (recentDisplaySet.hasOwnProperty('getSourceDisplaySet') &&
typeof recentDisplaySet.getSourceDisplaySet === 'function') {
await recentDisplaySet.getSourceDisplaySet(studies);
} else {
await recentDisplaySet.load(referencedDisplaySet, studies);
}
} catch (error) {
recentDisplaySet.isLoaded = false;
recentDisplaySet.loadError = true;
logger.error({ error, message: error.message });
snackbar.show({
title: 'Error loading derived display set:',
message: error.message,
type: 'error',
error,
autoClose: false,
});
}
}));
promises.push(recentDisplaySet.load(referencedDisplaySet, studies));
});
return promises;
/*
* TODO: Improve the way we notify parts of the app
* that depends on derived display sets to be loaded.
* (Implement pubsub for better tracking of derived display sets)
*/
const event = new CustomEvent('deriveddisplaysetsloadedandcached');
document.dispatchEvent(event);
};
export default loadAndCacheDerivedDisplaySets;
@@ -4,6 +4,7 @@ import str2ab from '../str2ab';
import unpackOverlay from './unpackOverlay';
import errorHandler from '../../errorHandler';
import getXHRRetryRequestHook from '../xhrRetryRequestHook';
export default async function fetchOverlayData(instance, server) {
const OverlayDataPromises = [];
@@ -69,6 +70,7 @@ async function _getOverlayData(tag, server) {
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
requestHooks: [getXHRRetryRequestHook()],
};
const dicomWeb = new api.DICOMwebClient(config);
const options = {
@@ -3,6 +3,7 @@ import DICOMWeb from '../../DICOMWeb';
import str2ab from '../str2ab';
import errorHandler from '../../errorHandler';
import getXHRRetryRequestHook from '../xhrRetryRequestHook';
export default async function fetchPaletteColorLookupTableData(
instance,
@@ -141,6 +142,7 @@ function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
requestHooks: [getXHRRetryRequestHook()],
};
const dicomWeb = new api.DICOMwebClient(config);
const options = {
@@ -0,0 +1,9 @@
const validNumber = val => {
if (Array.isArray(val)) {
return val.map(v => (v !== undefined ? Number(v) : v));
} else {
return val !== undefined ? Number(val) : val;
}
};
export default validNumber;
@@ -0,0 +1,103 @@
import retry from 'retry';
const defaultRetryOptions = {
retries: 5,
factor: 3,
minTimeout: 1 * 1000,
maxTimeout: 60 * 1000,
randomize: true,
retryableStatusCodes: [429, 500],
};
let retryOptions = { ...defaultRetryOptions };
/**
* Request hook used to add retry functionality to XHR requests.
*
* @param {XMLHttpRequest} request XHR request instance
* @param {object} metadata Metadata about the request
* @param {object} metadata.url URL
* @param {object} metadata.method HTTP method
* @returns {XMLHttpRequest} request instance optionally modified
*/
const xhrRetryRequestHook = (request, metadata) => {
const { url, method } = metadata;
function faultTolerantRequestSend(...args) {
const operation = retry.operation(retryOptions);
operation.attempt(function operationAttempt(currentAttempt) {
const originalOnReadyStateChange = request.onreadystatechange;
/** Overriding/extending XHR function */
request.onreadystatechange = function onReadyStateChange(...args) {
originalOnReadyStateChange.apply(request, args);
if (retryOptions.retryableStatusCodes.includes(request.status)) {
const errorMessage = `Attempt to request ${url} failed.`;
const attemptFailedError = new Error(errorMessage);
operation.retry(attemptFailedError);
}
};
/** Call open only on retry (after headers and other things were set in the xhr instance) */
if (currentAttempt > 1) {
console.warn(`Requesting ${url}... (attempt: ${currentAttempt})`);
request.open(method, url, true);
}
});
originalRequestSend.apply(request, args);
}
/** Overriding/extending XHR function */
const originalRequestSend = request.send;
request.send = faultTolerantRequestSend;
return request;
};
/**
* Returns a configured retry request hook function
* that can be used to add retry functionality to XHR request.
*
* Default options:
* retries: 5
* factor: 3
* minTimeout: 1 * 1000
* maxTimeout: 60 * 1000
* randomize: true
*
* @param {object} options
* @param {number} options.retires number of retries
* @param {number} options.factor factor
* @param {number} options.minTimeout the min timeout
* @param {number} options.maxTimeout the max timeout
* @param {boolean} options.randomize randomize
* @param {array} options.retryableStatusCodes status codes that can trigger retry
* @returns {function} the configured retry request function
*/
export const getXHRRetryRequestHook = (options = {}) => {
retryOptions = { ...defaultRetryOptions };
if ('retries' in options) {
retryOptions.retries = options.retries;
}
if ('factor' in options) {
retryOptions.factor = options.factor;
}
if ('minTimeout' in options) {
retryOptions.minTimeout = options.minTimeout;
}
if ('maxTimeout' in options) {
retryOptions.maxTimeout = options.maxTimeout;
}
if ('randomize' in options) {
retryOptions.randomize = options.randomize;
}
if ('retryableStatusCodes' in options) {
retryOptions.retryableStatusCodes = options.retryableStatusCodes;
}
return xhrRetryRequestHook;
};
export default getXHRRetryRequestHook;
+70
View File
@@ -3,6 +3,76 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.4](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.3...@ohif/ui@1.10.4) (2021-10-26)
**Note:** Version bump only for package @ohif/ui
## [1.10.3](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.2...@ohif/ui@1.10.3) (2021-10-15)
**Note:** Version bump only for package @ohif/ui
## [1.10.2](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.1...@ohif/ui@1.10.2) (2021-09-24)
### Bug Fixes
* throttle thumbnail progress updates ([#2557](https://github.com/OHIF/Viewers/issues/2557)) ([66c587b](https://github.com/OHIF/Viewers/commit/66c587b139835201331a64bc24a17901e7583649))
## [1.10.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.0...@ohif/ui@1.10.1) (2021-09-13)
**Note:** Version bump only for package @ohif/ui
# [1.10.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.9.3...@ohif/ui@1.10.0) (2021-09-09)
### Features
* OHIF-1000: Study prefetcher ([#2466](https://github.com/OHIF/Viewers/issues/2466)) ([8f26045](https://github.com/OHIF/Viewers/commit/8f2604509d24092b721f1333289d838820d68cac))
## [1.9.3](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.9.2...@ohif/ui@1.9.3) (2021-09-03)
**Note:** Version bump only for package @ohif/ui
## [1.9.2](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.9.1...@ohif/ui@1.9.2) (2021-05-17)
**Note:** Version bump only for package @ohif/ui
## [1.9.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.9.0...@ohif/ui@1.9.1) (2021-03-26)
**Note:** Version bump only for package @ohif/ui
# [1.9.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.8.2...@ohif/ui@1.9.0) (2021-03-04)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/ui",
"version": "1.9.0",
"version": "1.10.4",
"description": "A set of React components for Medical Imaging Viewers",
"author": "OHIF Contributors",
"license": "MIT",
@@ -4,6 +4,29 @@
z-index: 0;
}
.RoundedButtonGroup .badgeNumber-container {
background-color: var(--ui-border-color-dark);
width: 21px;
height: 21px;
justify-content: center;
align-items: center;
display: flex;
border-radius: 50%;
font-size: 12px;
font-weight: bold;
bottom: 10px;
left: 5px;
color: white;
position: absolute;
}
.RoundedButtonGroup .badgeNumber {
font-size: 10px;
font-weight: bold;
display: block;
width: 25px;
}
.roundedButtonWrapper {
cursor: pointer;
display: inline-block;
@@ -36,7 +59,12 @@
z-index: 1;
}
.roundedButtonWrapper .roundedButton svg .roundedButtonWrapper .roundedButton span {
.roundedButtonWrapper
.roundedButton
svg
.roundedButtonWrapper
.roundedButton
span {
margin: 0 2px;
}
@@ -13,6 +13,7 @@ class RoundedButtonGroup extends Component {
PropTypes.shape({
value: PropTypes.any,
label: PropTypes.string,
stateEvent: PropTypes.string,
icon: PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({
@@ -30,6 +31,15 @@ class RoundedButtonGroup extends Component {
value: null,
};
state = {
badgeNumbers: [],
};
constructor() {
super();
this.onStateEvent = this.onStateEvent.bind(this);
}
onClickOption = value => {
let newValue = value;
if (this.props.value === value) {
@@ -41,6 +51,48 @@ class RoundedButtonGroup extends Component {
}
};
onStateEvent(event) {
const optionIndex = this.props.options.findIndex(
o => o.value === event.detail.target
);
if (optionIndex > -1) {
const badgeNumbers = this.state.badgeNumbers;
badgeNumbers[optionIndex] = event.detail.badgeNumber;
this.setState({ badgeNumbers });
}
}
componentDidMount() {
this.props.options.forEach(option => {
if (option.stateEvent) {
document.addEventListener(option.stateEvent, this.onStateEvent);
}
});
}
componentDidUpdate(prevProps, prevState) {
this.props.options.forEach((option, index) => {
if (
option.stateEvent &&
option.stateEvent !==
(prevProps.options[index]
? prevProps.options[index].stateEvent
: null)
) {
document.removeEventListener(option.stateEvent, this.onStateEvent);
document.addEventListener(option.stateEvent, this.onStateEvent);
}
});
}
componentWillUnmount() {
this.props.options.forEach(option => {
if (option.stateEvent) {
document.removeEventListener(option.stateEvent, this.onStateEvent);
}
});
}
render() {
let className = classnames(
RoundedButtonGroup.className,
@@ -62,6 +114,14 @@ class RoundedButtonGroup extends Component {
<div className="bottomLabel">{option.bottomLabel}</div>
);
let badgeNumber = this.state.badgeNumbers[index];
const badgeNumberOverflow = String(badgeNumber).length > 2;
badgeNumber = badgeNumber
? badgeNumberOverflow
? 99
: badgeNumber
: null;
return (
<div
key={index}
@@ -70,6 +130,14 @@ class RoundedButtonGroup extends Component {
>
<div className="roundedButton">
{optionText}
{badgeNumber && (
<div className="badgeNumber-container">
<span className="badgeNumber">
{badgeNumber}
{badgeNumberOverflow && '+'}
</span>
</div>
)}
{iconProps && <Icon {...iconProps} />}
</div>
{bottomLabel}
@@ -2,7 +2,8 @@
import './ImageThumbnail.styl';
import { utils } from '@ohif/core';
import React, { useState, useEffect, createRef } from 'react';
import React, { useState, useEffect, createRef, useCallback } from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import ViewportErrorIndicator from '../../viewer/ViewportErrorIndicator';
@@ -15,12 +16,14 @@ import ViewportLoadingIndicator from '../../viewer/ViewportLoadingIndicator';
//import cornerstone from 'cornerstone-core';
function ImageThumbnail(props) {
const {
active,
width,
height,
imageSrc,
imageId,
stackPercentComplete,
error: propsError,
showProgressBar,
} = props;
const [isLoading, setLoading] = useState(false);
@@ -37,7 +40,8 @@ function ImageThumbnail(props) {
loadingOrError = <ViewportLoadingIndicator />;
}
const showStackLoadingProgressBar = stackPercentComplete !== undefined;
const showStackLoadingProgressBar =
showProgressBar && stackPercentComplete !== undefined;
const shouldRenderToCanvas = () => {
return imageId && !imageSrc;
@@ -69,11 +73,11 @@ function ImageThumbnail(props) {
}
};
const purgeCancelablePromise = () => {
const purgeCancelablePromise = useCallback(() => {
if (cancelablePromise) {
cancelablePromise.cancel();
}
};
});
useEffect(() => {
return () => {
@@ -94,22 +98,28 @@ function ImageThumbnail(props) {
setImagePromise();
fetchImagePromise();
}
}, [fetchImagePromise, image.imageId, imageId, purgeCancelablePromise, setImagePromise]);
}, [
fetchImagePromise,
image.imageId,
imageId,
purgeCancelablePromise,
setImagePromise,
]);
return (
<div className="ImageThumbnail">
<div className={classNames('ImageThumbnail', { active: active })}>
<div className="image-thumbnail-canvas">
{shouldRenderToCanvas() ? (
<canvas ref={canvasRef} width={width} height={height} />
) : (
<img
className="static-image"
src={imageSrc}
//width={this.props.width}
height={height}
alt={''}
/>
)}
<img
className="static-image"
src={imageSrc}
//width={this.props.width}
height={height}
alt={''}
/>
)}
</div>
{loadingOrError}
{showStackLoadingProgressBar && (
@@ -126,19 +136,23 @@ function ImageThumbnail(props) {
}
ImageThumbnail.propTypes = {
active: PropTypes.bool,
imageSrc: PropTypes.string,
imageId: PropTypes.string,
error: PropTypes.bool,
width: PropTypes.number,
height: PropTypes.number,
stackPercentComplete: PropTypes.number.isRequired,
showProgressBar: PropTypes.bool,
};
ImageThumbnail.defaultProps = {
active: false,
error: false,
stackPercentComplete: 0,
width: 217,
height: 123,
showProgressBar: true,
};
export default ImageThumbnail;
@@ -6,7 +6,7 @@
--sidebar-transition: all 0.3s ease;
}
.ThumbnailEntry.active .ImageThumbnail
.ImageThumbnail.active
border-color: var(--active-color);
box-shadow: none
transition: var(--sidebar-transition);
@@ -14,7 +14,7 @@
.ImageThumbnail
background-color: var(--primary-background-color);
box-shadow: inset 0 0 0 1px var(--ui-border-color-dark);
border: 5px solid transparent
border: 2px solid transparent
border-radius: 12px
height: 135px
margin: 0 auto
@@ -9,6 +9,7 @@ function StudyBrowser(props) {
onThumbnailClick,
onThumbnailDoubleClick,
supportsDrag,
showThumbnailProgressBar,
} = props;
return (
@@ -20,14 +21,16 @@ function StudyBrowser(props) {
return study.thumbnails.map((thumb, thumbIndex) => {
// TODO: Thumb has more props than we care about?
const {
active,
altImageText,
displaySetInstanceUID,
imageId,
InstanceNumber,
derivedDisplaySetsNumber,
numImageFrames,
SeriesDescription,
SeriesNumber,
stackPercentComplete,
hasWarnings,
hasDerivedDisplaySets,
} = thumb;
return (
@@ -37,6 +40,7 @@ function StudyBrowser(props) {
data-cy="thumbnail-list"
>
<Thumbnail
active={active}
supportsDrag={supportsDrag}
key={`${studyIndex}_${thumbIndex}`}
id={`${studyIndex}_${thumbIndex}`} // Unused?
@@ -45,18 +49,20 @@ function StudyBrowser(props) {
// Thumb
altImageText={altImageText}
imageId={imageId}
InstanceNumber={InstanceNumber}
derivedDisplaySetsNumber={derivedDisplaySetsNumber}
displaySetInstanceUID={displaySetInstanceUID} // used by drop
numImageFrames={numImageFrames}
SeriesDescription={SeriesDescription}
SeriesNumber={SeriesNumber}
stackPercentComplete={stackPercentComplete}
hasWarnings={hasWarnings}
hasDerivedDisplaySets={hasDerivedDisplaySets}
// Events
onClick={onThumbnailClick.bind(
undefined,
displaySetInstanceUID
)}
onDoubleClick={onThumbnailDoubleClick}
showProgressBar={showThumbnailProgressBar}
/>
</div>
);
@@ -79,7 +85,7 @@ StudyBrowser.propTypes = {
altImageText: PropTypes.string,
displaySetInstanceUID: PropTypes.string.isRequired,
imageId: PropTypes.string,
InstanceNumber: PropTypes.number,
derivedDisplaySetsNumber: PropTypes.number,
numImageFrames: PropTypes.number,
SeriesDescription: PropTypes.string,
SeriesNumber: PropTypes.number,
@@ -91,6 +97,7 @@ StudyBrowser.propTypes = {
supportsDrag: PropTypes.bool,
onThumbnailClick: PropTypes.func,
onThumbnailDoubleClick: PropTypes.func,
showThumbnailProgressBar: PropTypes.bool,
};
StudyBrowser.defaultProps = {
@@ -98,6 +105,7 @@ StudyBrowser.defaultProps = {
supportsDrag: true,
onThumbnailClick: noop,
onThumbnailDoubleClick: noop,
showThumbnailProgressBar: true,
};
export { StudyBrowser };
@@ -1,17 +1,44 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import throttle from 'lodash.throttle';
import { useDrag } from 'react-dnd';
import { classes } from '@ohif/core';
import ImageThumbnail from './ImageThumbnail';
import classNames from 'classnames';
import { Icon } from './../../elements/Icon';
import { Tooltip } from './../tooltip';
import { OverlayTrigger } from './../overlayTrigger';
import './Thumbnail.styl';
const StudyLoadingListener = classes.StudyLoadingListener;
function ThumbnailFooter({
SeriesDescription,
SeriesNumber,
InstanceNumber,
numImageFrames,
hasWarnings,
hasDerivedDisplaySets,
}) {
const [inconsistencyWarnings, inconsistencyWarningsSet] = useState([]);
const [derivedDisplaySetsActive, derivedDisplaySetsActiveSet] = useState([]);
useEffect(() => {
let unmounted = false;
hasWarnings.then(response => {
if (!unmounted) {
inconsistencyWarningsSet(response);
}
});
hasDerivedDisplaySets.then(response => {
if (!unmounted) {
derivedDisplaySetsActiveSet(response);
}
});
return () => {
unmounted = true;
};
}, [hasWarnings, hasDerivedDisplaySets]);
const infoOnly = !SeriesDescription;
const getInfo = (value, icon, className = '') => {
@@ -22,28 +49,108 @@ function ThumbnailFooter({
</div>
);
};
const getWarningContent = inconsistencyWarnings => {
if (Array.isArray(inconsistencyWarnings)) {
const listedWarnings = inconsistencyWarnings.map((warn, index) => {
return <li key={index}>{warn}</li>;
});
return <ol>{listedWarnings}</ol>;
} else {
return <React.Fragment>{inconsistencyWarnings}</React.Fragment>;
}
};
const getWarningInfo = (SeriesNumber, inconsistencyWarnings) => {
return (
<React.Fragment>
{inconsistencyWarnings && inconsistencyWarnings.length != 0 ? (
<OverlayTrigger
key={SeriesNumber}
placement="left"
overlay={
<Tooltip
placement="left"
className="in tooltip-warning"
id="tooltip-left"
>
<div className="warningTitle">Series Inconsistencies</div>
<div className="warningContent">
{getWarningContent(inconsistencyWarnings)}
</div>
</Tooltip>
}
>
<div className={classNames('warning')}>
<span className="warning-icon">
<Icon name="exclamation-triangle" />
</span>
</div>
</OverlayTrigger>
) : (
<React.Fragment></React.Fragment>
)}
</React.Fragment>
);
};
const getDerivedInfo = derivedDisplaySetsActive => {
return (
<React.Fragment>
{derivedDisplaySetsActive ? (
<div className="derived">
<Icon name="link" />
</div>
) : (
<React.Fragment></React.Fragment>
)}
</React.Fragment>
);
};
const getSeriesInformation = (
SeriesNumber,
InstanceNumber,
numImageFrames
numImageFrames,
inconsistencyWarnings,
derivedDisplaySetsActive
) => {
if (!SeriesNumber && !InstanceNumber && !numImageFrames) {
if (!SeriesNumber && !numImageFrames) {
return;
}
return (
const seriesInformation = (
<div className="series-information">
{getInfo(SeriesNumber, 'S:')}
{getInfo(InstanceNumber, 'I:')}
{getInfo(numImageFrames, '', 'image-frames')}
<React.Fragment>
{SeriesNumber !== undefined ? (
getInfo(SeriesNumber, 'S:')
) : (
<React.Fragment></React.Fragment>
)}
</React.Fragment>
<React.Fragment>
{numImageFrames !== undefined ? (
getInfo(numImageFrames, '', 'image-frames')
) : (
<React.Fragment></React.Fragment>
)}
</React.Fragment>
{getDerivedInfo(derivedDisplaySetsActive)}
{getWarningInfo(SeriesNumber, inconsistencyWarnings)}
</div>
);
return seriesInformation;
};
return (
<div className={classNames('series-details', { 'info-only': infoOnly })}>
<div className="series-description">{SeriesDescription}</div>
{getSeriesInformation(SeriesNumber, InstanceNumber, numImageFrames)}
{getSeriesInformation(
SeriesNumber,
numImageFrames,
inconsistencyWarnings,
derivedDisplaySetsActive
)}
</div>
);
}
@@ -56,18 +163,42 @@ function Thumbnail(props) {
displaySetInstanceUID,
imageId,
imageSrc,
InstanceNumber,
numImageFrames,
SeriesDescription,
SeriesNumber,
stackPercentComplete,
hasWarnings,
hasDerivedDisplaySets,
StudyInstanceUID,
onClick,
onDoubleClick,
onMouseDown,
supportsDrag,
showProgressBar,
} = props;
const [stackPercentComplete, setStackPercentComplete] = useState(0);
useEffect(() => {
const onProgressChange = throttle(({ detail }) => {
const { progressId, progressData } = detail;
if (`StackProgress:${displaySetInstanceUID}` === progressId) {
const percent = progressData ? progressData.percentComplete : 0;
setStackPercentComplete(percent);
}
}, 100);
document.addEventListener(
StudyLoadingListener.events.OnProgress,
onProgressChange
);
return () => {
document.removeEventListener(
StudyLoadingListener.events.OnProgress,
onProgressChange
);
};
}, [displaySetInstanceUID]);
const [collectedProps, drag, dragPreview] = useDrag({
// `droppedItem` in `dropTarget`
// The only data it will have access to
@@ -95,10 +226,12 @@ function Thumbnail(props) {
{/* SHOW IMAGE */}
{hasImage && (
<ImageThumbnail
active={active}
imageSrc={imageSrc}
imageId={imageId}
error={error}
stackPercentComplete={stackPercentComplete}
showProgressBar={showProgressBar}
/>
)}
{/* SHOW TEXT ALTERNATIVE */}
@@ -126,17 +259,19 @@ Thumbnail.propTypes = {
stackPercentComplete: PropTypes.number,
/**
altImageText will be used when no imageId or imageSrc is provided.
It will be displayed inside the <div>. This is useful when it is difficult
It will be displayed inside the <div>. This is useful when it is difficult
to make a preview for a type of DICOM series (e.g. DICOM-SR)
*/
altImageText: PropTypes.string,
SeriesDescription: PropTypes.string,
SeriesNumber: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
InstanceNumber: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
hasWarnings: PropTypes.instanceOf(Promise),
hasDerivedDisplaySets: PropTypes.instanceOf(Promise),
numImageFrames: PropTypes.number,
onDoubleClick: PropTypes.func,
onClick: PropTypes.func,
onMouseDown: PropTypes.func,
showProgressBar: PropTypes.bool,
};
Thumbnail.defaultProps = {
@@ -2,8 +2,15 @@
:root {
--series-count-background-color: #678696;
--active-color: #20A5D6;
}
.thumbnail.active
.alt-image-text
border-color: var(--active-color);
box-shadow: none
transition: var(--sidebar-transition);
.thumbnail
cursor: pointer
display: table
@@ -16,7 +23,7 @@
justify-content: center;
background-color: var(--primary-background-color);
box-shadow: inset 0 0 0 1px var(--ui-border-color-dark);
border: 5px solid transparent;
border: 2px solid transparent;
border-radius: 12px;
height: 135px;
margin: 0 auto;
@@ -78,6 +85,17 @@
height: 11px
width: 11px
.warning
margin: auto 0;
opacity: 1
color: #e29e4a;
svg
width: 16px;
height: 14px;
pointer-events: inherit;
.value
color: var(--text-secondary-color);
display: inline-block
@@ -6,10 +6,13 @@ import React, {
useEffect,
} from 'react';
import PropTypes from 'prop-types';
import { classes } from '@ohif/core';
import SnackbarContainer from '../components/snackbar/SnackbarContainer';
import SnackbarTypes from '../components/snackbar/SnackbarTypes';
const { LogManager } = classes;
const SnackbarContext = createContext(null);
export const useSnackbarContext = () => useContext(SnackbarContext);
@@ -28,6 +31,20 @@ const SnackbarProvider = ({ children, service }) => {
const [count, setCount] = useState(1);
const [snackbarItems, setSnackbarItems] = useState([]);
useEffect(() => {
const onLogHandler = ({ type, notify, title, message }) => {
if (notify) {
show({ type, title, message });
}
};
LogManager.subscribe(LogManager.EVENTS.OnLog, onLogHandler);
return () => {
LogManager.unsubscribe(LogManager.EVENTS.OnLog, onLogHandler);
};
}, [show]);
const show = useCallback(
options => {
if (!options || (!options.title && !options.message)) {
+31 -2
View File
@@ -12,8 +12,6 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
const ExtractCssChunksPlugin = require('extract-css-chunks-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { InjectManifest } = require('workbox-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// ~~ Rules
const extractStyleChunksRule = require('./rules/extractStyleChunks.js');
// ~~ Directories
@@ -41,6 +39,23 @@ module.exports = (env, argv) => {
path: DIST_DIR,
filename: isProdBuild ? '[name].bundle.[chunkhash].js' : '[name].js',
publicPath: PUBLIC_URL, // Used by HtmlWebPackPlugin for asset prefix
devtoolModuleFilenameTemplate: function(info) {
if (isProdBuild) {
return `webpack:///${info.resourcePath}`;
} else {
return 'file:///' + encodeURI(info.absoluteResourcePath);
}
},
},
resolve: {
// We use this alias and the CopyPlugin below to support using the dynamic-import version
// of WADO Image Loader, but only when building a PWA. When we build a package, we must use the
// bundled version of WADO Image Loader so we can produce a single file for the viewer.
// (Note: script-tag version of the viewer will no longer be supported in OHIF v3)
alias: {
'cornerstone-wado-image-loader':
'cornerstone-wado-image-loader/dist/dynamic-import/cornerstoneWADOImageLoader.min.js',
},
},
module: {
rules: [...extractStyleChunksRule(isProdBuild)],
@@ -71,6 +86,11 @@ module.exports = (env, argv) => {
from: `${PUBLIC_DIR}/${APP_CONFIG}`,
to: `${DIST_DIR}/app-config.js`,
},
{
from:
'../../../node_modules/cornerstone-wado-image-loader/dist/dynamic-import',
to: DIST_DIR,
},
]),
// https://github.com/faceyspacey/extract-css-chunks-webpack-plugin#webpack-4-standalone-installation
new ExtractCssChunksPlugin({
@@ -95,6 +115,15 @@ module.exports = (env, argv) => {
// maximumFileSizeToCacheInBytes: 4 * 1024 * 1024
}),
],
optimization: {
splitChunks: {
// include all types of chunks
chunks: 'all',
},
//runtimeChunk: 'single',
minimize: isProdBuild,
sideEffects: true,
},
// https://webpack.js.org/configuration/dev-server/
devServer: {
// gzip compression of everything served
+294
View File
@@ -3,6 +3,300 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.12.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.3...@ohif/viewer@4.12.4) (2021-10-26)
**Note:** Version bump only for package @ohif/viewer
## [4.12.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.2...@ohif/viewer@4.12.3) (2021-10-25)
**Note:** Version bump only for package @ohif/viewer
## [4.12.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.1...@ohif/viewer@4.12.2) (2021-10-15)
**Note:** Version bump only for package @ohif/viewer
## [4.12.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.0...@ohif/viewer@4.12.1) (2021-10-06)
**Note:** Version bump only for package @ohif/viewer
# [4.12.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.11.2...@ohif/viewer@4.12.0) (2021-10-04)
### Features
* Added support for WADO servers built with static hosting (e.g. S3) ([#2566](https://github.com/OHIF/Viewers/issues/2566)) ([a046c6c](https://github.com/OHIF/Viewers/commit/a046c6c9017a84e5ead71d5113b5e892333b4130))
## [4.11.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.11.1...@ohif/viewer@4.11.2) (2021-09-28)
**Note:** Version bump only for package @ohif/viewer
## [4.11.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.11.0...@ohif/viewer@4.11.1) (2021-09-24)
**Note:** Version bump only for package @ohif/viewer
# [4.11.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.10.1...@ohif/viewer@4.11.0) (2021-09-23)
### Features
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
## [4.10.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.10.0...@ohif/viewer@4.10.1) (2021-09-13)
**Note:** Version bump only for package @ohif/viewer
# [4.10.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.24...@ohif/viewer@4.10.0) (2021-09-09)
### Features
* OHIF-1000: Study prefetcher ([#2466](https://github.com/OHIF/Viewers/issues/2466)) ([8f26045](https://github.com/OHIF/Viewers/commit/8f2604509d24092b721f1333289d838820d68cac))
## [4.9.24](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.23...@ohif/viewer@4.9.24) (2021-09-03)
**Note:** Version bump only for package @ohif/viewer
## [4.9.23](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.22...@ohif/viewer@4.9.23) (2021-08-30)
### Bug Fixes
* MPR metadata parsing for spacings ([#2517](https://github.com/OHIF/Viewers/issues/2517)) ([715dfc5](https://github.com/OHIF/Viewers/commit/715dfc556a3883de31d74012dcc99e431d91f57d))
## [4.9.22](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.21...@ohif/viewer@4.9.22) (2021-08-19)
### Bug Fixes
* Remove internet dependency for workbox (addressing [#2109](https://github.com/OHIF/Viewers/issues/2109)) ([#2151](https://github.com/OHIF/Viewers/issues/2151)) ([5f92f6e](https://github.com/OHIF/Viewers/commit/5f92f6ec7faa11d3fdc33ef8f22971a0bab87a36))
## [4.9.21](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.20...@ohif/viewer@4.9.21) (2021-07-26)
**Note:** Version bump only for package @ohif/viewer
## [4.9.20](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.19...@ohif/viewer@4.9.20) (2021-06-03)
**Note:** Version bump only for package @ohif/viewer
## [4.9.19](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.18...@ohif/viewer@4.9.19) (2021-06-03)
**Note:** Version bump only for package @ohif/viewer
## [4.9.18](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.17...@ohif/viewer@4.9.18) (2021-06-02)
### Bug Fixes
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
## [4.9.17](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.16...@ohif/viewer@4.9.17) (2021-05-17)
**Note:** Version bump only for package @ohif/viewer
## [4.9.16](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.15...@ohif/viewer@4.9.16) (2021-05-14)
**Note:** Version bump only for package @ohif/viewer
## [4.9.15](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.14...@ohif/viewer@4.9.15) (2021-04-22)
**Note:** Version bump only for package @ohif/viewer
## [4.9.14](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.13...@ohif/viewer@4.9.14) (2021-04-22)
**Note:** Version bump only for package @ohif/viewer
## [4.9.13](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.12...@ohif/viewer@4.9.13) (2021-04-21)
**Note:** Version bump only for package @ohif/viewer
## [4.9.12](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.11...@ohif/viewer@4.9.12) (2021-04-21)
**Note:** Version bump only for package @ohif/viewer
## [4.9.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.10...@ohif/viewer@4.9.11) (2021-04-21)
**Note:** Version bump only for package @ohif/viewer
## [4.9.10](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.9...@ohif/viewer@4.9.10) (2021-04-16)
**Note:** Version bump only for package @ohif/viewer
## [4.9.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.8...@ohif/viewer@4.9.9) (2021-04-15)
**Note:** Version bump only for package @ohif/viewer
## [4.9.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.7...@ohif/viewer@4.9.8) (2021-03-31)
**Note:** Version bump only for package @ohif/viewer
## [4.9.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.6...@ohif/viewer@4.9.7) (2021-03-26)
**Note:** Version bump only for package @ohif/viewer
## [4.9.6](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.5...@ohif/viewer@4.9.6) (2021-03-26)
**Note:** Version bump only for package @ohif/viewer
## [4.9.5](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.4...@ohif/viewer@4.9.5) (2021-03-25)
### Bug Fixes
* IDC-2288: Update toolbar tabs after derived displayset loaded ([#2307](https://github.com/OHIF/Viewers/issues/2307)) ([6e723eb](https://github.com/OHIF/Viewers/commit/6e723ebced58852007ca553792c740976cfc4201))
## [4.9.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.3...@ohif/viewer@4.9.4) (2021-03-19)
**Note:** Version bump only for package @ohif/viewer
## [4.9.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.2...@ohif/viewer@4.9.3) (2021-03-15)
**Note:** Version bump only for package @ohif/viewer
## [4.9.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.1...@ohif/viewer@4.9.2) (2021-03-09)
**Note:** Version bump only for package @ohif/viewer
## [4.9.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.0...@ohif/viewer@4.9.1) (2021-03-09)
**Note:** Version bump only for package @ohif/viewer
# [4.9.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.12...@ohif/viewer@4.9.0) (2021-03-04)
@@ -91,7 +91,7 @@ describe('OHIF Cornerstone Hotkeys', () => {
':nth-child(2) > .viewport-wrapper > .viewport-element > .ViewportOrientationMarkers.noselect > .left-mid.orientation-marker'
).as('viewport2InfoMidLeft');
cy.get(
':nth-child(2) > .viewport-wrapper > .viewport-element > .ViewportOverlay > div.bottom-right.overlay-element > div'
':nth-child(2) > .viewport-wrapper > .viewport-element > .OHIFCornerstoneViewportOverlay > div.bottom-right.overlay-element > div'
).as('viewport2InfoBottomRight');
// Press multiples hotkeys on viewport #2
@@ -333,6 +333,7 @@ describe('OHIF User Preferences', () => {
.scrollIntoView()
.click();
// Header should be in "English (USA)""
cy.get('.research-use')
.scrollIntoView()
@@ -560,6 +561,13 @@ describe('OHIF User Preferences', () => {
.scrollIntoView()
.click();
// Close Success Message overlay (if displayed)
cy.get('body').then(body => {
if (body.find('.sb-closeIcon').length > 0) {
cy.get('.sb-closeIcon').click({ force: true });
}
});
// Open User Preferences modal again
cy.openPreferences();
@@ -668,6 +676,13 @@ describe('OHIF User Preferences', () => {
// Save changes
cy.get('@saveBtn').click();
// Close Success Message overlay (if displayed)
cy.get('body').then(body => {
if (body.find('.sb-closeIcon').length > 0) {
cy.get('.sb-closeIcon').click({ force: true });
}
});
// Open User Preferences modal
cy.openPreferences();
// Navigate to Window Level tab
@@ -741,7 +756,7 @@ describe('OHIF User Preferences', () => {
// Overlay information from 2nd viewport
let second_viewport_overlay =
'div:nth-child(2) > div > div.viewport-element > div.ViewportOverlay > div.bottom-right.overlay-element > div';
'div:nth-child(2) > div > div.viewport-element > div.OHIFCornerstoneViewportOverlay > div.bottom-right.overlay-element > div';
// Shift active viewport to Viewport #2
cy.get('body').type('{rightarrow}');
+2 -2
View File
@@ -25,10 +25,10 @@ export function initCommonElementsAliases() {
'.pull-left > .RoundedButtonGroup > .roundedButtonWrapper > .roundedButton'
).as('seriesBtn');
cy.get('section.sidepanel.from-left').as('seriesPanel');
cy.get('div.ViewportOverlay > div.bottom-left.overlay-element > div').as(
cy.get('div.OHIFCornerstoneViewportOverlay > div.bottom-left.overlay-element > div').as(
'viewportInfoBottomLeft'
);
cy.get('div.ViewportOverlay > div.bottom-right.overlay-element > div').as(
cy.get('div.OHIFCornerstoneViewportOverlay > div.bottom-right.overlay-element > div').as(
'viewportInfoBottomRight'
);
cy.get('.left-mid.orientation-marker').as('viewportInfoMidLeft');
Loaded 100 of 182 files, more files were not shown because too many files have changed in this diff. Show more