Compare commits

...
816 Commits
Author SHA1 Message Date
ohif-bot 3b66867508 chore(version): Update package versions [skip ci] 2024-11-13 04:21:28 +00:00
ohif-bot 1d267e5576 chore(version): version.json [skip ci] 2024-11-13 04:21:19 +00:00
Alireza 610faa5a27 fix: video playback (#4497) 2024-11-12 21:18:06 -05:00
Dan Rukas 4f8281b04a fix(ui): Scrolling and display fixes for study panel (#4494) 2024-11-12 17:21:05 -05:00
Alireza 2f7bb02387 fix: publish version 3.9 (#4491) 2024-11-12 14:45:02 -05:00
ohif-bot 54d4eabf03 chore(version): Update package versions [skip ci] 2024-11-12 19:13:32 +00:00
ohif-bot 5c20bf0faf chore(version): version.json [skip ci] 2024-11-12 19:13:25 +00:00
Alireza b417063200 fix version json 2024-11-12 13:53:36 -05:00
Alireza 4bd343ab3e fix version json 2024-11-12 13:46:15 -05:00
Alireza a6de628132 publish version 3.9 2024-11-12 13:34:08 -05:00
ohif-bot f4775eccae chore(version): Update package versions [skip ci] 2024-11-12 18:31:31 +00:00
ohif-bot bf8fe7799c chore(version): version.json [skip ci] 2024-11-12 18:31:25 +00:00
Alireza 62b2748847 fix: Measurement Tracking: Various UI and functionality improvements (#4481) 2024-11-12 13:14:47 -05:00
Bill Wallace 4a125236dd fix: Have an addIcon that adds to both ui and ui-next (#4490) 2024-11-12 12:34:14 -05:00
Ibrahim a5f03764d1 fix(styles): several fixes for different styles (#4483) 2024-11-12 12:33:45 -05:00
Dan Rukas 1801a217e3 ui(components): updated component font sizes and design component docs (#4486) 2024-11-12 12:33:09 -05:00
ohif-bot 23a2582231 chore(version): Update package versions [skip ci] 2024-11-11 19:45:52 +00:00
ohif-bot 1105db5dd1 chore(version): version.json [skip ci] 2024-11-11 19:45:44 +00:00
Alireza 05d41c5206 fix(bugs): Update dependencies and enhance UI components (#4478) 2024-11-11 14:28:34 -05:00
ohif-bot 61e3a9cb52 chore(version): Update package versions [skip ci] 2024-11-08 15:51:44 +00:00
ohif-bot 4b69279c70 chore(version): version.json [skip ci] 2024-11-08 15:51:36 +00:00
Ibrahim 8fb8b3bfd1 fix(style): worklist shifting (#4477) 2024-11-08 10:34:22 -05:00
ohif-bot c1a196d802 chore(version): Update package versions [skip ci] 2024-11-07 05:37:54 +00:00
ohif-bot 4a564528ba chore(version): version.json [skip ci] 2024-11-07 05:37:46 +00:00
Alireza aa2b649444 fix(tmtv): fix toggle one up weird behaviours (#4473) 2024-11-07 00:20:30 -05:00
ohif-bot 3f7ae76113 chore(version): Update package versions [skip ci] 2024-11-06 16:36:09 +00:00
ohif-bot 43ad641cf1 chore(version): version.json [skip ci] 2024-11-06 16:36:00 +00:00
Leszek Grzanka c80cd6102d fix(JSONScript) update name of the script in the comments to follow renaming (#4441) 2024-11-06 11:19:20 -05:00
Alireza 3d11ef28f2 fix: build (#4471) 2024-11-06 11:16:43 -05:00
ohif-bot 69a81ae838 chore(version): Update package versions [skip ci] 2024-11-06 12:34:24 +00:00
ohif-bot 417785ecbc chore(version): version.json [skip ci] 2024-11-06 12:34:15 +00:00
Alireza 7ce7127d7c OHIF with Cornerstone3D 2.0 (#4347) 2024-11-06 07:15:27 -05:00
ohif-bot 8981a61b8c chore(version): Update package versions [skip ci] 2024-11-05 18:47:30 +00:00
ohif-bot c66c688ace chore(version): version.json [skip ci] 2024-11-05 18:47:21 +00:00
Dan Rukas d9450104af fix(ui) Study Browser small spacing changes (#4469) 2024-11-05 13:28:59 -05:00
ohif-bot 2835a555c8 chore(version): Update package versions [skip ci] 2024-10-30 14:34:08 +00:00
ohif-bot 41e8ee692b chore(version): version.json [skip ci] 2024-10-30 14:33:59 +00:00
Pedro H. Köhler 4f20523109 fix(ui): show ui notification on displaySet load error (#4447) 2024-10-30 10:15:48 -04:00
ohif-bot 0166956dd8 chore(version): Update package versions [skip ci] 2024-10-29 23:31:44 +00:00
ohif-bot 1aa2c646a6 chore(version): version.json [skip ci] 2024-10-29 23:31:37 +00:00
Pedro H. Köhler 2e96371b06 fix(ui): display error in ui while loading seg (#4433) 2024-10-29 19:14:34 -04:00
ohif-bot 543c31109b chore(version): Update package versions [skip ci] 2024-10-29 14:10:39 +00:00
ohif-bot 2d1704063c chore(version): version.json [skip ci] 2024-10-29 14:10:31 +00:00
Pedro H. Köhler 0610425740 fix(packages): http-proxy-middleware vulnerability (#4443) 2024-10-29 09:52:07 -04:00
ohif-bot ce9ff7d535 chore(version): Update package versions [skip ci] 2024-10-18 15:59:40 +00:00
ohif-bot b4918d8a33 chore(version): version.json [skip ci] 2024-10-18 15:59:32 +00:00
Ibrahim a7fd9fa5bf feat(new-study-panel): default to list view for non thumbnail series, change default fitler to all, and add more menu to thumbnail items with a dicom tag browser (#4417) 2024-10-18 11:42:44 -04:00
ohif-bot 1d0259451f chore(version): Update package versions [skip ci] 2024-10-17 16:39:04 +00:00
ohif-bot abf2093d30 chore(version): version.json [skip ci] 2024-10-17 16:38:55 +00:00
dependabot[bot] 8fc395e68f chore(deps): bump webpack from 5.89.0 to 5.94.0 (#4354)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-17 12:20:33 -04:00
Ibrahim 33af9bb021 fix(tmtv): prevent fusion row in tmtv from getting inverted unexpectedly (#4420) 2024-10-17 12:18:15 -04:00
ohif-bot 8dab0883c8 chore(version): Update package versions [skip ci] 2024-10-17 14:20:54 +00:00
ohif-bot a146c9a50e chore(version): version.json [skip ci] 2024-10-17 14:20:44 +00:00
BillucandLuc Billaud 2ec4692eaf fix(createReport): early return on cancel in prompt (#4243)
Co-authored-by: Luc Billaud <luc.billaud@creatis.insa-lyon.fr>
2024-10-17 09:49:53 -04:00
Ibrahim 03aad4eba2 fix(typo): type in fourup preset (#4426) 2024-10-17 09:47:37 -04:00
dependabot[bot] f14aee2733 chore(deps): bump micromatch from 4.0.7 to 4.0.8 (#4350)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-17 09:46:36 -04:00
dependabot[bot] 57eb829173 chore(deps): bump express from 4.19.2 to 4.21.1 (#4422)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-17 09:44:37 -04:00
Ibrahim ace67b3bbb fix(hp-presets): select the active displaySet when toggling an HP preset, not a random one from the series panel (#4365) 2024-10-17 09:43:48 -04:00
Ibrahim 307b14476b fix(tools): check if seriesNumber is an undefined properly (#4338) 2024-10-17 09:43:10 -04:00
Ibrahim 433cc8089d fix(3d-viewport): exception was being thrown and 3d viewpot not getting resized. (#4366) 2024-10-17 09:42:05 -04:00
Sofien-Sellami fa2435d5e9 feat(hangingProtocols): added selection of the HangingProtocol stage from the url (#4310) 2024-10-17 09:41:41 -04:00
Ceciliadrc e8f9345b93 feat(removeButton) : add remove button from toolbar option (#4408) 2024-10-17 09:40:53 -04:00
brandon flowers e254e7bb3d chore(ui): Added Data Test Ids to Cine controls for e2e Testing (#4415) 2024-10-17 09:40:24 -04:00
Joeycho 7353f7f069 fix(sr): load existing point, if there is 2nd point in renderableData (Fix rotation in arrow annotation) (#4356) 2024-10-17 09:39:48 -04:00
Igor Octaviano 3dd0666c0c fix(dicomjson): Update getUIDsFromImageID to work with json data source + update getDisplaySetImageUIDs to work with mixed sop class json (#4322) 2024-10-17 09:38:40 -04:00
Ceciliadrc d5bcf54e23 fix(updateIndex): getNumberOfSlices is defined when used with a 3D viewport (#4424) 2024-10-17 09:36:42 -04:00
Igor Octaviano ac1cad25af feat(SR): SCOORD3D point annotations support for stack viewports (#4315) 2024-10-17 09:36:24 -04:00
Dan Rukas e0fca8c88c ui(component): Added new Toast (Sonner) component (#4412) 2024-10-17 09:30:42 -04:00
ohif-bot 43f54aa49c chore(version): Update package versions [skip ci] 2024-10-15 14:10:16 +00:00
ohif-bot 9b2e98a760 chore(version): version.json [skip ci] 2024-10-15 14:10:08 +00:00
Alireza 465df5c573 docs(ui): add base for ui component showcase in docs (#4414) 2024-10-15 09:53:26 -04:00
ohif-bot 9125cec333 chore(version): Update package versions [skip ci] 2024-10-11 13:25:01 +00:00
ohif-bot 87d623a76e chore(version): version.json [skip ci] 2024-10-11 13:24:54 +00:00
James PettsandJames Petts e849199eb0 fix(auth): oidc-react-issue (#4410)
Co-authored-by: James Petts <jamespetts@Yunu-MacBookPro-JamesPetts.local>
2024-10-11 09:09:26 -04:00
ohif-bot 0495020389 chore(version): Update package versions [skip ci] 2024-10-10 15:28:10 +00:00
ohif-bot 98ab95a743 chore(version): version.json [skip ci] 2024-10-10 15:28:02 +00:00
Ibrahim ec685ef5b9 fix(fossa): update fossa to track licenses correctly (#4411) 2024-10-10 11:12:21 -04:00
ohif-bot 70eb2aa6d7 chore(version): Update package versions [skip ci] 2024-10-08 16:41:57 +00:00
ohif-bot a1597d7bd6 chore(version): version.json [skip ci] 2024-10-08 16:41:49 +00:00
Dan Rukas 8f0035aeef ui(component): Adds flexible DataRow component (#4399) 2024-10-08 12:25:44 -04:00
ohif-bot c8923c5499 chore(version): Update package versions [skip ci] 2024-10-04 18:02:36 +00:00
ohif-bot c0c4f2398c chore(version): version.json [skip ci] 2024-10-04 18:02:29 +00:00
Ibrahim 60a8d5154a feat(tours): freeze versions and add licensings doc (#4407) 2024-10-04 13:47:32 -04:00
ohif-bot efc554bd49 chore(version): Update package versions [skip ci] 2024-10-04 17:21:26 +00:00
ohif-bot fc5f73bdfd chore(version): version.json [skip ci] 2024-10-04 17:21:18 +00:00
Ibrahim 579a592812 Merge branch 'IbrahimCSAE-feat/interactive-onboarding-guides' 2024-10-04 12:53:33 -04:00
Ibrahim ea961d6b66 add demo 2024-10-04 12:42:19 -04:00
Ibrahim 69a620ac68 read me 2024-10-04 12:37:10 -04:00
Ibrahim 8eaebd015d updates 2024-10-04 12:28:26 -04:00
Ibrahim 0326496c47 updates 2024-10-04 12:27:43 -04:00
Ibrahim 327f2cb1cf update 2024-10-04 12:16:56 -04:00
Ibrahim 478def8447 updates 2024-10-04 12:16:01 -04:00
Ibrahim 0a9cbb7589 updates 2024-10-04 12:14:31 -04:00
Ibrahim c5752e756a updates 2024-10-04 10:03:20 -04:00
Ibrahim 3b20d2b816 updates 2024-10-04 08:27:40 -04:00
Ibrahim 0d4cb69ec3 styling 2024-10-04 08:18:20 -04:00
ohif-bot 4f18a21868 chore(version): Update package versions [skip ci] 2024-10-01 20:56:33 +00:00
ohif-bot c4299de984 chore(version): version.json [skip ci] 2024-10-01 20:56:25 +00:00
Pedro H. Köhler a11cd6d6cb fix(Select): select clear button (#4398) 2024-10-01 16:42:21 -04:00
ohif-bot a72192da28 chore(version): Update package versions [skip ci] 2024-10-01 12:49:31 +00:00
ohif-bot 0dca891ff3 chore(version): version.json [skip ci] 2024-10-01 12:49:22 +00:00
Pedro H. Köhler b4f08adfb6 fix(dependent): high priority vulnerabilities (#4396) 2024-10-01 08:20:19 -04:00
ohif-bot 4d87e4dbda chore(version): Update package versions [skip ci] 2024-09-30 22:00:24 +00:00
ohif-bot 6a02f0b41c chore(version): version.json [skip ci] 2024-09-30 22:00:16 +00:00
Igor Octaviano 43efed207e fix: 🐛 Fix imports for ui-next (#4394) 2024-09-30 17:45:14 -04:00
Ibrahim 4f2583483c updates 2024-09-27 16:38:07 -04:00
Ibrahim e3f7838ce0 updates 2024-09-27 16:27:23 -04:00
Ibrahim 08d329add4 updates 2024-09-27 16:14:14 -04:00
Ibrahim 579c0c9d01 updates 2024-09-27 15:44:01 -04:00
Ibrahim ea7a3e07cb u 2024-09-27 12:58:48 -04:00
Ibrahim e91811ff23 updates 2024-09-27 12:58:39 -04:00
ohif-bot dd8e69a8cf chore(version): Update package versions [skip ci] 2024-09-27 16:33:00 +00:00
ohif-bot b1b66076b9 chore(version): version.json [skip ci] 2024-09-27 16:32:51 +00:00
Dan Rukas 80b1f54af1 ui(dropdown menu): Added DropdownMenu component in ui-next (#4387) 2024-09-27 12:17:54 -04:00
ohif-bot 75fde87b59 chore(version): Update package versions [skip ci] 2024-09-24 19:59:31 +00:00
ohif-bot 64f2f50f63 chore(version): version.json [skip ci] 2024-09-24 19:59:24 +00:00
Dan Rukas 7f17d5d733 ui(segmentation): Design Prototype for split panel component (#4378) 2024-09-24 15:44:59 -04:00
Ibrahim 9ff4ece541 updates 2024-09-22 20:47:33 -04:00
Ibrahim e534802abb updates 2024-09-22 20:41:12 -04:00
Ibrahim 51a87028c1 updates 2024-09-22 20:30:07 -04:00
Ibrahim eba85aa5b3 updates 2024-09-22 20:30:00 -04:00
Ibrahim 331778dfef Merge branch 'feat/interactive-onboarding-guides' of https://github.com/IbrahimCSAE/Viewers into local-interactive-onboarding-guides 2024-09-20 11:44:43 -04:00
Ibrahim daa5a857e4 updates 2024-09-20 11:44:36 -04:00
ohif-bot cb1f3b877b chore(version): Update package versions [skip ci] 2024-09-19 16:23:16 +00:00
ohif-bot f748d2a73a chore(version): version.json [skip ci] 2024-09-19 16:23:09 +00:00
Dan Rukas 365d824b98 fix(ui): Fixed study component open and closed feedback in Studies panel (#4384) 2024-09-19 12:07:54 -04:00
ohif-bot ee8962e9a7 chore(version): Update package versions [skip ci] 2024-09-19 04:39:08 +00:00
ohif-bot cde457c626 chore(version): version.json [skip ci] 2024-09-19 04:39:01 +00:00
Alireza 5ef102681d refactor(config): Update AWS S3 Static wado server URLs (#4381) 2024-09-19 00:23:28 -04:00
ohif-bot d55e794a86 chore(version): Update package versions [skip ci] 2024-09-17 04:08:08 +00:00
ohif-bot 4f7fa35def chore(version): version.json [skip ci] 2024-09-17 04:08:01 +00:00
Ibrahim 1f09527ae6 docs(mobile): IOS & Android Debugging Guides (#4371) 2024-09-16 23:53:10 -04:00
Ibrahim 118922f365 progress 2024-09-13 11:01:41 -04:00
ohif-bot 926f549e91 chore(version): Update package versions [skip ci] 2024-09-12 18:49:56 +00:00
ohif-bot 0d900bf4ad chore(version): version.json [skip ci] 2024-09-12 18:49:48 +00:00
Pedro H. Köhler 1bfce0a03c feat(toolbar): enable extensions to change toolbar button sections (#4367) 2024-09-12 14:34:25 -04:00
ohif-bot c08debd65a chore(version): Update package versions [skip ci] 2024-09-11 19:13:10 +00:00
ohif-bot ab5cead2af chore(version): version.json [skip ci] 2024-09-11 19:13:02 +00:00
Ibrahim 7a96406a11 feat(studies-panel): New OHIF study panel - under experimental flag (#4254) 2024-09-11 14:57:19 -04:00
ohif-bot 4432b6b420 chore(version): Update package versions [skip ci] 2024-09-05 13:31:46 +00:00
ohif-bot 7670941256 chore(version): version.json [skip ci] 2024-09-05 13:31:38 +00:00
Bill Wallace e1feffa425 fix: Add kheops integration into OHIF v3 again (#4345) 2024-09-05 09:15:48 -04:00
ohif-bot 3b47e6971c chore(version): Update package versions [skip ci] 2024-08-27 12:05:44 +00:00
ohif-bot 13ea778e61 chore(version): version.json [skip ci] 2024-08-27 12:05:36 +00:00
Igor Octaviano 14498d4e9a fix: 🐛 SeriesInstanceUID fallback + update retrieve metadata filtered to check for lazy (#4346) 2024-08-27 07:49:48 -04:00
ohif-bot 83f3bf4ec1 chore(version): Update package versions [skip ci] 2024-08-16 05:35:24 +00:00
ohif-bot 3b96c9e771 chore(version): version.json [skip ci] 2024-08-16 05:35:17 +00:00
dependabot[bot] da8bdb94a9 chore(deps): bump axios from 0.25.0 to 0.28.1 (#4337)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-16 01:09:56 -04:00
ohif-bot 1f2c7c4c57 chore(version): Update package versions [skip ci] 2024-08-16 02:11:52 +00:00
ohif-bot 5c7a8d0c56 chore(version): version.json [skip ci] 2024-08-16 02:11:45 +00:00
b2c0faf29d Test: Playwright tests for tmtv (#4331)
Co-authored-by: Ibrahim <93064150+IbrahimCSAE@users.noreply.github.com>
Co-authored-by: Ibrahim <ibrahim.mdev@gmail.com>
2024-08-15 21:57:04 -04:00
ohif-bot 4068488bb2 chore(version): Update package versions [skip ci] 2024-08-15 18:52:14 +00:00
ohif-bot acc6a217c9 chore(version): version.json [skip ci] 2024-08-15 18:52:06 +00:00
Bill Wallace 7599ec9421 feat: Add CS3D WSI and Video Viewports and add annotation navigation for MPR (#4182) 2024-08-15 14:36:53 -04:00
ohif-bot b12e0211d4 chore(version): Update package versions [skip ci] 2024-08-15 15:29:35 +00:00
ohif-bot 689008eb82 chore(version): version.json [skip ci] 2024-08-15 15:29:26 +00:00
Ibrahim 7377db8d28 fix(roundNumber): handle negative numbers properly (#4336) 2024-08-15 11:13:27 -04:00
ohif-bot a2be64f899 chore(version): Update package versions [skip ci] 2024-08-08 15:50:34 +00:00
ohif-bot 5a2e88455a chore(version): version.json [skip ci] 2024-08-08 15:50:27 +00:00
Pedro H. Köhler ab6e341731 fix: unexpected mpr measurements (#4332) 2024-08-08 11:33:36 -04:00
ohif-bot 45f3a2d84d chore(version): Update package versions [skip ci] 2024-08-07 15:24:37 +00:00
ohif-bot 00f78adb1f chore(version): version.json [skip ci] 2024-08-07 15:24:29 +00:00
Salim Kanoun 8e7cc11529 fix(ui): Tailwind build errors (#4329) 2024-08-07 11:08:52 -04:00
ohif-bot c21d4848ef chore(version): Update package versions [skip ci] 2024-08-06 13:42:34 +00:00
ohif-bot 1de19a07c1 chore(version): version.json [skip ci] 2024-08-06 13:42:27 +00:00
Igor Octaviano 9b10303a2e fix(url): series query param filtering (#4328) 2024-08-06 09:26:34 -04:00
ohif-bot c6d3990341 chore(version): Update package versions [skip ci] 2024-08-02 17:01:10 +00:00
ohif-bot c42cd90c1d chore(version): version.json [skip ci] 2024-08-02 17:01:02 +00:00
Dan Rukas 90364189b8 feat(ui): Created design and added core components for ui-next (#4324) 2024-08-02 12:35:07 -04:00
ohif-bot 00707db1ad chore(version): Update package versions [skip ci] 2024-07-31 15:37:18 +00:00
ohif-bot 688d896f3e chore(version): version.json [skip ci] 2024-07-31 15:37:10 +00:00
Pedro H. Köhler 72bef63ef6 fix: customization types (#4321) 2024-07-31 11:21:51 -04:00
ohif-bot 50669867c9 chore(version): Update package versions [skip ci] 2024-07-30 16:14:37 +00:00
ohif-bot 90d974b90c chore(version): version.json [skip ci] 2024-07-30 16:14:29 +00:00
Pedro H. Köhler aa0e5a5937 fix: ip SSRF improper categorization (#4319) 2024-07-30 11:58:50 -04:00
ohif-bot dd2648a080 chore(version): Update package versions [skip ci] 2024-07-30 13:21:38 +00:00
ohif-bot 0dbf85a3d5 chore(version): version.json [skip ci] 2024-07-30 13:21:29 +00:00
Salim Kanoun d402ded8c3 fix(ui): remove border-border class (#4317) 2024-07-30 09:05:19 -04:00
ohif-bot b1514b9a3e chore(version): Update package versions [skip ci] 2024-07-27 02:41:22 +00:00
ohif-bot bbfa3c1a1f chore(version): version.json [skip ci] 2024-07-27 02:41:15 +00:00
dependabot[bot] ba8af0f35b chore(deps): bump sharp from 0.30.7 to 0.32.6 (#4314)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-26 22:26:22 -04:00
ohif-bot 5755ce5430 chore(version): Update package versions [skip ci] 2024-07-26 17:03:24 +00:00
ohif-bot a7cba1a3cf chore(version): version.json [skip ci] 2024-07-26 17:03:16 +00:00
Igor Octaviano 16b7aa4f65 fix(dicom): Update multiframe DICOM JSON parsing for correct image ID generation (#4307) 2024-07-26 12:47:49 -04:00
ohif-bot 9f0828ae27 chore(version): Update package versions [skip ci] 2024-07-26 15:20:34 +00:00
ohif-bot 8903e3c3dd chore(version): version.json [skip ci] 2024-07-26 15:20:26 +00:00
AlirezaandDan Rukas 6231629b38 chore(ui): creating a playground for the new components (#4301)
Co-authored-by: Dan Rukas <dan.rukas@gmail.com>
2024-07-26 11:04:58 -04:00
ohif-bot b9eb54c71f chore(version): Update package versions [skip ci] 2024-07-24 20:56:53 +00:00
ohif-bot 2656beef0e chore(version): version.json [skip ci] 2024-07-24 20:56:45 +00:00
Leonardo Campos fc0064fd9d feat(pmap): added support for parametric map (#4284) 2024-07-24 16:40:28 -04:00
ohif-bot 9f3fc382c3 chore(version): Update package versions [skip ci] 2024-07-23 15:43:31 +00:00
ohif-bot 640200ba7e chore(version): version.json [skip ci] 2024-07-23 15:43:23 +00:00
Leonardo Campos 246ebab6eb feat(SR): text structured report (TEXT, CODE, NUM, PNAME, DATE, TIME and DATETIME) (#4287) 2024-07-23 11:28:48 -04:00
ohif-bot 58da0324cd chore(version): Update package versions [skip ci] 2024-07-19 14:10:42 +00:00
ohif-bot c9a2496aab chore(version): version.json [skip ci] 2024-07-19 14:10:34 +00:00
Pavel Klochkov 4368eb3896 docs(resources) Update resources.md in Docs (#4290) 2024-07-19 09:57:22 -04:00
ohif-bot fc9b521fa9 chore(version): Update package versions [skip ci] 2024-07-10 15:43:46 +00:00
ohif-bot dcd1e4eb28 chore(version): version.json [skip ci] 2024-07-10 15:43:37 +00:00
Ibrahim 0968b3abb1 docs(tools): add listener helpful tip for migration (#4285) 2024-07-10 11:28:35 -04:00
ohif-bot f6cdbe46bc chore(version): Update package versions [skip ci] 2024-07-09 19:17:33 +00:00
ohif-bot d8ae7b884b chore(version): version.json [skip ci] 2024-07-09 19:17:25 +00:00
Celian-abd 38c19fab77 fix: the start/end command in TMTV for the ROIStartEndThreshold tools (#4281) 2024-07-09 14:49:42 -04:00
ohif-bot 4b91ce4df9 chore(version): Update package versions [skip ci] 2024-07-09 18:48:34 +00:00
ohif-bot 6fb7efc46d chore(version): version.json [skip ci] 2024-07-09 18:48:27 +00:00
IbrahimandAlireza aefa6d94df feat(auth): Add Authorization Code Flow and new Keycloak recipes with new video tutorials (#4234)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2024-07-09 14:33:37 -04:00
ohif-bot 998302eb85 chore(version): Update package versions [skip ci] 2024-07-09 15:27:38 +00:00
ohif-bot 400e914011 chore(version): version.json [skip ci] 2024-07-09 15:27:30 +00:00
Bill WallaceandIbrahimCSAE 31271aeef7 fix: Tests run against e2e config for both playwright and older tests (#4283)
Co-authored-by: IbrahimCSAE <ibrahim.mdev@gmail.com>
2024-07-09 11:13:07 -04:00
ohif-bot 993308b32b chore(version): Update package versions [skip ci] 2024-07-05 18:15:46 +00:00
ohif-bot 3a0b36d4e4 chore(version): version.json [skip ci] 2024-07-05 18:15:38 +00:00
Bill Wallace 24c511f4bc fix: webpack import bugs showing warnings on import (#4265) 2024-07-05 13:19:42 -04:00
Hemant Patel 6d11048ca5 fix(image-orientation): Prevent incorrect orientation marker display for single-slice US images (#4275) 2024-07-05 13:08:48 -04:00
Bill Wallace 6fd3c7e293 fix: Cobb angle not working in basic-test mode and open contour (#4280) 2024-07-05 12:43:18 -04:00
Bill Wallace a2084f319b feat: Add interleaved HTJ2K and volume progressive loading (#4276) 2024-07-05 12:33:07 -04:00
ohif-bot 2845f07806 chore(version): Update package versions [skip ci] 2024-07-04 02:24:23 +00:00
ohif-bot 78b814224e chore(version): version.json [skip ci] 2024-07-04 02:24:16 +00:00
Celian-abd b2c629123c fix: stdValue in TMTV mode (#4278) 2024-07-03 22:10:47 -04:00
ohif-bot 6102e5d80f chore(version): Update package versions [skip ci] 2024-07-02 14:34:25 +00:00
ohif-bot 9b9e5be477 chore(version): version.json [skip ci] 2024-07-02 14:34:18 +00:00
Ibrahim 32762e6bce test(weekly): crosshair tests (#4273) 2024-07-02 10:20:31 -04:00
ohif-bot 6faf3a17f9 chore(version): Update package versions [skip ci] 2024-07-02 13:22:18 +00:00
ohif-bot bd742d19b2 chore(version): version.json [skip ci] 2024-07-02 13:22:10 +00:00
Salim Kanoun 5531487fd6 docs : Removed deprecated useBulkDataURI (#4272) 2024-07-02 09:08:37 -04:00
ohif-bot 6c7d01a184 chore(version): Update package versions [skip ci] 2024-06-28 13:45:33 +00:00
ohif-bot 369851d87f chore(version): version.json [skip ci] 2024-06-28 13:45:27 +00:00
Alireza 16cfab2aca chore: remove polyfill io (#4270) 2024-06-28 09:31:47 -04:00
ohif-bot e60d3bc069 chore(version): Update package versions [skip ci] 2024-06-28 13:23:20 +00:00
ohif-bot a9e0cc5518 chore(version): version.json [skip ci] 2024-06-28 13:23:13 +00:00
Leonardo Campos 2048b19484 feat(studyPrefetcher): Study Prefetcher (#4206) 2024-06-28 08:58:39 -04:00
ohif-bot 257cc477cd chore(version): Update package versions [skip ci] 2024-06-28 12:29:57 +00:00
ohif-bot c9362726eb chore(version): version.json [skip ci] 2024-06-28 12:29:50 +00:00
Salim Kanoun 02eb256dc7 docs: Add prettier tailwind (#4269)
This fixes some docs issues/reformatting issues broken by changes to exclude unneeded dependencies.
2024-06-28 08:06:34 -04:00
ohif-bot 897de44856 chore(version): Update package versions [skip ci] 2024-06-27 16:52:54 +00:00
ohif-bot cfb06e458b chore(version): version.json [skip ci] 2024-06-27 16:52:47 +00:00
Salim Kanoun f660f8e970 fix(cli): missing js (#4268) 2024-06-27 12:38:43 -04:00
ohif-bot bb2f5625fc chore(version): Update package versions [skip ci] 2024-06-27 13:29:38 +00:00
ohif-bot a883f0de66 chore(version): version.json [skip ci] 2024-06-27 13:29:32 +00:00
Salim Kanoun d09f8b5ba2 fix(cli): Fix the cli utilities which require full paths
reset .js extension in import (#4267)
2024-06-27 09:15:22 -04:00
ohif-bot 976003d665 chore(version): Update package versions [skip ci] 2024-06-26 16:40:54 +00:00
ohif-bot 8714f01f9d chore(version): version.json [skip ci] 2024-06-26 16:40:47 +00:00
Bill Wallace fdf883ada8 fix(orthanc): Correct bulkdata URL handling and add configuration example PDF (#4262) 2024-06-26 12:26:00 -04:00
ohif-bot bea56d4f66 chore(version): Update package versions [skip ci] 2024-06-26 02:30:49 +00:00
ohif-bot 6c38381ac2 chore(version): version.json [skip ci] 2024-06-26 02:30:42 +00:00
Ibrahim 75ef99d3b6 test(hyrdation): weekly tests PR (#4263) 2024-06-25 22:17:12 -04:00
ohif-bot ff1d97f76e chore(version): Update package versions [skip ci] 2024-06-25 14:30:15 +00:00
ohif-bot 2155b0641d chore(version): version.json [skip ci] 2024-06-25 14:30:09 +00:00
Ibrahim c9cc372581 test(improve-setup): sharding configuration, allow accessing cornerstone methods and events in tests, and add types (#4255) 2024-06-25 10:16:51 -04:00
ohif-bot 2bcdb52db9 chore(version): Update package versions [skip ci] 2024-06-21 21:35:35 +00:00
ohif-bot 16751ee141 chore(version): version.json [skip ci] 2024-06-21 21:35:28 +00:00
Alireza 71a1e561bf chore(build): move exaca to dependencies (#4253) 2024-06-21 17:21:51 -04:00
aef00e91d6 fix(CustomViewportOverlay): pass accurate data to Custom Viewport Functions (#4224)
Co-authored-by: Michael Andersen <Michael.Andersen@rmp.uhn.ca>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2024-06-21 16:22:35 -04:00
Ibrahim d5d821464a feat(HP): Frame View HP (#4235) 2024-06-21 11:22:05 -04:00
Bill Wallace 9d0b729dc5 test: Unit test was failing (#4249) 2024-06-21 10:57:35 -04:00
Ibrahim f3bbfff09b fix(code): remove console log (#4248) 2024-06-21 10:04:02 -04:00
Bill Wallace b6136acff6 chore(build): revert webpack upgrade to fix deploy (#4244) 2024-06-21 09:30:18 -04:00
Ibrahim a433d406e2 feat(sort): custom series sort in study panel (#4214) 2024-06-20 16:58:59 -04:00
IbrahimandSalim Kanoun 6f93449149 fix(studybrowser): Differentiate recent and all in study panel based on a provided time period (#4242)
Co-authored-by: Salim Kanoun <salim.kanoun@gmail.com>
2024-06-20 08:53:56 -04:00
Bill Wallace e462fd31f7 feat: customization service append and customize functionality should run once (#4238) 2024-06-19 16:31:59 -04:00
Bill Wallace 88575c6c09 fix: Allow the mode setup/creation to be async, and provide a few more values to extension/app config/mode setup. (#4016) 2024-06-19 09:32:00 -04:00
ohif-bot 01e200986c chore(version): Update package versions [skip ci] 2024-06-18 02:48:53 +00:00
ohif-bot 0ac7b2faaa chore(version): version.json [skip ci] 2024-06-18 02:48:45 +00:00
Bill Wallace d8f6991dbe fix: Use correct external URL for rendered responses with relative URI (#4236) 2024-06-17 22:36:03 -04:00
ohif-bot 6fc4390645 chore(version): Update package versions [skip ci] 2024-06-18 02:33:30 +00:00
ohif-bot 29d522f5f9 chore(version): version.json [skip ci] 2024-06-18 02:33:23 +00:00
Bill Wallace 2eab049d79 fix: Re-enable hpScale module (#4237) 2024-06-17 22:21:16 -04:00
ohif-bot c7a2000873 chore(version): Update package versions [skip ci] 2024-06-17 14:29:37 +00:00
ohif-bot bd6917a2ca chore(version): version.json [skip ci] 2024-06-17 14:29:30 +00:00
Alireza 097ef76655 fix(cli): version txt had a new line which it should not (#4233) 2024-06-17 10:17:38 -04:00
Michael AndersenandMichael Andersen 729efb6d76 fix(pdf-viewport): Allow Drag and Drop on PDF Viewport (#4225)
Co-authored-by: Michael Andersen <Michael.Andersen@rmp.uhn.ca>
2024-06-14 12:55:09 -04:00
ohif-bot 2f2f6b4c8d chore(version): Update package versions [skip ci] 2024-06-12 19:10:20 +00:00
ohif-bot 36a015a3ae chore(version): version.json [skip ci] 2024-06-12 19:10:12 +00:00
Joeycho 6971287460 fix(sr): rendering issue by running loadSR before updateSR (#4226) 2024-06-12 14:57:38 -04:00
ohif-bot a97666ea4e chore(version): Update package versions [skip ci] 2024-06-12 15:52:26 +00:00
ohif-bot 5a4783cb1a chore(version): version.json [skip ci] 2024-06-12 15:52:19 +00:00
Simon Stemplinger 4e87317db6 docs: broken link to DICOM JSON minimum requirements on DICOM JSON documentation page (#4228) 2024-06-12 11:39:39 -04:00
ohif-bot d5d004292e chore(version): Update package versions [skip ci] 2024-06-12 15:32:19 +00:00
ohif-bot 7ed75bc4e9 chore(version): version.json [skip ci] 2024-06-12 15:32:12 +00:00
Bill Wallace 55dcfa1f69 feat: Add customization merge, append or replace functionality (#3871) 2024-06-12 11:20:27 -04:00
ohif-bot efa6df8bfb chore(version): Update package versions [skip ci] 2024-06-12 14:48:35 +00:00
ohif-bot 58bde5982f chore(version): version.json [skip ci] 2024-06-12 14:48:28 +00:00
Ibrahim 09853abc52 tests(increase-coverage): weekly tests PR (#4221) 2024-06-12 10:36:13 -04:00
ohif-bot 727088f3dc chore(version): Update package versions [skip ci] 2024-06-08 13:02:40 +00:00
ohif-bot 89a0fae244 chore(version): version.json [skip ci] 2024-06-08 13:02:33 +00:00
Alireza 4a3b37c6bb docs(faq, migrations, orthanc): Add FAQ for dynamic measurements, migration guides, and fix orthanc docker (#4217) 2024-06-08 08:52:03 -04:00
ohif-bot f044587b73 chore(version): Update package versions [skip ci] 2024-06-07 20:20:02 +00:00
ohif-bot 7bf9e6214f chore(version): version.json [skip ci] 2024-06-07 20:19:54 +00:00
Ibrahim 33f4c18f26 fix(window-level): move window level region to more tools menu (#4215) 2024-06-07 16:08:52 -04:00
Pedro H. Köhler 319b31d9a8 chore: add extra exports to cornerstone ext (#4216) 2024-06-07 16:02:36 -04:00
ohif-bot 14221eb78e chore(version): Update package versions [skip ci] 2024-06-05 17:21:51 +00:00
ohif-bot bfb6fc5be7 chore(version): version.json [skip ci] 2024-06-05 17:21:43 +00:00
Ibrahim 9f8ad28d71 tests(increase-coverage): weekly tests PR (#4202) 2024-06-05 13:09:53 -04:00
ohif-bot f93cdf326d chore(version): Update package versions [skip ci] 2024-06-05 16:52:29 +00:00
ohif-bot 6d54132386 chore(version): version.json [skip ci] 2024-06-05 16:52:21 +00:00
Igor Octaviano 649ffab4d9 fix: get direct url pixel data should be optional for video (#4152) 2024-06-05 12:39:59 -04:00
ohif-bot 35077f1f02 chore(version): Update package versions [skip ci] 2024-06-05 16:37:26 +00:00
ohif-bot 24fe144fa8 chore(version): version.json [skip ci] 2024-06-05 16:37:18 +00:00
Ibrahim a29e94de80 fix(seg): maintain algorithm name and algorithm type when DICOM seg is exported or downloaded (#4203) 2024-06-05 12:26:17 -04:00
ohif-bot d747c97be3 chore(version): Update package versions [skip ci] 2024-06-05 14:21:01 +00:00
ohif-bot 95c47ed16c chore(version): version.json [skip ci] 2024-06-05 14:20:53 +00:00
Ibrahim 430330f7e3 fix(hydration): Maintain the same slice that the user was on pre hydration in post hydration for SR and SEG. (#4200) 2024-06-05 10:07:53 -04:00
ohif-bot ddc7559e16 chore(version): Update package versions [skip ci] 2024-06-05 14:02:25 +00:00
ohif-bot abf9197b15 chore(version): version.json [skip ci] 2024-06-05 14:02:18 +00:00
Ibrahim ab1a18af5a feat(window-level-region): add window level region tool (#4127) 2024-06-05 09:50:22 -04:00
ohif-bot 4761bbf0c8 chore(version): Update package versions [skip ci] 2024-05-31 15:26:46 +00:00
ohif-bot 8a3367279a chore(version): version.json [skip ci] 2024-05-31 15:26:40 +00:00
Alireza f85da32f34 fix(tmtv): crosshairs should not have viewport indicators (#4197) 2024-05-31 11:14:55 -04:00
ohif-bot 6e6ecd8709 chore(version): Update package versions [skip ci] 2024-05-30 18:37:49 +00:00
ohif-bot e91827f002 chore(version): version.json [skip ci] 2024-05-30 18:37:41 +00:00
Alireza 93e760937f fix(seg): should be able to navigate outside toolbox and come back later (#4196) 2024-05-30 14:25:38 -04:00
ohif-bot 5945887b22 chore(version): Update package versions [skip ci] 2024-05-30 17:18:43 +00:00
ohif-bot b8b65eac77 chore(version): version.json [skip ci] 2024-05-30 17:18:36 +00:00
Alireza 2174026789 fix: segmentation creation and segmentation mode viewport rendering (#4193) 2024-05-30 13:07:37 -04:00
Alireza d7aa386800 fix(docker): docker build was broken because of imports (#4192) 2024-05-30 13:01:14 -04:00
ohif-bot af2237e7c7 chore(version): Update package versions [skip ci] 2024-05-30 16:02:17 +00:00
ohif-bot 4dfb68f9cc chore(version): version.json [skip ci] 2024-05-30 16:02:09 +00:00
Ibrahim 19b5b1c15c fix(tmtv): side panel crashing when activeToolOptions is not an array (#4189) 2024-05-30 11:50:23 -04:00
ohif-bot c02a4f2e26 chore(version): Update package versions [skip ci] 2024-05-30 15:22:57 +00:00
ohif-bot 756c2f88e6 chore(version): version.json [skip ci] 2024-05-30 15:22:50 +00:00
Ibrahim e073d195fd fix(queryparam): set all query params to lowercase by default (#4190) 2024-05-30 11:11:08 -04:00
ohif-bot 4ce7831ea7 chore(version): Update package versions [skip ci] 2024-05-29 16:07:46 +00:00
ohif-bot fe4dfb63e4 chore(version): version.json [skip ci] 2024-05-29 16:07:38 +00:00
Ibrahim 731340d70a fix(contour): set renderFill to false for contour (#4186) 2024-05-29 11:56:03 -04:00
ohif-bot 395894c6a9 chore(version): Update package versions [skip ci] 2024-05-29 15:50:43 +00:00
ohif-bot 8c25e7d557 chore(version): version.json [skip ci] 2024-05-29 15:50:36 +00:00
Bill Wallace 18ac08ed86 feat(hp): Add displayArea option for Hanging protocols and example with Mamo(#3808) 2024-05-29 11:31:09 -04:00
ohif-bot cb30b97610 chore(version): Update package versions [skip ci] 2024-05-29 14:09:04 +00:00
ohif-bot 22d5e51751 chore(version): version.json [skip ci] 2024-05-29 14:08:56 +00:00
Alireza 75a71db7f8 fix(ultrasound): Upgrade cornerstone3D version to resolve coloring issues (#4181) 2024-05-29 09:57:50 -04:00
ohif-bot c6a3ce4804 chore(version): Update package versions [skip ci] 2024-05-29 13:24:13 +00:00
ohif-bot a389daff2f chore(version): version.json [skip ci] 2024-05-29 13:24:05 +00:00
Ibrahim 18686c2caf feat(measurements): show untracked measurements in measurement panel under additional findings (#4160) 2024-05-29 09:12:39 -04:00
ohif-bot 07eb3c527d chore(version): Update package versions [skip ci] 2024-05-28 15:25:49 +00:00
ohif-bot 96860292ea chore(version): version.json [skip ci] 2024-05-28 15:25:39 +00:00
IbrahimandAlireza 7745c092bb fix(rt): dont convert to volume for RTSTRUCT (#4157)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2024-05-28 11:13:14 -04:00
ohif-bot b0af8ec553 chore(version): Update package versions [skip ci] 2024-05-27 18:41:10 +00:00
ohif-bot 2114ffff41 chore(version): version.json [skip ci] 2024-05-27 18:41:02 +00:00
AlirezaandIbrahimCSAE 70f2c797f4 feat(ui): move to React 18 and base for using shadcn/ui (#4174)
Co-authored-by: IbrahimCSAE <ibrahim.mdev@gmail.com>
2024-05-27 14:30:25 -04:00
ohif-bot 72b63dcd6a chore(version): Update package versions [skip ci] 2024-05-24 16:34:01 +00:00
ohif-bot 268569e8c4 chore(version): version.json [skip ci] 2024-05-24 16:33:53 +00:00
Ibrahim 8960b89911 feat(types): typed app config (#4171) 2024-05-24 12:21:09 -04:00
ohif-bot 8c5b228d27 chore(version): Update package versions [skip ci] 2024-05-24 14:07:55 +00:00
ohif-bot a547b47e2d chore(version): version.json [skip ci] 2024-05-24 14:07:45 +00:00
dependabot[bot] 6ca158e4f6 chore(deps): bump axios from 0.28.0 to 0.28.1 in the npm_and_yarn group across 1 directory (#4173)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 09:51:12 -04:00
ohif-bot 8c6ae839ef chore(version): Update package versions [skip ci] 2024-05-24 13:35:33 +00:00
ohif-bot 1ffe71d52a chore(version): version.json [skip ci] 2024-05-24 13:35:25 +00:00
dependabot[bot] 628132eb86 chore(deps): bump axios from 0.25.0 to 0.28.0 in the npm_and_yarn group across 1 directory (#4170)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 09:24:04 -04:00
ohif-bot 9a8ee2b3fb chore(version): Update package versions [skip ci] 2024-05-24 12:48:05 +00:00
ohif-bot 023fde9b26 chore(version): version.json [skip ci] 2024-05-24 12:47:58 +00:00
dependabot[bot] 2f2df1826b chore(deps): bump the npm_and_yarn group across 2 directories with 2 updates (#4169)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 08:36:16 -04:00
ohif-bot 590a5a81d7 chore(version): Update package versions [skip ci] 2024-05-23 20:30:46 +00:00
ohif-bot c0ff0698f1 chore(version): version.json [skip ci] 2024-05-23 20:30:38 +00:00
IbrahimandAlireza 73d9e99d5d fix(crosshairs): reset angle, position, and slabthickness for crosshairs when reset viewport tool is used (#4113)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2024-05-23 16:18:44 -04:00
ohif-bot 1124c409e9 chore(version): Update package versions [skip ci] 2024-05-23 17:40:58 +00:00
ohif-bot a394f4cea7 chore(version): version.json [skip ci] 2024-05-23 17:40:50 +00:00
Alireza 34c7d72142 fix: dicom json for orthanc by Update package versions for @cornerstonejs dependencies (#4165) 2024-05-23 13:28:31 -04:00
ohif-bot 7b24cc5038 chore(version): Update package versions [skip ci] 2024-05-22 16:00:27 +00:00
ohif-bot 5da97511e3 chore(version): version.json [skip ci] 2024-05-22 16:00:18 +00:00
Ibrahim 4fcee5d449 test(playwright): move readme, add video tutorial (#4156) 2024-05-22 11:48:00 -04:00
ohif-bot 24291c3a11 chore(version): Update package versions [skip ci] 2024-05-21 23:53:08 +00:00
ohif-bot c2132beced chore(version): version.json [skip ci] 2024-05-21 23:52:58 +00:00
b124c91d8f fix(HangingProtocol): fix hp when unsupported series load first (#4145)
Co-authored-by: Michael Andersen <Michael.Andersen@rmp.uhn.ca>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2024-05-21 19:39:44 -04:00
ohif-bot bf3acd076d chore(version): Update package versions [skip ci] 2024-05-21 21:35:15 +00:00
ohif-bot 925d18b463 chore(version): version.json [skip ci] 2024-05-21 21:35:07 +00:00
Ibrahim 5903b0749a feat(rt): allow rendering of points in RT Struct (#4128) 2024-05-21 17:23:18 -04:00
ohif-bot ebe39cb8e6 chore(version): Update package versions [skip ci] 2024-05-21 20:56:32 +00:00
ohif-bot de628ff814 chore(version): version.json [skip ci] 2024-05-21 20:56:23 +00:00
Alireza 29944c8512 fix(segmentation): Address issue where segmentation creation failed on layout change (#4153) 2024-05-21 16:43:47 -04:00
ohif-bot 3ea60c3044 chore(version): Update package versions [skip ci] 2024-05-21 19:02:05 +00:00
ohif-bot 8ecf3424d1 chore(version): version.json [skip ci] 2024-05-21 19:01:57 +00:00
Ibrahim fe1a706446 feat(test): Playwright testing integration (#4146) 2024-05-21 14:48:35 -04:00
ohif-bot 856cd17531 chore(version): Update package versions [skip ci] 2024-05-21 15:34:50 +00:00
ohif-bot 3d27d8079a chore(version): version.json [skip ci] 2024-05-21 15:34:40 +00:00
Alireza 70bb6c4626 fix(stack-invalidation): Resolve stack invalidation if metadata invalidated (#4147) 2024-05-21 11:21:41 -04:00
ohif-bot d4b09738fb chore(version): Update package versions [skip ci] 2024-05-17 17:20:55 +00:00
ohif-bot 533cb26d53 chore(version): version.json [skip ci] 2024-05-17 17:20:46 +00:00
Ibrahim dce1e7d423 fix(select): utilize react portals for select component (#4144) 2024-05-17 13:08:03 -04:00
ohif-bot 0d81ecf166 chore(version): Update package versions [skip ci] 2024-05-16 19:16:03 +00:00
ohif-bot de06a98312 chore(version): version.json [skip ci] 2024-05-16 19:15:55 +00:00
Alireza fb91c6da69 chore: Update CircleCI workflow to include approval step before NPM publish for release branches (#4138) 2024-05-16 15:03:45 -04:00
ohif-bot 391ff2243d chore(version): Update package versions [skip ci] 2024-05-15 15:51:44 +00:00
ohif-bot 3227ec1ddc chore(version): version.json [skip ci] 2024-05-15 15:51:35 +00:00
Ibrahim 8d5c676a5e fix(tmtv): threshold was crashing the side panel (#4119) 2024-05-15 11:39:03 -04:00
ohif-bot bbd41ac3dd chore(version): Update package versions [skip ci] 2024-05-15 13:28:59 +00:00
ohif-bot e096aa8b1a chore(version): version.json [skip ci] 2024-05-15 13:28:50 +00:00
Igor Octaviano 6bba2e70f8 fix: 🐛 Overflow scroll list menu based on screen hight (#4123) 2024-05-15 09:16:02 -04:00
ohif-bot d5759d6c06 chore(version): Update package versions [skip ci] 2024-05-14 17:41:13 +00:00
ohif-bot a289c10eb2 chore(version): version.json [skip ci] 2024-05-14 17:41:04 +00:00
Ibrahim 997a1afd7c test(visual-regression): Add percy tests and improve coverage (#4065) 2024-05-14 13:27:21 -04:00
ohif-bot 953d9eed9c chore(version): Update package versions [skip ci] 2024-05-14 17:10:40 +00:00
ohif-bot c86a141119 chore(version): version.json [skip ci] 2024-05-14 17:10:30 +00:00
Sofien-Sellami 8fa339f296 fix(auth): bind handleUnauthenticated to correct context (#4120) 2024-05-14 12:56:58 -04:00
Igor Octaviano 2de102c73c fix(DicomJSONDataSource): Fix series filtering (#4092) 2024-05-09 16:10:56 -04:00
ohif-bot e341ebe9d6 chore(version): Update package versions [skip ci] 2024-05-08 16:25:20 +00:00
ohif-bot 2046a16dad chore(version): version.json [skip ci] 2024-05-08 16:25:13 +00:00
Ibrahim 374065bc3b feat(typings): Enhance typing support with withAppTypes and custom services throughout OHIF (#4090) 2024-05-08 12:12:49 -04:00
ohif-bot b2cd0172d6 chore(version): Update package versions [skip ci] 2024-05-06 19:07:46 +00:00
ohif-bot cf8a99fbd3 chore(version): version.json [skip ci] 2024-05-06 19:07:38 +00:00
Ibrahim 730f434910 fix(bugs): enhancements and bugs in several areas (#4086) 2024-05-06 14:55:32 -04:00
ohif-bot 01fe7fab3d chore(version): Update package versions [skip ci] 2024-05-06 18:38:31 +00:00
ohif-bot 4ca8595095 chore(version): version.json [skip ci] 2024-05-06 18:38:23 +00:00
Ibrahim 0156bc426f fix(rt): enhanced RT support, utilize SVGs for rendering. (#4074) 2024-05-06 14:26:11 -04:00
ohif-bot 8c304718ad chore(version): Update package versions [skip ci] 2024-04-29 18:28:48 +00:00
ohif-bot 000224ce1a chore(version): version.json [skip ci] 2024-04-29 18:28:39 +00:00
Alireza f95255e8cd chore(version): move to the next beta version [BUMP BETA] (#4073) 2024-04-29 14:16:17 -04:00
ohif-bot 351244e8f3 chore(version): Update package versions [skip ci] 2024-04-29 17:12:22 +00:00
ohif-bot e4749f722c chore(version): version.json [skip ci] 2024-04-29 17:12:15 +00:00
Alireza e4b6478952 chore(version): move to the next beta version [BUMP BETA] (#4072) 2024-04-29 13:00:14 -04:00
ohif-bot 6a8f4b9f38 chore(version): Update package versions [skip ci] 2024-04-29 16:09:24 +00:00
ohif-bot 956fff4e5b chore(version): version.json [skip ci] 2024-04-29 16:09:14 +00:00
Alireza f1a736d193 fix(toolbox): Preserve user-specified tool state and streamline command execution (#4063) 2024-04-29 11:58:03 -04:00
ohif-bot 4784638717 chore(version): Update package versions [skip ci] 2024-04-28 02:01:58 +00:00
ohif-bot 49e82f4756 chore(version): version.json [skip ci] 2024-04-28 02:01:50 +00:00
Ibrahim c09311d3b7 fix(bugs): fix patient header for doc, track ball rotate resize observer and add segmentation button not being enabled on viewport data change (#4068) 2024-04-27 21:50:28 -04:00
ohif-bot b4ed80d99b chore(version): Update package versions [skip ci] 2024-04-25 15:57:15 +00:00
ohif-bot c87081d924 chore(version): version.json [skip ci] 2024-04-25 15:57:08 +00:00
Ibrahim f09dfaf911 docs: updates images (#4064) 2024-04-25 11:45:50 -04:00
ohif-bot 11a4ba3c99 chore(version): Update package versions [skip ci] 2024-04-22 19:20:46 +00:00
ohif-bot 56ccb4f7e0 chore(version): version.json [skip ci] 2024-04-22 19:20:37 +00:00
Mateus Freira dos Santos 6ebd2cc7cb fix(viewport-sync): Enable re-sync image slices in a different position when needed (#3984) 2024-04-22 15:08:03 -04:00
ohif-bot 25c01fc66c chore(version): Update package versions [skip ci] 2024-04-22 18:56:10 +00:00
ohif-bot 2ea882e299 chore(version): version.json [skip ci] 2024-04-22 18:56:02 +00:00
Alireza 52a0c59294 fix(viewport-webworker-segmentation): Resolve issues with viewport detection, webworker termination, and segmentation panel layout change (#4059) 2024-04-22 14:43:26 -04:00
Mateus Freira dos Santos 3ec7e51216 fix(vewport): Add missing blendmodes from cornerstonejs (#4055) 2024-04-22 14:33:52 -04:00
ohif-bot 1b366c92ee chore(version): Update package versions [skip ci] 2024-04-22 14:06:09 +00:00
ohif-bot 13d67d90fc chore(version): version.json [skip ci] 2024-04-22 14:06:01 +00:00
Bill Wallace f0ed3fd7b9 fix(hp): Fails to display any layouts in the layout selector if first layout has multiple stages (#4058) 2024-04-22 09:53:10 -04:00
ohif-bot 65d419dd52 chore(version): Update package versions [skip ci] 2024-04-19 19:23:01 +00:00
ohif-bot 4349fc1085 chore(version): version.json [skip ci] 2024-04-19 19:22:53 +00:00
Alireza 8192e348ec feat(tmtv-mode): Add Brush tools and move SUV peak calculation to web worker (#4053) 2024-04-19 15:11:31 -04:00
ohif-bot 8c5ab942b9 chore(version): Update package versions [skip ci] 2024-04-19 03:32:31 +00:00
ohif-bot ab19d9c0a2 chore(version): version.json [skip ci] 2024-04-19 03:32:23 +00:00
Alireza d250d04580 fix(layouts): and fix thumbnail in touch and update migration guide for 3.8 release (#4052) 2024-04-18 23:21:02 -04:00
ohif-bot 28bc8914a8 chore(version): Update package versions [skip ci] 2024-04-18 15:07:19 +00:00
ohif-bot 3952d465dc chore(version): version.json [skip ci] 2024-04-18 15:07:08 +00:00
Pavel Klochkov 1a013909a1 docs(link): Fix wrong link for the components library in the architecture.md (#4045) 2024-04-18 10:53:21 -04:00
ohif-bot 8f42baf36e chore(version): Update package versions [skip ci] 2024-04-18 02:11:12 +00:00
ohif-bot 2633ef7323 chore(version): version.json [skip ci] 2024-04-18 02:11:05 +00:00
Alireza da7c1a5d8c fix(bugs): and replace seriesInstanceUID and seriesInstanceUIDs URL with seriesInstanceUIDs (#4049) 2024-04-17 21:59:47 -04:00
ohif-bot 058da93863 chore(version): Update package versions [skip ci] 2024-04-18 01:58:25 +00:00
ohif-bot bc819409b3 chore(version): version.json [skip ci] 2024-04-18 01:58:17 +00:00
Ibrahim 170bb96983 fix(bugs): enhancements and bug fixes - final (#4048) 2024-04-17 21:47:09 -04:00
ohif-bot c4e6556259 chore(version): Update package versions [skip ci] 2024-04-17 18:54:51 +00:00
ohif-bot ab61cc9b1f chore(version): version.json [skip ci] 2024-04-17 18:54:41 +00:00
Ibrahim 3754c224b4 fix(bugs): enhancements and bug fixes - more (#4043) 2024-04-17 14:42:40 -04:00
ohif-bot d3cdfd8506 chore(version): Update package versions [skip ci] 2024-04-16 15:17:07 +00:00
ohif-bot aa572daa2c chore(version): version.json [skip ci] 2024-04-16 15:16:59 +00:00
Alireza f99a0bfb31 fix(viewport): Reset viewport state and fix CINE looping, thumbnail resolution, and dynamic tool settings (#4037) 2024-04-16 11:04:48 -04:00
ohif-bot a6c6fff43a chore(version): Update package versions [skip ci] 2024-04-16 13:13:50 +00:00
ohif-bot e687384933 chore(version): version.json [skip ci] 2024-04-16 13:13:42 +00:00
Ibrahim e80fc6f477 fix(bugs): enhancements and bug fixes (#4036) 2024-04-16 09:01:06 -04:00
ohif-bot 41a8d105f1 chore(version): Update package versions [skip ci] 2024-04-10 17:55:21 +00:00
ohif-bot 60b2f69334 chore(version): version.json [skip ci] 2024-04-10 17:55:12 +00:00
Alireza df49a653be feat(SM): remove SM measurements from measurement panel (#4022) 2024-04-10 13:44:25 -04:00
ohif-bot 77693ddef4 chore(version): Update package versions [skip ci] 2024-04-10 15:45:03 +00:00
ohif-bot 6a757f69d8 chore(version): version.json [skip ci] 2024-04-10 15:44:55 +00:00
Ibrahim 2b83393f91 fix(general): enhancements and bug fixes (#4018) 2024-04-10 11:34:13 -04:00
ohif-bot ac0e1c0a2c chore(version): Update package versions [skip ci] 2024-04-10 15:06:15 +00:00
ohif-bot b0b65c582d chore(version): version.json [skip ci] 2024-04-10 15:06:06 +00:00
Igor Octaviano 4f99244d86 fix(dicom-video): Update get direct func for dicom json to use url if present and fix config argument (#4017) 2024-04-10 10:55:19 -04:00
Mohannad Hussain be9bb06565 docker: Enabled Orthanc's DIMSE port for easier loading of DICOM studies in OpenResty-Orthanc Recipe (#3917) 2024-04-10 10:46:27 -04:00
ohif-bot c7e53bdbf8 chore(version): Update package versions [skip ci] 2024-04-10 14:43:05 +00:00
ohif-bot 7c3d67c2a8 chore(version): version.json [skip ci] 2024-04-10 14:42:57 +00:00
Jorge Lopes e9c30a108e fix(MetaDataProvider): Fix tag in GeneralImageModule (#4000) 2024-04-10 10:31:04 -04:00
ohif-bot 603d5cc53a chore(version): Update package versions [skip ci] 2024-04-10 14:27:17 +00:00
ohif-bot 5cc693d26c chore(version): version.json [skip ci] 2024-04-10 14:27:08 +00:00
Bill Wallace 7fac49b449 fix: Microscopy bulkdata and image retrieve (#3894) 2024-04-10 10:16:09 -04:00
ohif-bot 7014e6ca4a chore(version): Update package versions [skip ci] 2024-04-10 04:15:43 +00:00
ohif-bot 87b68bba4a chore(version): version.json [skip ci] 2024-04-10 04:15:35 +00:00
d57e8bc157 feat(4D): Add 4D dynamic volume rendering and new pre-clinical 4d pt/ct mode (#3664)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
Co-authored-by: Neil <neil.a.macphee@gmail.com>
2024-04-09 23:57:34 -04:00
ohif-bot dc0b183dd4 chore(version): Update package versions [skip ci] 2024-04-08 13:39:28 +00:00
ohif-bot 06920ab927 chore(version): version.json [skip ci] 2024-04-08 13:39:21 +00:00
Kay Park dc37802ec1 fix : Keycloak docker image source modification (#4015) 2024-04-08 09:28:51 -04:00
ohif-bot 894a227978 chore(version): Update package versions [skip ci] 2024-04-05 10:00:59 +00:00
ohif-bot 1c9fee1bec chore(version): version.json [skip ci] 2024-04-05 10:00:52 +00:00
5645ac1b27 fix(cornerstone-dicom-sr): Freehand SR hydration support (#3996)
Co-authored-by: Ibrahim <93064150+IbrahimCSAE@users.noreply.github.com>
Co-authored-by: IbrahimCSAE <ibrahim.mdev@gmail.com>
2024-04-05 05:42:43 -04:00
ohif-bot 6e5892d202 chore(version): Update package versions [skip ci] 2024-04-05 03:10:27 +00:00
ohif-bot 9d8997ad84 chore(version): version.json [skip ci] 2024-04-05 03:10:20 +00:00
Ibrahim cea27d438d feat(advanced-roi-tools): new tools and icon updates and overlay bug fixes (#4014) 2024-04-04 23:00:11 -04:00
ohif-bot 78fd8cce86 chore(version): Update package versions [skip ci] 2024-04-05 02:53:53 +00:00
ohif-bot f7c9a5cb64 chore(version): version.json [skip ci] 2024-04-05 02:53:45 +00:00
nithin-trenser 56b1eae635 feat(measurement): Add support measurement label autocompletion (#3855) 2024-04-04 22:42:48 -04:00
ohif-bot 807afb8ce5 chore(version): Update package versions [skip ci] 2024-04-03 13:04:29 +00:00
ohif-bot 5f494598bb chore(version): version.json [skip ci] 2024-04-03 13:04:21 +00:00
Ibrahim fa38e6a07a fix(presentation-state): Iterate over map properly to restore the presentation state (#4013) 2024-04-03 08:53:29 -04:00
ohif-bot d8f66856cc chore(version): Update package versions [skip ci] 2024-04-03 03:44:14 +00:00
ohif-bot 9c39689451 chore(version): version.json [skip ci] 2024-04-03 03:44:05 +00:00
IbrahimandAlireza 9f3235ff09 feat(segmentation): Enhanced segmentation panel design for TMTV (#3988)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2024-04-02 23:33:04 -04:00
ohif-bot e8e209291c chore(version): Update package versions [skip ci] 2024-04-02 13:46:18 +00:00
ohif-bot bc71861619 chore(version): version.json [skip ci] 2024-04-02 13:46:10 +00:00
IbrahimandJoe Boccanfuso b7f90e3951 feat(ViewportActionMenu): window level per viewport / new patient info / colorbars/ 3D presets and 3D volume rendering (#3963)
Co-authored-by: Joe Boccanfuso <109477394+jbocce@users.noreply.github.com>
2024-04-02 09:35:30 -04:00
ohif-bot 07301fc020 chore(version): Update package versions [skip ci] 2024-03-28 20:56:04 +00:00
ohif-bot 1203477dd4 chore(version): version.json [skip ci] 2024-03-28 20:55:57 +00:00
Alireza 158a181670 fix(new layout): address black screen bugs (#4008) 2024-03-28 16:45:11 -04:00
ohif-bot aac3546bed chore(version): Update package versions [skip ci] 2024-03-28 14:52:21 +00:00
ohif-bot 1256a4d565 chore(version): version.json [skip ci] 2024-03-28 14:52:11 +00:00
Ibrahim 617043fe0d feat(layout): new layout selector with 3D volume rendering (#3923) 2024-03-28 10:40:32 -04:00
ohif-bot 6f610b09e1 chore(version): Update package versions [skip ci] 2024-03-27 20:12:57 +00:00
ohif-bot a2e320f9f8 chore(version): version.json [skip ci] 2024-03-27 20:12:47 +00:00
Alireza 566b25a544 feat(toolbar): new Toolbar to enable reactive state synchronization (#3983) 2024-03-27 16:01:32 -04:00
ohif-bot 79d5c36bda chore(version): Update package versions [skip ci] 2024-03-25 15:44:13 +00:00
ohif-bot f47a94317d chore(version): version.json [skip ci] 2024-03-25 15:44:04 +00:00
Ibrahim 45b68e841d feat(worklist): new investigational use text (#3999) 2024-03-25 11:33:24 -04:00
ohif-bot d70aae3233 chore(version): Update package versions [skip ci] 2024-03-19 00:37:56 +00:00
ohif-bot 345b137bec chore(version): version.json [skip ci] 2024-03-19 00:37:48 +00:00
Ibrahim 9bcd1ae6f5 feat(worklist): New worklist buttons and tooltips (#3989) 2024-03-18 20:27:12 -04:00
ohif-bot 6b79dff5c1 chore(version): Update package versions [skip ci] 2024-03-18 18:38:23 +00:00
ohif-bot 88859f75d5 chore(version): version.json [skip ci] 2024-03-18 18:38:16 +00:00
Alireza feed2304c1 fix(SR display): and the token based navigation (#3995) 2024-03-18 14:27:17 -04:00
ohif-bot 08815909c7 chore(version): Update package versions [skip ci] 2024-03-15 13:52:45 +00:00
ohif-bot ed2234a67c chore(version): version.json [skip ci] 2024-03-15 13:52:34 +00:00
Pedro H. Köhler f7fe91c5f6 feat(delete measurement): icon for measurement table (#3775) 2024-03-15 09:35:29 -04:00
ohif-bot 21ec6860f2 chore(version): Update package versions [skip ci] 2024-03-08 13:38:48 +00:00
ohif-bot ac474b7f8f chore(version): version.json [skip ci] 2024-03-08 13:38:41 +00:00
EricB e485d68fd4 fix(cli): mode creation template (#3876) (#3981) 2024-03-08 08:27:37 -05:00
ohif-bot 5089229ff9 chore(version): Update package versions [skip ci] 2024-03-05 16:15:56 +00:00
ohif-bot df09176d63 chore(version): version.json [skip ci] 2024-03-05 16:15:48 +00:00
EricB bb603bd1f8 docs: Typo (#3975) 2024-03-05 11:04:28 -05:00
ohif-bot 82a4a7d377 chore(version): Update package versions [skip ci] 2024-02-28 17:25:27 +00:00
ohif-bot c27fa9c802 chore(version): version.json [skip ci] 2024-02-28 17:25:19 +00:00
EricB 4cdfdae814 fix(docs): Minor typos in hpModule.md (#3962) 2024-02-28 12:13:46 -05:00
ohif-bot 596340803a chore(version): Update package versions [skip ci] 2024-02-22 19:54:20 +00:00
ohif-bot ff4e17f5c2 chore(version): version.json [skip ci] 2024-02-22 19:54:11 +00:00
Bill Wallace 21e8a2bd0b fix(demo): Deploy issue (#3951) 2024-02-22 14:42:46 -05:00
ohif-bot 2a442e30eb chore(version): Update package versions [skip ci] 2024-02-21 18:59:56 +00:00
ohif-bot 8e8a66bd29 chore(version): version.json [skip ci] 2024-02-21 18:59:49 +00:00
Alireza b3a0faf5f5 feat(resize): Optimize resizing process and maintain zoom level (#3889) 2024-02-21 13:49:23 -05:00
ohif-bot a2a0090eeb chore(version): Update package versions [skip ci] 2024-02-14 14:02:25 +00:00
ohif-bot ea552027c5 chore(version): version.json [skip ci] 2024-02-14 14:02:17 +00:00
Ibrahim 0eac386a31 feat(errorboundary): format stack trace properly (#3931) 2024-02-14 08:51:17 -05:00
ohif-bot 40b590d75a chore(version): Update package versions [skip ci] 2024-02-05 19:01:43 +00:00
ohif-bot 01df1763ce chore(version): version.json [skip ci] 2024-02-05 19:01:34 +00:00
Igor Octaviano 5bba98ed84 fix: 🐛 Sort merge results based on default data source (input) (#3903) 2024-02-05 13:46:47 -05:00
ohif-bot 16785936d9 chore(version): Update package versions [skip ci] 2024-01-22 20:52:46 +00:00
ohif-bot 3deb73d642 chore(version): version.json [skip ci] 2024-01-22 20:52:38 +00:00
m00n620 ef46b5a704 fix toggleHpTools to support split primary button (#3900)
Fixes a small bug in toggling buttons with hanging protocols.
2024-01-22 15:42:51 -05:00
ohif-bot ed2bf23cd2 chore(version): Update package versions [skip ci] 2024-01-22 15:07:51 +00:00
ohif-bot 2ff2f3f34d chore(version): version.json [skip ci] 2024-01-22 15:07:43 +00:00
Patrick Wespi a47aeb8bd7 fix: catch errors in getPTImageIdInstanceMetadata (#3897) 2024-01-22 09:58:22 -05:00
ohif-bot 94f40192f5 chore(version): Update package versions [skip ci] 2024-01-22 14:55:27 +00:00
ohif-bot 1c43aa791a chore(version): version.json [skip ci] 2024-01-22 14:55:17 +00:00
Alireza e4a116b074 fix(viewport-sync): remember synced viewports bw stack and volume and RENAME StackImageSync to ImageSliceSync (#3849) 2024-01-22 09:44:35 -05:00
ohif-bot 2460d28afc chore(version): Update package versions [skip ci] 2024-01-19 13:30:55 +00:00
ohif-bot 4a958b8989 chore(version): version.json [skip ci] 2024-01-19 13:30:47 +00:00
Pedro H. Köhler 31b837fa90 fix: is same orientaiton (#3905) 2024-01-19 08:21:22 -05:00
ohif-bot 9bb00d71fa chore(version): Update package versions [skip ci] 2024-01-17 16:01:31 +00:00
ohif-bot 40bbe33a9d chore(version): version.json [skip ci] 2024-01-17 16:01:24 +00:00
Igor Octaviano 911d672835 fix: 🐛 Check merge key for merge data source (#3901) 2024-01-17 10:51:49 -05:00
ohif-bot 9c553fded4 chore(version): Update package versions [skip ci] 2024-01-12 16:10:06 +00:00
ohif-bot 144dd39393 chore(version): version.json [skip ci] 2024-01-12 16:09:58 +00:00
AbishekBista9 df2f50748e docs: Update architecture.md (#3891) 2024-01-12 11:00:17 -05:00
ohif-bot 83a1523408 chore(version): Update package versions [skip ci] 2024-01-12 13:58:08 +00:00
ohif-bot 5a62a983e5 chore(version): version.json [skip ci] 2024-01-12 13:57:58 +00:00
Bill Wallace d00a86b022 fix: Update CS3D to fix second render (#3892) 2024-01-12 08:48:15 -05:00
ohif-bot 710a2dca72 chore(version): Update package versions [skip ci] 2024-01-09 19:31:20 +00:00
ohif-bot 9100b1f02e chore(version): version.json [skip ci] 2024-01-09 19:31:10 +00:00
0049f4c030 feat(hp): enable OHIF to run with partial metadata for large studies at the cost of less effective hanging protocol (#3804)
Co-authored-by: rodrigobasilio2022 <rodrigo.basilio@radicalimaging.com>
Co-authored-by: rodrigobasilio2022 <114958722+rodrigobasilio2022@users.noreply.github.com>
2024-01-09 14:22:00 -05:00
ohif-bot b5cdf88a57 chore(version): Update package versions [skip ci] 2024-01-09 17:45:39 +00:00
ohif-bot c793df727b chore(version): version.json [skip ci] 2024-01-09 17:45:30 +00:00
Alireza 1456a493d6 feat(transferSyntax): prefer server transcoded transfer syntax for all images (#3883) 2024-01-09 12:36:29 -05:00
ohif-bot 7c551f5043 chore(version): Update package versions [skip ci] 2024-01-09 15:24:36 +00:00
ohif-bot 0b2c2a235f chore(version): version.json [skip ci] 2024-01-09 15:24:28 +00:00
Alireza b1efe40aa1 fix(segmentation): upgrade cs3d to fix various segmentation bugs (#3885) 2024-01-09 10:15:56 -05:00
ohif-bot a8a0bdb2df chore(version): Update package versions [skip ci] 2024-01-08 19:34:59 +00:00
ohif-bot 6878c1a264 chore(version): version.json [skip ci] 2024-01-08 19:34:49 +00:00
AbishekBista9 d181eb4fdc docs: Update user-account-control.md (#3877) 2024-01-08 14:24:42 -05:00
ohif-bot fd809e4a0b chore(version): Update package versions [skip ci] 2024-01-08 19:21:13 +00:00
ohif-bot 4c29670d76 chore(version): version.json [skip ci] 2024-01-08 19:21:04 +00:00
Igor Octaviano f58725ce40 feat: Add on mode init hook (#3882) 2024-01-08 14:12:26 -05:00
ohif-bot 5a1cb842f0 chore(version): Update package versions [skip ci] 2024-01-08 19:08:04 +00:00
ohif-bot c5bd79552a chore(version): version.json [skip ci] 2024-01-08 19:07:56 +00:00
Sofien-Sellami 61bf22c6f8 feat(ui): sidePanel expandedWidth (#3728) 2024-01-08 13:59:20 -05:00
ohif-bot 3d98548072 chore(version): Update package versions [skip ci] 2024-01-08 18:53:43 +00:00
ohif-bot bf9770a55c chore(version): version.json [skip ci] 2024-01-08 18:53:35 +00:00
Pedro H. KöhlerandIgor Octaviano 2049c0936c feat: improve disableEditing flag (#3875)
Co-authored-by: Igor Octaviano <igoroctaviano@gmail.com>
2024-01-08 13:45:24 -05:00
ohif-bot 7d5607073d chore(version): Update package versions [skip ci] 2024-01-08 18:24:06 +00:00
ohif-bot 1b6910bf76 chore(version): version.json [skip ci] 2024-01-08 18:23:58 +00:00
Bill WallaceandAlireza 98650302c7 fix: PDF display request in v3 (#3878)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2024-01-08 13:15:54 -05:00
Salim Kanoun bf846c94c3 fix: convert radian to degree value for mip rotation (#3881) 2024-01-08 13:09:22 -05:00
ohif-bot b5dc9047ba chore(version): Update package versions [skip ci] 2024-01-08 17:37:17 +00:00
ohif-bot 636d8b0458 chore(version): version.json [skip ci] 2024-01-08 17:37:08 +00:00
Celian-abd e8858f3eb5 fix: colormap for stack viewports via HangingProtocol (#3866) 2024-01-08 12:28:22 -05:00
ohif-bot eb7c950d7e chore(version): Update package versions [skip ci] 2023-12-15 02:56:03 +00:00
ohif-bot c6214d66b9 chore(version): version.json [skip ci] 2023-12-15 02:55:55 +00:00
Alireza 0eb502aaf0 chore(version): upgrade cornerstone3D versions to fix orientation (#3854) 2023-12-14 21:47:15 -05:00
ohif-bot 44a5a24d4f chore(version): Update package versions [skip ci] 2023-12-14 03:48:22 +00:00
ohif-bot b0bf5dc780 chore(version): version.json [skip ci] 2023-12-14 03:48:15 +00:00
Alireza 5915fada83 chore(version): upgrade cornerstone3D versions (#3853) 2023-12-13 22:39:41 -05:00
ohif-bot 2fa335c6d9 chore(version): Update package versions [skip ci] 2023-12-13 21:37:11 +00:00
ohif-bot de4ad88a09 chore(version): version.json [skip ci] 2023-12-13 21:37:03 +00:00
Sofien-Sellami 6ca13c0a4c fix(icon-style): Ensure consistent icon dimensions (#3727) 2023-12-13 16:28:20 -05:00
Pedro H. Köhler 0177b625ba feat(overlay): add inline binary overlays (#3852) 2023-12-13 16:18:49 -05:00
ohif-bot c4064acfbc chore(version): Update package versions [skip ci] 2023-12-13 20:27:22 +00:00
ohif-bot 7a05d169ef chore(version): version.json [skip ci] 2023-12-13 20:27:14 +00:00
dxlin 13d0c883f8 docs: Update HangingProtocolService.md doc sameAs example to contain constr… (#3765) 2023-12-13 15:18:15 -05:00
ohif-bot c45f60dce2 chore(version): Update package versions [skip ci] 2023-12-13 20:11:48 +00:00
ohif-bot 7bb2713c5c chore(version): version.json [skip ci] 2023-12-13 20:11:39 +00:00
Celian-abdandAlireza 97cb1f94e1 fix(dicom-seg) : Fix the missing color tag when loading Dicom-Seg (#3822)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2023-12-13 15:03:09 -05:00
ohif-bot f99645f3a1 chore(version): Update package versions [skip ci] 2023-12-13 19:55:36 +00:00
ohif-bot ae16c7f972 chore(version): version.json [skip ci] 2023-12-13 19:55:28 +00:00
Salim Kanoun 9c6e1cd305 Fix(HPService): custom image load performed is now reset to false on HP exit (#3809) 2023-12-13 14:47:12 -05:00
ohif-bot 7f0ca857fa chore(version): Update package versions [skip ci] 2023-12-13 19:29:55 +00:00
ohif-bot 920256d3a4 chore(version): version.json [skip ci] 2023-12-13 19:29:48 +00:00
Pedro H. Köhler e1f55e65f2 feat(customizationService): Enable saving and loading of private tags in SRs (#3842) 2023-12-13 14:21:46 -05:00
ohif-bot d1c07d90f0 chore(version): Update package versions [skip ci] 2023-12-13 15:29:16 +00:00
ohif-bot b1a04ecac3 chore(version): version.json [skip ci] 2023-12-13 15:29:09 +00:00
Sofien-Sellami f707b4ebc9 feat(config): Add activateViewportBeforeInteraction parameter for viewport interaction customization (#3847) 2023-12-13 10:17:48 -05:00
Pedro H. Köhler 805c53270f fix: address and improve system vulnerabilities (#3851) 2023-12-13 09:25:41 -05:00
Pavel d14a8f0199 feat(i18n): enhanced i18n support (#3761) 2023-12-11 09:43:58 -05:00
ohif-bot 5a5c4f58af chore(version): Update package versions [skip ci] 2023-12-08 14:35:30 +00:00
ohif-bot 6e5d8b691e chore(version): version.json [skip ci] 2023-12-08 14:35:23 +00:00
Joe Boccanfuso 59576d695d feat(HP): Added new 3D hanging protocols to be used in the new layout selector (#3844) 2023-12-08 09:25:57 -05:00
ohif-bot 78d4bea04d chore(version): Update package versions [skip ci] 2023-12-06 13:37:37 +00:00
ohif-bot 806c2f4b36 chore(version): version.json [skip ci] 2023-12-06 13:37:29 +00:00
Alireza 6651008fbb fix(auth): fix the issue with oauth at a non root path (#3840) 2023-12-06 08:28:02 -05:00
ohif-bot 74c98b6e9f chore(version): Update package versions [skip ci] 2023-11-28 18:18:13 +00:00
ohif-bot ecee25b1d5 chore(version): version.json [skip ci] 2023-11-28 18:18:03 +00:00
Alireza f1a67647ae fix(SM): drag and drop is now fixed for SM (#3813) 2023-11-28 13:04:30 -05:00
ohif-bot 4f9a00f872 chore(version): Update package versions [skip ci] 2023-11-27 13:36:36 +00:00
ohif-bot 7673a887e4 chore(version): version.json [skip ci] 2023-11-27 13:36:29 +00:00
Joe Boccanfuso 924affa7b5 fix(cine): Set cine disabled on mode exit. (#3812) 2023-11-27 08:27:38 -05:00
ohif-bot abac6f583e chore(version): Update package versions [skip ci] 2023-11-24 22:41:51 +00:00
ohif-bot 36ba20d6e8 chore(version): version.json [skip ci] 2023-11-24 22:41:42 +00:00
Bill Wallace 9d1884d7d8 fix: Update the CS3D packages to add the most recent HTJ2K TSUIDS (#3806) 2023-11-24 17:32:19 -05:00
ohif-bot 622f18bd3e chore(version): Update package versions [skip ci] 2023-11-24 13:56:33 +00:00
ohif-bot 8b6256e76f chore(version): version.json [skip ci] 2023-11-24 13:56:24 +00:00
Igor Octaviano c4ff2c2f09 feat: Merge Data Source (#3788)
Add the ability to merge two different series queries to generate a complete study query result.  Provides basic support for other types of merges, but those aren't yet added as full features.
2023-11-24 08:46:54 -05:00
ohif-bot 1be5613946 chore(version): Update package versions [skip ci] 2023-11-21 17:47:19 +00:00
ohif-bot 6edb1c1e4c chore(version): version.json [skip ci] 2023-11-21 17:47:12 +00:00
Pedro H. Köhler 404b0a5d53 feat(events): broadcast series summary metadata (#3798) 2023-11-21 12:38:08 -05:00
ohif-bot 786898fd63 chore(version): Update package versions [skip ci] 2023-11-21 14:15:45 +00:00
ohif-bot dfa851600c chore(version): version.json [skip ci] 2023-11-21 14:15:37 +00:00
Bill Wallace 00e751933a fix(DICOM Overlay): The overlay data wasn't being refreshed on change (#3793) 2023-11-21 09:05:25 -05:00
ohif-bot c4e22c251f chore(version): Update package versions [skip ci] 2023-11-21 02:33:40 +00:00
ohif-bot 3cf4b6dea5 chore(version): version.json [skip ci] 2023-11-21 02:33:32 +00:00
Bill Wallace 8c8924af37 fix(metadata): to handle cornerstone3D update for htj2k (#3783) 2023-11-20 21:23:42 -05:00
ohif-bot 04121e3713 chore(version): Update package versions [skip ci] 2023-11-18 01:18:27 +00:00
ohif-bot d1eef6dbdb chore(version): version.json [skip ci] 2023-11-18 01:18:18 +00:00
Joe Boccanfuso d83beb7c62 feat(docs): Added various training videos to support the OHIF CLI tools (#3794) 2023-11-17 20:08:38 -05:00
ohif-bot 7867b29742 chore(version): Update package versions [skip ci] 2023-11-15 14:46:52 +00:00
ohif-bot 4a6f1bd407 chore(version): version.json [skip ci] 2023-11-15 14:46:44 +00:00
Igor Octaviano b694228dd5 feat(url): Add SeriesInstanceUIDs wado query param (#3746) 2023-11-15 09:37:09 -05:00
ohif-bot 013068bcb0 chore(version): Update package versions [skip ci] 2023-11-13 15:52:59 +00:00
ohif-bot 3fbc23b04c chore(version): version.json [skip ci] 2023-11-13 15:52:50 +00:00
Igor Octaviano 323401418e fix: 🐛 Run error handler for failed image requests (#3773) 2023-11-13 10:42:55 -05:00
ohif-bot fc438e90ae chore(version): Update package versions [skip ci] 2023-11-13 14:00:34 +00:00
ohif-bot 9b7f38467b chore(version): version.json [skip ci] 2023-11-13 14:00:27 +00:00
Bill Wallace fd1251f751 fix(overlay): Overlays aren't shown on undefined origin (#3781) 2023-11-13 08:50:58 -05:00
ohif-bot 14793493a4 chore(version): Update package versions [skip ci] 2023-11-10 22:00:19 +00:00
ohif-bot 73d8848707 chore(version): version.json [skip ci] 2023-11-10 22:00:11 +00:00
Alireza 43b1c17209 feat(dicomJSON): Add Loading Other Display Sets and JSON Metadata Generation script (#3777) 2023-11-10 16:51:23 -05:00
ohif-bot 5b513fd30e chore(version): Update package versions [skip ci] 2023-11-10 14:07:30 +00:00
ohif-bot 26c9567c38 chore(version): version.json [skip ci] 2023-11-10 14:07:23 +00:00
Pedro H. Köhler 8bbcd0e692 fix(path): upgrade docusaurus for security (#3780) 2023-11-10 08:54:40 -05:00
ohif-bot 8c2f495405 chore(version): Update package versions [skip ci] 2023-11-09 04:13:50 +00:00
ohif-bot e1e1e60b7d chore(version): version.json [skip ci] 2023-11-09 04:13:43 +00:00
edwardyangxin 8af1046803 fix(arrow): ArrowAnnotate text key cause validation error (#3771) 2023-11-08 23:04:55 -05:00
ohif-bot 4bbba871f5 chore(version): Update package versions [skip ci] 2023-11-08 21:09:51 +00:00
ohif-bot 73e804c311 chore(version): version.json [skip ci] 2023-11-08 21:09:42 +00:00
Pedro H. Köhler 442f99d5eb feat: add VolumeViewport rotation (#3776) 2023-11-08 16:00:18 -05:00
ohif-bot 9575a6f54c chore(version): Update package versions [skip ci] 2023-11-08 14:28:55 +00:00
ohif-bot dc345d1875 chore(version): version.json [skip ci] 2023-11-08 14:28:48 +00:00
AlirezaandOuwen Huang bf252bcec2 feat(hp callback): Add viewport ready callback (#3772)
Co-authored-by: Ouwen Huang <ouwen.huang@duke.edu>
2023-11-08 09:19:49 -05:00
ohif-bot 1a68c06c3b chore(version): Update package versions [skip ci] 2023-11-03 16:29:50 +00:00
ohif-bot a2361fa615 chore(version): version.json [skip ci] 2023-11-03 16:29:42 +00:00
Alireza ee67bcc0f5 docs(faq): FAQ and Segmentation Mode Fix for some studies (#3762) 2023-11-03 12:19:24 -04:00
ohif-bot f9f16bc69f chore(version): Update package versions [skip ci] 2023-11-02 16:17:30 +00:00
ohif-bot a29f5bc9d3 chore(version): version.json [skip ci] 2023-11-02 16:17:20 +00:00
Joe Boccanfuso b23eeff937 fix(thumbnail): Avoid multiple promise creations for thumbnails (#3756) 2023-11-02 12:07:52 -04:00
ohif-bot 09ff293f5f chore(version): Update package versions [skip ci] 2023-10-31 19:49:41 +00:00
ohif-bot 71a30dc9f2 chore(version): version.json [skip ci] 2023-10-31 19:49:31 +00:00
Pavel 330e11c7ff feat(i18n): enhanced i18n support (#3730) 2023-10-31 15:36:45 -04:00
ohif-bot c73a403cdc chore(version): Update package versions [skip ci] 2023-10-30 15:01:05 +00:00
ohif-bot 5b3b6eed16 chore(version): version.json [skip ci] 2023-10-30 15:00:58 +00:00
Joe Boccanfusoandladeirarodolfo 2a15ef0e44 feat(filters): save worklist query filters to session storage so that they persist between navigation to the viewer and back (#3749)
Co-authored-by: ladeirarodolfo <39910206+ladeirarodolfo@users.noreply.github.com>
2023-10-30 10:47:43 -04:00
Joe Boccanfuso db395852b6 fix(measurement service): Implemented correct check of schema keys in _isValidMeasurment. (#3750) 2023-10-30 10:44:21 -04:00
ohif-bot 423ba7c269 chore(version): Update package versions [skip ci] 2023-10-25 17:17:55 +00:00
ohif-bot cafba997bf chore(version): version.json [skip ci] 2023-10-25 17:17:47 +00:00
Bill WallaceandJoe Boccanfuso dd6d9768bb fix(toolbar): allow customizable toolbar for active viewport and allow active tool to be deactivated via a click (#3608)
Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
2023-10-25 13:08:12 -04:00
ohif-bot 2145b42920 chore(version): Update package versions [skip ci] 2023-10-24 17:20:41 +00:00
ohif-bot a5ad1cc02a chore(version): version.json [skip ci] 2023-10-24 17:20:32 +00:00
Joe Boccanfuso d98439fe7f fix(sr): dcm4chee requires the patient name for an SR to match what is in the original study (#3739) 2023-10-24 13:06:12 -04:00
ohif-bot 3bbbcea0d7 chore(version): Update package versions [skip ci] 2023-10-23 14:03:11 +00:00
ohif-bot 6e993511b7 chore(version): version.json [skip ci] 2023-10-23 14:03:01 +00:00
bluesteel23 eec9c33b76 docs(azure): Azure static Deployment details (#3740) 2023-10-23 09:53:43 -04:00
ohif-bot c31c7f8e79 chore(version): Update package versions [skip ci] 2023-10-23 13:23:07 +00:00
ohif-bot a7125f9385 chore(version): version.json [skip ci] 2023-10-23 13:22:59 +00:00
edwardyangxin 49514aedfe fix(recipes): package.json script orthanc:up docker-compose path (#3741) 2023-10-23 09:13:24 -04:00
ohif-bot 43c882ac87 chore(version): Update package versions [skip ci] 2023-10-19 17:39:23 +00:00
ohif-bot 912b3c7c53 chore(version): version.json [skip ci] 2023-10-19 17:39:15 +00:00
d9258eca70 fix(cine): Use the frame rate specified in DICOM and optionally auto play cine (#3735)
Co-authored-by: rareramos <rareramos1126@gmail.com>
Co-authored-by: Doug Horner <horner@mieweb.com>
Co-authored-by: Rehan <58819707+Rayhan-011@users.noreply.github.com>
2023-10-19 13:29:44 -04:00
ohif-bot fa599d05fb chore(version): Update package versions [skip ci] 2023-10-19 17:10:43 +00:00
ohif-bot 5f8325e338 chore(version): version.json [skip ci] 2023-10-19 17:10:36 +00:00
Joe Boccanfuso 93d798db99 fix(calibration): No calibration popup caused by perhaps an unused code optimization for production builds (#3736) 2023-10-19 13:02:03 -04:00
ohif-bot 33f1259408 chore(version): Update package versions [skip ci] 2023-10-12 21:18:38 +00:00
ohif-bot c54658a320 chore(version): version.json [skip ci] 2023-10-12 21:18:31 +00:00
Alireza 55002fbd71 chore(version): move to the next beta version [BUMP BETA] 2023-10-12 17:09:44 -04:00
ohif-bot 3f9ee1451b chore(version): Update package versions [skip ci] 2023-10-11 16:10:32 +00:00
ohif-bot 70ea13ec1f chore(version): version.json [skip ci] 2023-10-11 16:10:25 +00:00
Alireza 157b88c909 fix(display messages): broken after timings (#3719) 2023-10-11 12:01:32 -04:00
ohif-bot 75c3f94177 chore(version): Update package versions [skip ci] 2023-10-11 14:19:25 +00:00
ohif-bot 6874d5f0b9 chore(version): version.json [skip ci] 2023-10-11 14:19:16 +00:00
Alireza a3f2a1a7b0 fix(export): wrong export for the tmtv RT function (#3715) 2023-10-11 10:10:29 -04:00
ohif-bot 8067f0c62c chore(version): Update package versions [skip ci] 2023-10-10 19:30:50 +00:00
ohif-bot a894b6ffc5 chore(version): version.json [skip ci] 2023-10-10 19:30:41 +00:00
Joe Boccanfuso c3a5847dcd fix(i18n): display set(s) are two words for English messages (#3711) 2023-10-10 15:20:47 -04:00
ohif-bot 132938bbe0 chore(version): Update package versions [skip ci] 2023-10-10 17:32:24 +00:00
ohif-bot 5ba63bd010 chore(version): version.json [skip ci] 2023-10-10 17:32:14 +00:00
Alireza 7c57f67844 fix(modules): add stylus loader as an option to be uncommented (#3710) 2023-10-10 13:21:17 -04:00
ohif-bot dc929d3df5 chore(version): Update package versions [skip ci] 2023-10-10 14:36:12 +00:00
ohif-bot 5a3af83168 chore(version): version.json [skip ci] 2023-10-10 14:36:03 +00:00
Joe Boccanfuso a9a6ad50ea fix(segmentation): Various fixes for segmentation mode and other (#3709) 2023-10-10 10:25:30 -04:00
ohif-bot f8455056cd chore(version): Update package versions [skip ci] 2023-10-10 00:58:12 +00:00
ohif-bot f9c3d510d3 chore(version): version.json [skip ci] 2023-10-10 00:58:05 +00:00
Alireza 52f34c64d0 fix(voi): should publish voi change event on reset (#3707) 2023-10-09 20:49:10 -04:00
ohif-bot 7d87766ac4 chore(version): Update package versions [skip ci] 2023-10-09 17:58:57 +00:00
ohif-bot 820bb83b70 chore(version): version.json [skip ci] 2023-10-09 17:58:49 +00:00
Alireza 0a42d573bb fix(modality unit): fix the modality unit per target via upgrade of cs3d (#3706) 2023-10-09 13:48:31 -04:00
ohif-bot f9f33f74c1 chore(version): Update package versions [skip ci] 2023-10-09 15:13:37 +00:00
ohif-bot 6a5fe59136 chore(version): version.json [skip ci] 2023-10-09 15:13:28 +00:00
Alireza 4911e4796c fix(segmentation): do not use SAB if not specified (#3705) 2023-10-09 11:02:49 -04:00
ohif-bot 349c503f04 chore(version): Update package versions [skip ci] 2023-10-06 19:14:47 +00:00
ohif-bot a6a6aed361 chore(version): version.json [skip ci] 2023-10-06 19:14:40 +00:00
dxlinandAlireza 40673f64b3 feat(Segmentation): download RTSS from Labelmap(#3692)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2023-10-06 15:00:04 -04:00
ohif-bot eab42c1032 chore(version): Update package versions [skip ci] 2023-10-06 03:49:04 +00:00
ohif-bot a790be9f5a chore(version): version.json [skip ci] 2023-10-06 03:48:56 +00:00
Alireza 8bc12a37d0 fix(bugs): fixing lots of bugs regarding release candidate (#3700) 2023-10-05 23:39:56 -04:00
ohif-bot 29aeb070e0 chore(version): Update package versions [skip ci] 2023-10-06 02:22:47 +00:00
ohif-bot cdb66dcdef chore(version): version.json [skip ci] 2023-10-06 02:22:39 +00:00
Joe Boccanfuso 1fd98d9220 fix(segmentation scroll): and hydration bugs (#3701) 2023-10-05 22:13:08 -04:00
ohif-bot 748acaf9c1 chore(version): Update package versions [skip ci] 2023-10-04 18:25:03 +00:00
ohif-bot 3f27c3820f chore(version): version.json [skip ci] 2023-10-04 18:24:55 +00:00
Joe Boccanfuso c1d5ee7e3f fix(measurement and microscopy): various small fixes for measurement and microscopy side panel (#3696) 2023-10-04 14:11:09 -04:00
ohif-bot 646bef1d93 chore(version): Update package versions [skip ci] 2023-10-04 17:22:52 +00:00
ohif-bot 83c001f8e4 chore(version): version.json [skip ci] 2023-10-04 17:22:43 +00:00
Alirezaandpwespi ebe8f71da2 feat(locale): add German translations - community PR (#3697)
Co-authored-by: pwespi <pwespi@pm.me>
2023-10-04 13:11:31 -04:00
ohif-bot 17f528bcde chore(version): Update package versions [skip ci] 2023-10-04 16:34:07 +00:00
ohif-bot ed82511acc chore(version): version.json [skip ci] 2023-10-04 16:33:58 +00:00
AlirezaandAhmet Altay 745050a28e feat(locale): Added Turkish language support (tr-TR) - Community PR (#3695)
Co-authored-by: Ahmet Altay <46381367+ahmetaltay33@users.noreply.github.com>
2023-10-04 12:22:26 -04:00
ohif-bot 953c564d98 chore(version): Update package versions [skip ci] 2023-10-04 16:17:52 +00:00
ohif-bot c418185f0b chore(version): version.json [skip ci] 2023-10-04 16:17:44 +00:00
wangxuan 29748d46a1 fix(translation): Side panel translate fix (#3156) 2023-10-04 12:07:30 -04:00
ohif-bot a755cce7f1 chore(version): Update package versions [skip ci] 2023-10-04 15:37:52 +00:00
ohif-bot 42b7d9983c chore(version): version.json [skip ci] 2023-10-04 15:37:44 +00:00
Joe Boccanfuso 28cec04ff4 fix(cli): Add npm packaged mode not working (#3689) 2023-10-04 11:27:55 -04:00
ohif-bot 01d843f267 chore(version): Update package versions [skip ci] 2023-10-03 21:11:31 +00:00
ohif-bot 15eca78b68 chore(version): version.json [skip ci] 2023-10-03 21:11:23 +00:00
Bill Wallace 108383b9ef feat(debug): Add timing information about time to first image/all images, and query time (#3681) 2023-10-03 17:01:00 -04:00
ohif-bot 1b207f14d2 chore(version): Update package versions [skip ci] 2023-10-03 19:28:23 +00:00
ohif-bot 38cd3d8afc chore(version): version.json [skip ci] 2023-10-03 19:28:14 +00:00
AlirezaandOuwen Huang 5e7fe91617 feat(displayArea): add display area to hanging protocol (#3691)
Co-authored-by: Ouwen Huang <ouwen.oh@gmail.com>
2023-10-03 15:18:15 -04:00
ohif-bot 64f68f58c2 chore(version): Update package versions [skip ci] 2023-10-03 18:36:48 +00:00
ohif-bot 49bc346661 chore(version): version.json [skip ci] 2023-10-03 18:36:39 +00:00
Yaroslav Halchenko 5cc1dc9b9c docs(links): Adding references to two used markdown links (#3650) 2023-10-03 14:26:31 -04:00
ohif-bot 2a086a699a chore(version): Update package versions [skip ci] 2023-10-03 18:12:49 +00:00
ohif-bot 58f214bca5 chore(version): version.json [skip ci] 2023-10-03 18:12:41 +00:00
rodrigobasilio2022 4dc2acdefa fix(editing): regression bug in disable editing (#3687) 2023-10-03 14:02:21 -04:00
ohif-bot 4c88a4a2fd chore(version): Update package versions [skip ci] 2023-10-03 16:35:17 +00:00
ohif-bot 06facd8ea2 chore(version): version.json [skip ci] 2023-10-03 16:35:08 +00:00
Edward Sonandedward65 eb22328fc0 fix(typescript error): Change pubSubServiceInterface file type to typescript (#3546)
Co-authored-by: edward65 <edward@afxmedical.com>
2023-10-03 12:23:40 -04:00
ohif-bot b33ad0c7e9 chore(version): Update package versions [skip ci] 2023-10-03 15:55:03 +00:00
ohif-bot 3415cf909f chore(version): version.json [skip ci] 2023-10-03 15:54:54 +00:00
Joe BoccanfusoandAlireza e36a604331 fix(dicom overlay): Handle special cases of ArrayBuffer for various DICOM overlay attributes. (#3684)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2023-10-03 11:44:56 -04:00
Bill Wallace 8a335bd03d fix(StackSync): Miscellaneous fixes for stack image sync (#3663) 2023-10-03 10:59:09 -04:00
ohif-bot b3429729f1 chore(version): Update package versions [skip ci] 2023-10-03 14:22:22 +00:00
ohif-bot e2e806f188 chore(version): version.json [skip ci] 2023-10-03 14:22:12 +00:00
Alireza 1129c155d2 fix(config): support more values for the useSharedArrayBuffer (#3688) 2023-10-03 10:11:32 -04:00
ohif-bot c9c57b04d3 chore(version): Update package versions [skip ci] 2023-09-29 22:04:50 +00:00
ohif-bot d09938b72a chore(version): version.json [skip ci] 2023-09-29 22:04:41 +00:00
Alireza a67d72de85 fix(no sab): should work when shared array buffer is not required (#3686) 2023-09-29 17:54:16 -04:00
ohif-bot 3dabee2342 chore(version): Update package versions [skip ci] 2023-09-29 14:51:58 +00:00
ohif-bot 66abdace30 chore(version): version.json [skip ci] 2023-09-29 14:51:49 +00:00
Joe BoccanfusoandAlireza dc73b18748 fix(cli): various fixes for adding custom modes and extensions (#3683)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2023-09-29 10:35:30 -04:00
ohif-bot 52da92fed4 chore(version): Update package versions [skip ci] 2023-09-26 20:58:57 +00:00
ohif-bot cfbb3cfded chore(version): version.json [skip ci] 2023-09-26 20:58:49 +00:00
Joe Boccanfuso 86f54d0d07 fix(toggleOneUp): fixed one up for main tmtv layout (#3677) 2023-09-26 16:49:06 -04:00
ohif-bot 485cf037a1 chore(version): Update package versions [skip ci] 2023-09-26 19:38:00 +00:00
ohif-bot 3d38122b84 chore(version): version.json [skip ci] 2023-09-26 19:37:51 +00:00
Alireza 8feb5ee3f9 chore(publish): fix timeout problem without lerna (#3679) 2023-09-26 15:27:24 -04:00
ohif-bot 1f906963e7 chore(version): Update package versions [skip ci] 2023-09-26 15:54:37 +00:00
ohif-bot 9a4aa1a5aa chore(version): version.json [skip ci] 2023-09-26 15:54:28 +00:00
Alireza fcd323fa8c chore(publish): fix timeout problem (#3678) 2023-09-26 11:43:37 -04:00
ohif-bot b6e33224c8 chore(version): Update package versions [skip ci] 2023-09-26 13:03:46 +00:00
ohif-bot b8e519a4a9 chore(version): version.json [skip ci] 2023-09-26 13:03:37 +00:00
Alireza 0bf9949f45 chore(build): try to publish to npm again (#3676) 2023-09-26 08:53:40 -04:00
ohif-bot 3431b103e2 chore(version): Update package versions [skip ci] 2023-09-26 03:23:59 +00:00
ohif-bot 00918d86d3 chore(version): version.json [skip ci] 2023-09-26 03:23:52 +00:00
Alireza c86b129f58 chore(build): try to publish to npm (#3675) 2023-09-25 23:13:43 -04:00
ohif-bot 10ca35d5f4 chore(version): Update package versions [skip ci] 2023-09-22 20:00:44 +00:00
ohif-bot a308f7b4b6 chore(version): version.json [skip ci] 2023-09-22 20:00:36 +00:00
Sofien-Sellami 04ca10d877 fix(react-select): update react select package (#3622) 2023-09-22 15:48:12 -04:00
Joe Boccanfuso 85c899b399 feat(SidePanel): new side panel tab look-and-feel (#3657) 2023-09-22 15:36:44 -04:00
Alireza 48bbd6281a feat(segmentation mode): Add create, and export SEG with Brushes (#3632) 2023-09-22 10:23:44 -04:00
ohif-bot 896866e9d0 chore(version): Update package versions [skip ci] 2023-09-22 13:40:25 +00:00
ohif-bot a02a5ded9a chore(version): version.json [skip ci] 2023-09-22 13:40:16 +00:00
AlirezaandOuwen Huang 2bd3b26a6a perf(memory): add 16 bit texture via configuration - reduces memory by half (#3662)
Co-authored-by: Ouwen Huang <ouwen.huang@duke.edu>
2023-09-22 09:31:45 -04:00
ohif-bot 034a6d7696 chore(version): Update package versions [skip ci] 2023-09-21 20:53:19 +00:00
ohif-bot 2fc28978da chore(version): version.json [skip ci] 2023-09-21 20:53:11 +00:00
Joe Boccanfuso 221dedde5d fix(mpr): Return the original/raw hanging protocol when fetching and preserving the current active protocol. (#3670) 2023-09-21 16:45:15 -04:00
ohif-bot 807bd663b3 chore(version): Update package versions [skip ci] 2023-09-21 11:38:01 +00:00
ohif-bot 0318504597 chore(version): version.json [skip ci] 2023-09-21 11:37:54 +00:00
Alireza d609ae1edb docs(url): fix incorrect url param in the doc (#3667) 2023-09-21 07:30:01 -04:00
ohif-bot 2132f0777e chore(version): Update package versions [skip ci] 2023-09-19 14:19:42 +00:00
ohif-bot 10ecd4997b chore(version): version.json [skip ci] 2023-09-19 14:19:34 +00:00
AlirezaandJoe Boccanfuso 2d7721cb58 fix(keyCloak): fix openresty keycloak deployment recipe (#3655)
Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
2023-09-19 10:11:32 -04:00
ohif-bot c49b833274 chore(version): Update package versions [skip ci] 2023-09-18 15:10:25 +00:00
ohif-bot 3279df543f chore(version): version.json [skip ci] 2023-09-18 15:10:17 +00:00
Joe Boccanfuso 2737903386 fix(DicomJson): retrieve.series.metadata method should be async (#3659) 2023-09-18 11:02:27 -04:00
ohif-bot 9ccdaef812 chore(version): Update package versions [skip ci] 2023-09-15 14:58:43 +00:00
ohif-bot 8c6764a936 chore(version): version.json [skip ci] 2023-09-15 14:58:36 +00:00
Bill Wallace 38af3112ec fix(measurements): Update the calibration tool to match changes in CS3D (#3505)
This change makes the OHIF side consistent with the CS3D user calibration settings, and will correctly display px and rounding values consistent with the CS3D display.
2023-09-15 10:50:23 -04:00
ohif-bot bd7f9592ea chore(version): Update package versions [skip ci] 2023-09-12 14:38:15 +00:00
ohif-bot fd74e46280 chore(version): version.json [skip ci] 2023-09-12 14:38:08 +00:00
Ibrahim 74e62a1763 fix(health imaging): studies not loading from healthimaging if imagepositionpatient is missing (#3646) 2023-09-12 10:30:02 -04:00
ohif-bot 7eda0c5f46 chore(version): Update package versions [skip ci] 2023-09-12 11:49:05 +00:00
ohif-bot 15e1af2526 chore(version): version.json [skip ci] 2023-09-12 11:48:58 +00:00
Yaroslav Halchenko 11ca5b6eae docs(spelling): Add codespell config + github action, run and fix a good number of typos (#3645) 2023-09-12 07:40:38 -04:00
2602 changed files with 167423 additions and 33014 deletions

No files matched your search

+222 -173
View File
@@ -13,22 +13,24 @@ version: 2.1
##
orbs:
codecov: codecov/codecov@1.0.5
cypress: cypress-io/cypress@1.26.0
cypress: cypress-io/cypress@3.3.1
executors:
# Custom executor to override Cypress config
deploy-to-prod-executor:
cypress-custom:
description: |
Single Docker container used to run Cypress Tests
docker:
- image: cimg/node:16.14
environment:
CYPRESS_BASE_URL: https://ohif-staging.netlify.com/
chrome-and-pacs:
docker:
# Primary container image where all steps run.
- image: 'cypress/browsers:node16.14.2-slim-chrome103-ff102'
- image: cimg/node:<< parameters.node-version >>-browsers
parameters:
node-version:
default: '18.16.1'
description: |
The version of Node to run your tests with.
type: string
defaults: &defaults
docker:
- image: cimg/node:16.14-browsers
- image: cimg/node:18.18
environment:
TERM: xterm # Enable colors in term
QUICK_BUILD: true
@@ -40,9 +42,11 @@ jobs:
###
UNIT_TESTS:
<<: *defaults
resource_class: large
steps:
# Update yarn
- run: yarn -v
- run: node --version
# Checkout code and ALL Git Tags
- checkout
- restore_cache:
@@ -90,48 +94,12 @@ jobs:
file: '/home/circleci/repo/platform/core/coverage/reports'
flags: 'core'
###
# Workflow: PR_OPTIONAL_DOCKER_PUBLISH
###
DOCKER_PR_PUBLISH:
<<: *defaults
steps:
# Enable yarn workspaces
- run: yarn config set workspaces-experimental true
# Checkout code and ALL Git Tags
- checkout
- 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
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build and push Docker image
command: |
# Remove npm config
rm -f ./.npmrc
# Set our version number using vars
echo $CIRCLE_BUILD_NUM
# Build our image, auth, and push
docker build --tag ohif/app:PR_BUILD-$CIRCLE_BUILD_NUM .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push ohif/app:PR_BUILD-$CIRCLE_BUILD_NUM
###
# Workflow: DEPLOY
###
BUILD:
<<: *defaults
resource_class: large
steps:
# Checkout code and ALL Git Tags
- checkout
@@ -177,68 +145,10 @@ jobs:
- commit.txt
- version.json
# DEPLOY_TO_DEV:
# docker:
# - image: circleci/node:16.14.0
# environment:
# TERM: xterm
# NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
# working_directory: ~/repo
# steps:
# - attach_workspace:
# at: ~/repo
# - run: cd .netlify && npm install
# - run: cp .netlify/deploy-workflow/_redirects platform/app/dist/_redirects
# - run: cd .netlify && npm run deploy
# DEPLOY_TO_STAGING:
# docker:
# - image: circleci/node:16.14.0
# environment:
# TERM: xterm
# NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
# working_directory: ~/repo
# steps:
# - attach_workspace:
# at: ~/repo
# - run: cd .netlify && npm install
# - run: cp .netlify/deploy-workflow/_redirects platform/app/dist/_redirects
# - run: cd .netlify && npm run deploy
# DEPLOY_TO_PRODUCTION:
# docker:
# - image: circleci/node:16.14.0
# environment:
# TERM: xterm
# NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
# working_directory: ~/repo
# steps:
# - attach_workspace:
# at: ~/repo
# - run: cd .netlify && npm install
# - run: cp .netlify/deploy-workflow/_redirects platform/app/dist/_redirects
# - run: cd .netlify && npm run deploy
# DEPLOY_TO_RELEASE_DEV:
# docker:
# - image: circleci/node:16.14.0
# environment:
# TERM: xterm
# NETLIFY_SITE_ID: 3270878-22
# working_directory: ~/repo
# steps:
# - attach_workspace:
# at: ~/repo
# - run: cd .netlify && npm install
# - run:
# cp .netlify/deploy-workflow/_redirects platform/app/dist/_redirects
# - run: cd .netlify && npm run deploy
###
# Workflow: RELEASE
###
NPM_PUBLISH:
# just to make sure later on we can publish them
BUILD_PACKAGES_QUICK:
<<: *defaults
resource_class: large
steps:
- run: yarn -v
# Checkout code and ALL Git Tags
@@ -271,9 +181,51 @@ jobs:
name: Authenticate with NPM registry
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
- run:
name: Increase the event emitter limit
name: build half of the packages (to avoid out of memory in circleci)
command: |
node ./increaseEventEmitterLimit.mjs
yarn run build:package-all
- run:
name: build the other half of the packages
command: |
yarn run build:package-all-1
###
# Workflow: RELEASE
###
NPM_PUBLISH:
<<: *defaults
resource_class: large
steps:
- run: yarn -v
# Checkout code and ALL Git Tags
- checkout
- attach_workspace:
at: ~/repo
# Use increasingly general patterns to restore cache
- 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
- save_cache:
name: Save Yarn Package Cache
paths:
- ~/.cache/yarn
key: yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Avoid hosts unknown for github
command: |
rm -rf ~/.ssh
mkdir ~/.ssh/
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
git config --global user.name "ohif-bot"
- run:
name: Authenticate with NPM registry
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
- run:
name: build half of the packages (to avoid out of memory in circleci)
command: |
@@ -282,6 +234,14 @@ jobs:
name: build the other half of the packages
command: |
yarn run build:package-all-1
- run:
name: increase min time out
command: |
npm config set fetch-retry-mintimeout 20000
- run:
name: increase max time out
command: |
npm config set fetch-retry-maxtimeout 120000
- run:
name: publish package versions
command: |
@@ -300,6 +260,7 @@ jobs:
DOCKER_RELEASE_PUBLISH:
<<: *defaults
resource_class: large
steps:
- attach_workspace:
at: ~/repo
@@ -329,6 +290,7 @@ jobs:
DOCKER_BETA_PUBLISH:
<<: *defaults
resource_class: large
steps:
- attach_workspace:
at: ~/repo
@@ -362,71 +324,153 @@ jobs:
docker push ohif/app:$IMAGE_VERSION_FULL
fi
workflows:
version: 2
# This is copied from the Cypress orb since the default for cypress/run is node 16 and
# we migrated to 18
CYPRESS_CUSTOM_RUN:
description: |
A single, complete job to run Cypress end-to-end tests in your application.
executor: cypress-custom
parallelism: << parameters.parallelism >>
parameters:
cypress-cache-key:
default: cypress-cache-{{ arch }}-{{ checksum "package.json" }}
description: Cache key used to cache the Cypress binary.
type: string
cypress-cache-path:
default: ~/.cache/Cypress
description: |
By default, this will cache the '~/.cache/Cypress' directory so that the Cypress binary is cached. You can override this by providing your own cache path.
type: string
cypress-command:
default: npx cypress run
description: Command used to run your Cypress tests
type: string
include-branch-in-node-cache-key:
default: false
description: |
If true, this cache will only apply to runs within the same branch. (Adds -{{ .Branch }}- to the node cache key)
type: boolean
install-browsers:
default: false
description: |
Cypress runs by default in the Electron browser. Use this flag to install additional browsers to run your tests in.
This is only needed if you are passing the `--browser` flag in your `cypress-command`.
This parameter leverages the `circleci/browser-tools` orb and includes Chrome and FireFox.
If you need additional browser support you can set this to false and use an executor with a docker image
that includes the browsers of your choosing. See https://hub.docker.com/r/cypress/browsers/tags
type: boolean
install-command:
default: ''
description: Overrides the default NPM command (npm ci)
type: string
node-cache-version:
default: v1
description:
Change the default node cache version if you need to clear the cache for any reason.
type: string
package-manager:
default: npm
description: Select the default node package manager to use. NPM v5+ Required.
enum:
- npm
- yarn
- yarn-berry
type: enum
parallelism:
default: 4
description: |
Number of Circle machines to use for load balancing, min 1
(requires `parallel` and `record` flags in your `cypress-command`)
type: integer
post-install:
default: ''
description: |
Additional commands to run after running install but before verifying Cypress and saving cache.
type: string
start-command:
default: ''
description: Command used to start your local dev server for Cypress to tests against
type: string
working-directory:
default: ''
description: Directory containing package.json
type: string
resource_class: large
steps:
- cypress/install:
cypress-cache-key: << parameters.cypress-cache-key >>
cypress-cache-path: << parameters.cypress-cache-path >>
include-branch-in-node-cache-key: << parameters.include-branch-in-node-cache-key >>
install-browsers: << parameters.install-browsers >>
install-command: << parameters.install-command >>
node-cache-version: << parameters.node-cache-version >>
package-manager: << parameters.package-manager >>
post-install: << parameters.post-install >>
working-directory: << parameters.working-directory >>
- cypress/run-tests:
cypress-command: << parameters.cypress-command >>
start-command: << parameters.start-command >>
working-directory: << parameters.working-directory >>
workflows:
PR_CHECKS:
jobs:
- UNIT_TESTS
# E2E: PWA
- cypress/run:
name: 'E2E: PWA'
executor: chrome-and-pacs
browser: chrome
pre-steps:
- run: |
# Clear yarn cache; use yarn from image (update image to update yarn)
rm -rf ~/.yarn
yarn -v
yarn: true
record: true
store_artifacts: true
working_directory: platform/app
build: yarn test:data
start: yarn run test:e2e:serve
spec: 'cypress/integration/**/*'
wait-on: 'http://localhost:3000'
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
no-workspace: true # Don't persist workspace
post-steps:
- store_artifacts:
path: platform/app/cypress/screenshots
- store_artifacts:
path: platform/app/cypress/videos
- store_test_results:
path: platform/app/cypress/results
- BUILD_PACKAGES_QUICK:
filters:
branches:
ignore: master
- UNIT_TESTS:
requires:
- UNIT_TESTS
PR_OPTIONAL_VISUAL_TESTS:
jobs:
- AWAIT_APPROVAL:
type: approval
# Update hub.docker.org
- cypress/run:
name: 'Generate Percy Snapshots'
executor: cypress/browsers-chrome76
browser: chrome
pre-steps:
- run: 'rm -rf ~/.yarn && yarn -v && yarn global add wait-on'
yarn: true
store_artifacts: false
working_directory: platform/app
build:
yarn test:data && 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" }}'
no-workspace: true # Don't persist workspace
post-steps:
- store_artifacts:
path: platform/app/cypress/screenshots
- store_artifacts:
path: platform/app/cypress/videos
- BUILD_PACKAGES_QUICK
- CYPRESS_CUSTOM_RUN:
name: 'Cypress Tests'
context: cypress
matrix:
parameters:
start-command:
- yarn run test:data && yarn run test:e2e:serve
install-browsers:
- true
cypress-command:
- 'npx wait-on@latest http://localhost:3000 && cd platform/app && npx cypress run
--record --browser chrome --parallel'
package-manager:
- 'yarn'
cypress-cache-key:
- 'yarn-packages-{{ checksum "yarn.lock" }}'
cypress-cache-path:
- '~/.cache/Cypress'
requires:
- AWAIT_APPROVAL
- BUILD_PACKAGES_QUICK
# PR_OPTIONAL_VISUAL_TESTS:
# jobs:
# - AWAIT_APPROVAL:
# type: approval
# # Update hub.docker.org
# - cypress/run:
# name: 'Generate Percy Snapshots'
# executor: cypress/browsers-chrome76
# browser: chrome
# pre-steps:
# - run: 'rm -rf ~/.yarn && yarn -v && yarn global add wait-on'
# yarn: true
# store_artifacts: false
# working_directory: platform/app
# build:
# yarn test:data && 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" }}'
# no-workspace: true # Don't persist workspace
# post-steps:
# - store_artifacts:
# path: platform/app/cypress/screenshots
# - store_artifacts:
# path: platform/app/cypress/videos
# requires:
# - AWAIT_APPROVAL
# Our master branch deploys to viewer-dev.ohif.org, the viewer.ohif.org is
# deployed from the release branch which is more stable and less frequently updated.
@@ -450,13 +494,18 @@ workflows:
- BUILD:
filters:
branches:
only: release
- NPM_PUBLISH:
only: /^release\/.*/
- HOLD_FOR_APPROVAL:
type: approval
requires:
- BUILD
- NPM_PUBLISH:
requires:
- HOLD_FOR_APPROVAL
- DOCKER_RELEASE_PUBLISH:
requires:
- NPM_PUBLISH
###
# Unit and E2E tests have already run for PR_CHECKS
# Re-running should not gain us any confidence here
-25
View File
@@ -1,25 +0,0 @@
# ABOUT:
# https://docs.codecov.io/docs/codecov-yaml
#
#
# COMMIT STATUS:
# https://docs.codecov.io/docs/commit-status
coverage:
status:
project:
default:
threshold: 0.5%
core:
flags: core
threshold: 0.5%
viewer:
flags: viewer
threshold: 0.5%
patch: off
flags:
core:
paths:
- platform/core
viewer:
paths:
- platform/app
+6
View File
@@ -0,0 +1,6 @@
[codespell]
skip = .git,*.pdf,*.svg,yarn.lock,*.min.js,locales
# ignore words ending with … and some camelcased variables and names
ignore-regex = \b\S+…\S*|\b(doubleClick|afterAll|PostgresSQL)\b|\bWee, L\.|.*te.*Telugu.*
# some odd variables
ignore-words-list = datea,ser,childrens
-48
View File
@@ -1,48 +0,0 @@
worker_processes 1;
events { worker_connections 1024; }
http {
upstream orthanc-server {
server orthanc:8042;
}
server {
listen [::]:80 default_server;
listen 80;
# CORS Magic
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow_Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow_Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://orthanc:8042;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
# CORS Magic
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow_Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
}
}
}
-23
View File
@@ -1,23 +0,0 @@
version: '3.5'
services:
orthanc:
image: jodogne/orthanc-plugins:1.11.0
hostname: orthanc
volumes:
# Config
- ./config/orthanc.json:/etc/orthanc/orthanc.json:ro
# Persist data
- ./volumes/orthanc-db/:/var/lib/orthanc/db/
ports:
- '4242:4242' # DICOM
- '8042:8042' # Web
restart: unless-stopped
nginx:
image: nginx:latest
volumes:
- ./config/nginx.conf:/etc/nginx/nginx.conf
ports:
- '80:80' #ngnix proxy
depends_on:
- orthanc
-1
View File
@@ -1 +0,0 @@
PERCY_TOKEN=<your token here>
+1 -1
View File
@@ -64,7 +64,7 @@ body:
attributes:
label: 'Node version'
description: 'Your Node.js version.'
placeholder: 'e.g., 16.14.0'
placeholder: 'e.g., 18.16.1'
validations:
required: true
- type: input
+1 -1
View File
@@ -83,7 +83,7 @@ after the commits are squashed.
#### Tested Environment
- [] OS: <!--[e.g. Windows 10, macOS 10.15.4]-->
- [] Node version: <!--[e.g. 16.14.0]-->
- [] Node version: <!--[e.g. 18.16.1]-->
- [] Browser:
<!--[e.g. Chrome 83.0.4103.116, Firefox 77.0.1, Safari 13.1.1]-->
+65
View File
@@ -0,0 +1,65 @@
name: Playwright Tests
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
playwright-tests:
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5]
shardTotal: [5]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run:
export NODE_OPTIONS="--max_old_space_size=8192" && npx playwright test --shard=${{
matrix.shardIndex }}/${{ matrix.shardTotal }}
- name: Upload blob report to GitHub Actions Artifacts
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: blob-report-${{ matrix.shardIndex }}
path: blob-report
retention-days: 1
merge-reports:
if: ${{ !cancelled() }}
needs: [playwright-tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Download blob reports from GitHub Actions Artifacts
uses: actions/download-artifact@v4
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true
- name: Merge into HTML Report
run: npx playwright merge-reports --reporter html ./all-blob-reports
- name: Upload HTML report
uses: actions/upload-artifact@v4
with:
name: html-report--attempt-${{ github.run_attempt }}
path: playwright-report
retention-days: 14
+15 -2
View File
@@ -10,6 +10,8 @@ junit.xml
coverage/
.docz/
.yarn/
.nx/
addOns/yarn.lock
# YALC (for Erik)
.yalc
@@ -24,6 +26,7 @@ yarn-error.log
.DS_Store
.env
*.code-workspace
.directory
# Common Example Data Directories
sampledata/
@@ -32,7 +35,7 @@ docker/dcm4che/dcm4che-arc
# Cypress test results
videos/
screenshots/
# Locize settings
.locize
@@ -40,6 +43,16 @@ screenshots/
# autogenerated files
platform/app/src/pluginImports.js
/Viewers.iml
platform/app/.recipes/Nginx-Dcm4Che/dcm4che/dcm4che-arc/*
platform/app/.recipes/Nginx-Dcm4Chee/logs/*
platform/app/.recipes/OpenResty-Orthanc/logs/*
.vercel
.vs
# PlayWright
node_modules/
tests/test-results/
tests/playwright-report/
/blob-report/
/playwright/.cache/
+1 -1
View File
@@ -1 +1 @@
16.14.0
18.16.1
+273
View File
@@ -0,0 +1,273 @@
/*
* This script uses nodejs to generate a JSON file from a DICOM study folder.
* You need to have dcmjs installed in your project.
* The JSON file can be used to load the study into the OHIF Viewer. You can get more detail
* in the DICOM JSON Data source on docs.ohif.org
*
* Usage: node dicom-json-generator.js <studyFolder> <urlPrefix> <outputJSONPath> <optional scheme>
*
* params:
* - studyFolder: path to the study folder which contains the DICOM files
* - urlPrefix: prefix to the url that will be used to load the study into the viewer. For instance
* we use https://ohif-assets.s3.us-east-2.amazonaws.com/dicom-json/data as the urlPrefix for the
* example since the data is hosted on S3 and each study is in a folder. So the url in the generated
* json file for the first instance of the first series of the first study will be
* dicomweb:https://ohif-assets.s3.us-east-2.amazonaws.com/dicom-json/data/Series1/Instance1
*
* as you see the dicomweb is a prefix that is used to load the data into the viewer, which is suited when
* the .dcm file is hosted statically and can be accessed via a URL (like our example above)
* However, you can specify a new scheme bellow.
*
* - outputJSONPath: path to the output JSON file
* - scheme: default dicomweb if not provided
*/
const dcmjs = require('dcmjs');
const path = require('path');
const fs = require('fs').promises;
const args = process.argv.slice(2);
const [studyDirectory, urlPrefix, outputPath, scheme = 'dicomweb'] = args;
if (args.length < 3 || args.length > 4) {
console.error(
'Usage: node dicom-json-generator.js <studyFolder> <urlPrefix> <outputJSONPath> [scheme]'
);
process.exit(1);
}
const model = {
studies: [],
};
async function convertDICOMToJSON(studyDirectory, urlPrefix, outputPath, scheme) {
try {
const files = await recursiveReadDir(studyDirectory);
console.debug('Processing...');
for (const file of files) {
if (!file.includes('.DS_Store') && !file.includes('.xml')) {
const arrayBuffer = await fs.readFile(file);
const dicomDict = dcmjs.data.DicomMessage.readFile(arrayBuffer.buffer);
const instance = dcmjs.data.DicomMetaDictionary.naturalizeDataset(dicomDict.dict);
instance.fileLocation = createImageId(file, urlPrefix, studyDirectory, scheme);
processInstance(instance);
}
}
console.log('Successfully loaded data');
model.studies.forEach(study => {
study.NumInstances = findInstancesNumber(study);
study.Modalities = findModalities(study).join('/');
});
await fs.writeFile(outputPath, JSON.stringify(model, null, 2));
console.log('JSON saved');
} catch (error) {
console.error(error);
}
}
async function recursiveReadDir(dir) {
let results = [];
const list = await fs.readdir(dir);
for (const file of list) {
const filePath = path.resolve(dir, file);
const stat = await fs.stat(filePath);
if (stat.isDirectory()) {
const res = await recursiveReadDir(filePath);
results = results.concat(res);
} else {
results.push(filePath);
}
}
return results;
}
function createImageId(fileLocation, urlPrefix, studyDirectory, scheme) {
const relativePath = path.relative(studyDirectory, fileLocation);
const normalizedPath = path.normalize(relativePath).replace(/\\/g, '/');
return `${scheme}:${urlPrefix}${normalizedPath}`;
}
function processInstance(instance) {
const { StudyInstanceUID, SeriesInstanceUID } = instance;
let study = getStudy(StudyInstanceUID);
if (!study) {
study = createStudyMetadata(StudyInstanceUID, instance);
model.studies.push(study);
}
let series = getSeries(StudyInstanceUID, SeriesInstanceUID);
if (!series) {
series = createSeriesMetadata(instance);
study.series.push(series);
}
const instanceMetaData =
instance.NumberOfFrames > 1
? createInstanceMetaDataMultiFrame(instance)
: createInstanceMetaData(instance);
series.instances.push(...[].concat(instanceMetaData));
}
function getStudy(StudyInstanceUID) {
return model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID);
}
function getSeries(StudyInstanceUID, SeriesInstanceUID) {
const study = getStudy(StudyInstanceUID);
return study
? study.series.find(series => series.SeriesInstanceUID === SeriesInstanceUID)
: undefined;
}
const findInstancesNumber = study => {
let numInstances = 0;
study.series.forEach(aSeries => {
numInstances = numInstances + aSeries.instances.length;
});
return numInstances;
};
const findModalities = study => {
let modalities = new Set();
study.series.forEach(aSeries => {
modalities.add(aSeries.Modality);
});
return Array.from(modalities);
};
function createStudyMetadata(StudyInstanceUID, instance) {
return {
StudyInstanceUID,
StudyDescription: instance.StudyDescription,
StudyDate: instance.StudyDate,
StudyTime: instance.StudyTime,
PatientName: instance.PatientName,
PatientID: instance.PatientID || '1234', // this is critical to have
AccessionNumber: instance.AccessionNumber,
PatientAge: instance.PatientAge,
PatientSex: instance.PatientSex,
PatientWeight: instance.PatientWeight,
series: [],
};
}
function createSeriesMetadata(instance) {
return {
SeriesInstanceUID: instance.SeriesInstanceUID,
SeriesDescription: instance.SeriesDescription,
SeriesNumber: instance.SeriesNumber,
SeriesTime: instance.SeriesTime,
Modality: instance.Modality,
SliceThickness: instance.SliceThickness,
instances: [],
};
}
function commonMetaData(instance) {
return {
Columns: instance.Columns,
Rows: instance.Rows,
InstanceNumber: instance.InstanceNumber,
SOPClassUID: instance.SOPClassUID,
AcquisitionNumber: instance.AcquisitionNumber,
PhotometricInterpretation: instance.PhotometricInterpretation,
BitsAllocated: instance.BitsAllocated,
BitsStored: instance.BitsStored,
PixelRepresentation: instance.PixelRepresentation,
SamplesPerPixel: instance.SamplesPerPixel,
PixelSpacing: instance.PixelSpacing,
HighBit: instance.HighBit,
ImageOrientationPatient: instance.ImageOrientationPatient,
ImagePositionPatient: instance.ImagePositionPatient,
FrameOfReferenceUID: instance.FrameOfReferenceUID,
ImageType: instance.ImageType,
Modality: instance.Modality,
SOPInstanceUID: instance.SOPInstanceUID,
SeriesInstanceUID: instance.SeriesInstanceUID,
StudyInstanceUID: instance.StudyInstanceUID,
WindowCenter: instance.WindowCenter,
WindowWidth: instance.WindowWidth,
RescaleIntercept: instance.RescaleIntercept,
RescaleSlope: instance.RescaleSlope,
};
}
function conditionalMetaData(instance) {
return {
...(instance.ConceptNameCodeSequence && {
ConceptNameCodeSequence: instance.ConceptNameCodeSequence,
}),
...(instance.SeriesDate && { SeriesDate: instance.SeriesDate }),
...(instance.ReferencedSeriesSequence && {
ReferencedSeriesSequence: instance.ReferencedSeriesSequence,
}),
...(instance.SharedFunctionalGroupsSequence && {
SharedFunctionalGroupsSequence: instance.SharedFunctionalGroupsSequence,
}),
...(instance.PerFrameFunctionalGroupsSequence && {
PerFrameFunctionalGroupsSequence: instance.PerFrameFunctionalGroupsSequence,
}),
...(instance.ContentSequence && { ContentSequence: instance.ContentSequence }),
...(instance.ContentTemplateSequence && {
ContentTemplateSequence: instance.ContentTemplateSequence,
}),
...(instance.CurrentRequestedProcedureEvidenceSequence && {
CurrentRequestedProcedureEvidenceSequence: instance.CurrentRequestedProcedureEvidenceSequence,
}),
...(instance.CodingSchemeIdentificationSequence && {
CodingSchemeIdentificationSequence: instance.CodingSchemeIdentificationSequence,
}),
...(instance.RadiopharmaceuticalInformationSequence && {
RadiopharmaceuticalInformationSequence: instance.RadiopharmaceuticalInformationSequence,
}),
...(instance.ROIContourSequence && {
ROIContourSequence: instance.ROIContourSequence,
}),
...(instance.StructureSetROISequence && {
StructureSetROISequence: instance.StructureSetROISequence,
}),
...(instance.ReferencedFrameOfReferenceSequence && {
ReferencedFrameOfReferenceSequence: instance.ReferencedFrameOfReferenceSequence,
}),
...(instance.CorrectedImage && { CorrectedImage: instance.CorrectedImage }),
...(instance.Units && { Units: instance.Units }),
...(instance.DecayCorrection && { DecayCorrection: instance.DecayCorrection }),
...(instance.AcquisitionDate && { AcquisitionDate: instance.AcquisitionDate }),
...(instance.AcquisitionTime && { AcquisitionTime: instance.AcquisitionTime }),
...(instance.PatientWeight && { PatientWeight: instance.PatientWeight }),
...(instance.NumberOfFrames && { NumberOfFrames: instance.NumberOfFrames }),
...(instance.FrameTime && { FrameTime: instance.FrameTime }),
...(instance.EncapsulatedDocument && { EncapsulatedDocument: instance.EncapsulatedDocument }),
...(instance.SequenceOfUltrasoundRegions && {
SequenceOfUltrasoundRegions: instance.SequenceOfUltrasoundRegions,
}),
};
}
function createInstanceMetaData(instance) {
const metadata = {
...commonMetaData(instance),
...conditionalMetaData(instance),
};
return { metadata, url: instance.fileLocation };
}
function createInstanceMetaDataMultiFrame(instance) {
const instances = [];
const commonData = commonMetaData(instance);
const conditionalData = conditionalMetaData(instance);
for (let i = 1; i <= instance.NumberOfFrames; i++) {
const metadata = { ...commonData, ...conditionalData };
const result = { metadata, url: instance.fileLocation + `?frame=${i}` };
instances.push(result);
}
return instances;
}
convertDICOMToJSON(studyDirectory, urlPrefix, outputPath, scheme);
+1
View File
@@ -1,6 +1,7 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"streetsidesoftware.code-spell-checker",
"dbaeumer.vscode-eslint",
"mikestead.dotenv",
"bungcip.better-toml",
+91 -13
View File
@@ -1,4 +1,5 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.rulers": [80, 120],
// ===
// Spacing
@@ -14,23 +15,100 @@
// Event Triggers
// ===
"editor.formatOnSave": true,
"eslint.autoFixOnSave": true,
"eslint.run": "onSave",
"eslint.validate": [
{
"language": "javascript",
"autoFix": true
},
{
"language": "javascriptreact",
"autoFix": true
}
],
"jest.autoRun": "off",
"prettier.disableLanguages": ["html"],
"prettier.endOfLine": "lf",
"workbench.colorCustomizations": {},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
"source.fixAll.eslint": "explicit"
},
"cSpell.userWords": [
"aabb",
"architectured",
"attrname",
"Barksy",
"browserslist",
"bulkdata",
"Cacheable",
"cfun",
"clonedeep",
"Colormap",
"Colormaps",
"Comlink",
"cornerstonejs",
"Crosshairs",
"datasource",
"dcmjs",
"decache",
"decached",
"decaching",
"deepmerge",
"Dicom",
"dicomweb",
"DISPLAYSETS",
"glwindow",
"grababble",
"grabbable",
"Hounsfield",
"Interactable",
"Interactor",
"istyle",
"kitware",
"labelmap",
"labelmaps",
"livewire",
"Mergeable",
"multiframe",
"nifti",
"ofun",
"OHIF",
"polylines",
"POLYSEG",
"prapogation",
"precisionmetrics",
"prefetch",
"Prescaled",
"pydicom",
"Radiopharmaceutical",
"rasterizing",
"reconstructable",
"Rehydratable",
"renderable",
"resampler",
"resemblejs",
"reslice",
"resliced",
"Reslices",
"roadmap",
"ROADMAPS",
"Segmentations",
"semibold",
"sitk",
"SUBRESOLUTION",
"suvbsa",
"suvbw",
"suvlbm",
"textbox",
"thresholded",
"thresholding",
"timepoint",
"timepoints",
"TMTV",
"TOOLGROUP",
"tqdm",
"transferables",
"typedoc",
"unsubscriptions",
"uuidv",
"viewplane",
"viewports",
"Voxel",
"Voxels",
"Vtkjs",
"wado",
"wadors",
"wadouri",
"workerpool"
]
}
+10
View File
@@ -0,0 +1,10 @@
const stylusToJavaScript = {
test: /\.styl$/,
use: [
{ loader: 'style-loader' }, // 3. Style nodes from JS Strings
{ loader: 'css-loader' }, // 2. CSS to CommonJS
{ loader: 'stylus-loader' }, // 1. Stylus to CSS
],
};
module.exports = stylusToJavaScript;
+85 -23
View File
@@ -7,17 +7,20 @@ const fs = require('fs');
const webpack = require('webpack');
// ~~ PLUGINS
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const TerserJSPlugin = require('terser-webpack-plugin');
// ~~ PackageJSON
// const vtkRules = require('vtk.js/Utilities/config/dependency.js').webpack.core
// .rules;
// ~~ RULES
const loadShadersRule = require('./rules/loadShaders.js');
// const loadShadersRule = require('./rules/loadShaders.js');
const loadWebWorkersRule = require('./rules/loadWebWorkers.js');
const transpileJavaScriptRule = require('./rules/transpileJavaScript.js');
const cssToJavaScript = require('./rules/cssToJavaScript.js');
// Only uncomment for old v2 stylus
// const stylusToJavaScript = require('./rules/stylusToJavaScript.js');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
// ~~ ENV VARS
const NODE_ENV = process.env.NODE_ENV;
@@ -32,6 +35,27 @@ const COMMIT_HASH = fs.readFileSync(path.join(__dirname, '../commit.txt'), 'utf8
//
dotenv.config();
const defineValues = {
/* Application */
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.NODE_DEBUG': JSON.stringify(process.env.NODE_DEBUG),
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
'process.env.VERSION_NUMBER': JSON.stringify(VERSION_NUMBER),
'process.env.COMMIT_HASH': JSON.stringify(COMMIT_HASH),
/* i18n */
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
'process.env.LOCIZE_PROJECTID': JSON.stringify(process.env.LOCIZE_PROJECTID || ''),
'process.env.LOCIZE_API_KEY': JSON.stringify(process.env.LOCIZE_API_KEY || ''),
'process.env.REACT_APP_I18N_DEBUG': JSON.stringify(process.env.REACT_APP_I18N_DEBUG || ''),
};
// Only redefine updated values. This avoids warning messages in the logs
if (!process.env.APP_CONFIG) {
defineValues['process.env.APP_CONFIG'] = '';
}
module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
if (!process.env.NODE_ENV) {
throw new Error('process.env.NODE_ENV not set');
@@ -74,12 +98,51 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
type: 'filesystem',
},
module: {
noParse: [/(codec)/, /(dicomicc)/],
noParse: [/(dicomicc)/],
rules: [
...(isProdBuild
? []
: [
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
plugins: ['react-refresh/babel'],
},
},
]),
{
test: /\.js$/,
enforce: 'pre',
use: 'source-map-loader',
test: /\.svg?$/,
oneOf: [
{
use: [
{
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
},
},
},
],
},
prettier: false,
svgo: true,
titleProp: true,
},
},
],
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
},
],
},
transpileJavaScriptRule(mode),
loadWebWorkersRule,
@@ -91,10 +154,24 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
},
},
cssToJavaScript,
// Note: Only uncomment the following if you are using the old style of stylus in v2
// Also you need to uncomment this platform/app/.webpack/rules/extractStyleChunks.js
// stylusToJavaScript,
{
test: /\.wasm/,
type: 'asset/resource',
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/images/[name].[ext]',
},
},
],
},
], //.concat(vtkRules),
},
resolve: {
@@ -108,8 +185,6 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
'@state': path.resolve(__dirname, '../platform/app/src/state'),
'dicom-microscopy-viewer':
'dicom-microscopy-viewer/dist/dynamic-import/dicomMicroscopyViewer.min.js',
'@cornerstonejs/dicom-image-loader':
'@cornerstonejs/dicom-image-loader/dist/dynamic-import/cornerstoneDICOMImageLoader.min.js',
},
// Which directories to search when resolving modules
modules: [
@@ -133,24 +208,11 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
},
},
plugins: [
new webpack.DefinePlugin({
/* Application */
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
'process.env.APP_CONFIG': JSON.stringify(process.env.APP_CONFIG || ''),
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
'process.env.VERSION_NUMBER': JSON.stringify(VERSION_NUMBER),
'process.env.COMMIT_HASH': JSON.stringify(COMMIT_HASH),
/* i18n */
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
'process.env.LOCIZE_PROJECTID': JSON.stringify(process.env.LOCIZE_PROJECTID || ''),
'process.env.LOCIZE_API_KEY': JSON.stringify(process.env.LOCIZE_API_KEY || ''),
'process.env.REACT_APP_I18N_DEBUG': JSON.stringify(process.env.REACT_APP_I18N_DEBUG || ''),
}),
new webpack.DefinePlugin(defineValues),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
...(isProdBuild ? [] : [new ReactRefreshWebpackPlugin({ overlay: false })]),
// Uncomment to generate bundle analyzer
// new BundleAnalyzerPlugin(),
],
+2622
View File
File diff suppressed because it is too large. Load diff
+148
View File
@@ -10,6 +10,7 @@ Please find below the list of datasets used on the demo page, along with their r
## Platforms
### NIH NCI IDC
- Fedorov, A., Longabaugh, W.J., Pot, D., Clunie, D.A., Pieper, S., Aerts, H.J., Homeyer, A., Lewis, R., Akbarzadeh, A., Bontempi, D. and Clifford, W., 2021. NCI imaging data commons. Cancer research, 81(16), p.4188.
@@ -147,3 +148,150 @@ Stony Brook University COVID-19 Positive Cases (COVID-19-NY-SBU)
### 2.16.840.1.114362.1.11972228.22789312658.616067305.306.2
https://data.kitware.com/
### 1.2.276.0.7230010.3.1.2.296485376.1.1665793212.499772
### 2.25.269859997690759739055099378767846712697
### 1.3.6.1.4.1.14519.5.2.1.5099.8010.217836670708542506360829799868
### 1.3.6.1.4.1.14519.5.2.1.4792.2001.232252967813565730694525674696
### 1.3.6.1.4.1.14519.5.2.1.4792.2001.105216574054253895819671475627
### 1.3.6.1.4.1.5962.99.1.1117.5035.1620319789811.1.2.1
### 1.3.6.1.4.1.5962.99.1.1123.9231.1620326176300.1.2.1
### 1.3.6.1.4.1.5962.99.1.1126.3483.1620329455972.1.2.1
https://github.com/ImagingInformatics/hackathon-images
### 2.16.124.113543.6004.101.103.20021117.162333.1
### 2.16.124.113543.6004.101.103.20021117.190619.1
### 2.16.124.113543.6004.101.103.20021117.123455.1
### 2.16.124.113543.6004.101.103.20021117.061159.1
https://www.aapm.org/
### 1.2.840.113619.2.30.1.1762295590.1623.978668949.886
### 1.2.276.0.7230010.3.1.2.447481088.1.1669202398.851612
Custom data SPECT, specifically I123-FP-CIT (DaTSCAN) SPECT, evaluates the dopaminergic system to diagnose Parkinson's disease, especially when tremor symptoms are unclear. It helps distinguish Parkinson's disease from treatment-related tremor.
### 1.3.6.1.4.1.9328.50.1.54652
https://www.cancerimagingarchive.net/collection/rider-pilot/
Lung Image Database Consortium (LIDC). (2023) RIDER Pilot [Data set]. The Cancer Imaging Archive (TCIA). https://doi.org/10.7937/m87f-mz83
### 1.3.6.1.4.1.14519.5.2.1.331759366792756327296606233801322964986
Mayr, N., Yuh, W. T. C., Bowen, S., Harkenrider, M., Knopp, M. V., Lee, E. Y.-P., Leung, E., Lo, S. S., Small Jr., W., & Wolfson, A. H. (2023). Cervical Cancer – Tumor Heterogeneity: Serial Functional and Molecular Imaging Across the Radiation Therapy Course in Advanced Cervical Cancer (Version 1) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/ERZ5-QZ59
https://www.cancerimagingarchive.net/collection/cc-tumor-heterogeneity/
### 1.3.6.1.4.1.14519.5.2.1.297577087050970310787702792940607009472
Eslick, E. M., Kipritidis, J., Gradinscak, D., Stevens, M. J., Bailey, D. L., Harris, B., Booth, J. T., & Keall, P. J. (2022). CT Ventilation as a functional imaging modality for lung cancer radiotherapy (CT-vs-PET-Ventilation-Imaging) (Version 1) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/3ppx-7s22
https://www.cancerimagingarchive.net/collection/ct-vs-pet-ventilation-imaging/
### 1.3.6.1.4.1.14519.5.2.1.2103.7010.634114621738943599785009586807
### 1.3.6.1.4.1.14519.5.2.1.2103.7010.135953723682765205394176991681
Huang, W., Tudorica, A., Chui, S., Kemmer, K., Naik, A., Troxell, M., Oh, K., Roy, N., Afzal, A., & Holtorf, M. (2014). Variations of dynamic contrast-enhanced magnetic resonance imaging in evaluation of breast cancer therapy response: a multicenter data analysis challenge (QIN Breast DCE-MRI) (Version 2) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/k9/tcia.2014.a2n1ixox
https://www.cancerimagingarchive.net/collection/qin-breast-dce-mri/
### 1.3.6.1.4.1.14519.5.2.1.1.24766180081901755714059656629507905556
Cancer Moonshot Biobank. (2023). Cancer Moonshoot Biobank – Acute Myeloid Leukemia (CMB-AML) (Version 4) [Dataset]. The Cancer Imaging Archive. https://doi.org/10.7937/PCTE-6M66
https://www.cancerimagingarchive.net/collection/cmb-aml/
### 1.3.6.1.4.1.14519.5.2.1.3098.5025.285242291560760827564488897577
https://www.cancerimagingarchive.net/collection/anti-pd-1_lung/
Madhavi, P., Patel, S., & Tsao, A. S. (2019). Data from Anti-PD-1 Immunotherapy Lung [Data set]. The Cancer Imaging Archive. DOI: 10.7937/tcia.2019.zjjwb9ip
### 1.3.6.1.4.1.14519.5.2.1.1.84416332615988066829602832830236187384
https://www.cancerimagingarchive.net/collection/cmb-pca/
Cancer Moonshot Biobank. (2022). Cancer Moonshot Biobank – Prostate Cancer Collection (CMB-PCA) (Version 7) [Dataset]. The Cancer Imaging Archive. https://doi.org/10.7937/25T7-6Y12
### 1.3.6.1.4.1.32722.99.99.239341353911714368772597187099978969331
Aerts, H. J. W. L., Wee, L., Rios Velazquez, E., Leijenaar, R. T. H., Parmar, C., Grossmann, P., Carvalho, S., Bussink, J., Monshouwer, R., Haibe-Kains, B., Rietveld, D., Hoebers, F., Rietbergen, M. M., Leemans, C. R., Dekker, A., Quackenbush, J., Gillies, R. J., Lambin, P. (2014). Data From NSCLC-Radiomics (version 4) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2015.PF0M9REI
https://www.cancerimagingarchive.net/collection/nsclc-radiomics/
### 1.3.6.1.4.1.14519.5.2.1.7085.2626.494695569589117268722281491772
https://www.cancerimagingarchive.net/collection/cptac-ucec/
National Cancer Institute Clinical Proteomic Tumor Analysis Consortium (CPTAC). (2019). The Clinical Proteomic Tumor Analysis Consortium Uterine Corpus Endometrial Carcinoma Collection (CPTAC-UCEC) (Version 12) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2018.3R3JUISW
### 1.3.6.1.4.1.14519.5.2.1.207544490797667703011829289839681390478
https://www.cancerimagingarchive.net/collection/remind/
Juvekar, P., Dorent, R., Kögl, F., Torio, E., Barr, C., Rigolo, L., Galvin, C., Jowkar, N., Kazi, A., Haouchine, N., Cheema, H., Navab, N., Pieper, S., Wells, W. M., Bi, W. L., Golby, A., Frisken, S., & Kapur, T. (2023). The Brain Resection Multimodal Imaging Database (ReMIND) (Version 1) [dataset]. The Cancer Imaging Archive. https://doi.org/10.7937/3RAG-D070
### 1.3.12.2.1107.5.1.4.60175.30000008042114404745300000010
Gavrielides, M. A., Kinnard, L. M., Myers, K. J., Peregoy, J., Pritchard, W. F., Zeng, R., Esparza, J., Karanian, J., & Petrick, N. (2015). Data From Phantom FDA [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/k9/TCIA.2015.orbjkmux
https://www.cancerimagingarchive.net/collection/phantom-fda/
### 1.3.6.1.4.1.14519.5.2.1.6834.5010.992793141464713669479982159310
https://www.cancerimagingarchive.net/collection/4d-lung/
Hugo, G. D., Weiss, E., Sleeman, W. C., Balik, S., Keall, P. J., Lu, J., & Williamson, J. F. (2016). Data from 4D Lung Imaging of NSCLC Patients (Version 2) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2016.ELN8YGLE
### 1.3.6.1.4.1.9328.50.17.15423521354819720574322014551955370036
https://www.cancerimagingarchive.net/collection/rider-lung-pet-ct/
Muzi P, Wanner M, & Kinahan P. (2015). Data From RIDER Lung PET-CT. The Cancer Imaging Archive. https://doi.org/10.7937/k9/tcia.2015.ofip7tvm
### 1.3.6.1.4.1.14519.5.2.1.9823.1001.134394060407147891170882809392
https://www.cancerimagingarchive.net/collection/prostate-mri/
Choyke P, Turkbey B, Pinto P, Merino M, Wood B. (2016). Data From PROSTATE-MRI. The Cancer Imaging Archive. http://doi.org/10.7937/K9/TCIA.2016.6046GUDv
### 1.3.6.1.4.1.14519.5.2.1.191696062987463500085282581898315738844
https://www.cancerimagingarchive.net/collection/upenn-gbm/
Bakas, S., Sako, C., Akbari, H., Bilello, M., Sotiras, A., Shukla, G., Rudie, J. D., Flores Santamaria, N., Fathi Kazerooni, A., Pati, S., Rathore, S., Mamourian, E., Ha, S. M., Parker, W., Doshi, J., Baid, U., Bergman, M., Binder, Z. A., Verma, R., … Davatzikos, C. (2021). Multi-parametric magnetic resonance imaging (mpMRI) scans for de novo Glioblastoma (GBM) patients from the University of Pennsylvania Health System (UPENN-GBM) (Version 2) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.709X-DN49
### 1.3.6.1.4.1.14519.5.2.1.4792.2001.921758700577562664959693695481
https://www.cancerimagingarchive.net/collection/breast-diagnosis/
Bloch, B. Nicolas, Jain, Ashali, & Jaffe, C. Carl. (2015). BREAST-DIAGNOSIS [Data set]. The Cancer Imaging Archive. http://doi.org/10.7937/K9/TCIA.2015.SDNRQXXR
### 1.3.6.1.4.1.14519.5.2.1.1620.1225.189514895974227080410265976065
Comstock, C. E., Gatsonis, C., Newstead, G. M., Snyder, B. S., Gareen, I. F., Bergin, J. T., Rahbar, H., Sung, J. S., Jacobs, C., Harvey, J. A., Nicholson, M. H., Ward, R. C., Holt, J., Prather, A., Miller, K. D., Schnall, M. D., & Kuhl, C. K. (2023). Abbreviated Breast MRI and Digital Tomosynthesis Mammography in Screening Women With Dense Breasts (EA1141) (Version 1) [dataset]. The Cancer Imaging Archive. https://doi.org/10.7937/2BAS-HR33
https://www.cancerimagingarchive.net/collection/ea1141/
### 1.2.276.0.7230010.3.1.2.2155604110.4180.1021041295.21
From OFFIS DICOM-Team
https://www.offis.de/
OFFIS DICOM-Team
+2 -2
View File
@@ -21,7 +21,7 @@
# Stage 1: Build the application
# docker build -t ohif/viewer:latest .
FROM node:16.15.0-slim as json-copier
FROM node:18.16.1-slim as json-copier
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
@@ -37,7 +37,7 @@ COPY platform /usr/src/app/platform
#RUN find platform \! -name "package.json" -mindepth 2 -maxdepth 2 -print | xargs rm -rf
# Copy Files
FROM node:16.15.0-slim as builder
FROM node:18.16.1-slim as builder
RUN apt-get update && apt-get install -y build-essential python3
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
+27 -26
View File
@@ -17,6 +17,10 @@ provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF
<div align="center">
📰 <a href="https://ohif.org/news/"><strong>Join OHIF Newsletter</strong></a> 📰
</div>
<div align="center">
📰 <a href="https://ohif.org/news/"><strong>Join OHIF Newsletter</strong></a> 📰
</div>
<hr />
@@ -37,17 +41,19 @@ provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF
| | | |
| :-: | :--- | :--- |
| <img src="platform/docs/docs/assets/img/demo-measurements.jpg" alt="Measurement tracking" width="350"/> | Measurement Tracking | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5) |
| <img src="platform/docs/docs/assets/img/demo-segmentation.png" alt="Segmentations" width="350"/> | Labelmap Segmentations | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.12.2.1107.5.2.32.35162.30000015050317233592200000046) |
| <img src="platform/docs/docs/assets/img/demo-ptct.png" alt="Hanging Protocols" width="350"/> | Fusion and Custom Hanging protocols | [Demo](https://viewer.ohif.org/tmtv?StudyInstanceUIDs=1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463) |
| <img src="platform/docs/docs/assets/img/demo-microscopy.png" alt="Microscopy" width="350"/> | Slide Microscopy | [Demo](https://viewer.ohif.org/microscopy?StudyInstanceUIDs=2.25.275741864483510678566144889372061815320) |
| <img src="platform/docs/docs/assets/img/demo-volumeRendering.png" alt="Volume Rendering" width="350"/> | Volume Rendering | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&hangingprotocolId=mprAnd3DVolumeViewport) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-measurements.webp?raw=true" alt="Measurement tracking" width="350"/> | Measurement Tracking | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-segmentation.webp?raw=true" alt="Segmentations" width="350"/> | Labelmap Segmentations | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.12.2.1107.5.2.32.35162.30000015050317233592200000046) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-ptct.webp?raw=true" alt="Hanging Protocols" width="350"/> | Fusion and Custom Hanging protocols | [Demo](https://viewer.ohif.org/tmtv?StudyInstanceUIDs=1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-volume-rendering.webp?raw=true" alt="Volume Rendering" width="350"/> | Volume Rendering | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&hangingprotocolId=mprAnd3DVolumeViewport) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-pdf.webp?raw=true" alt="PDF" width="350"/> | PDF | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.317377619501274872606137091638706705333) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-rtstruct.webp?raw=true" alt="RTSTRUCT" width="350"/> | RT STRUCT | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-4d.webp?raw=true" alt="4D" width="350"/> | 4D | [Demo](https://viewer.ohif.org/dynamic-volume?StudyInstanceUIDs=2.25.232704420736447710317909004159492840763) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-video.webp?raw=true" alt="VIDEO" width="350"/> | Video | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.96975534054447904995905761963464388233) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/microscopy.webp?raw=true" alt="microscopy" width="350"/> | Slide Microscopy | [Demo](https://viewer.ohif.org/microscopy?StudyInstanceUIDs=2.25.141277760791347900862109212450152067508) |
## About
The OHIF Medical Imaging Viewer is for viewing medical images. It can retrieve
The OHIF Viewer can retrieve
and load images from most sources and formats; render sets in 2D, 3D, and
reconstructed representations; allows for the manipulation, annotation, and
serialization of observations; supports internationalization, OpenID Connect,
@@ -112,19 +118,16 @@ This is typically where the latest development happens. Code that is in the mast
Each package is tagged with beta version numbers, and published to npm such as `@ohif/ui@3.6.0-beta.1`
### `release` branch - The latest stable release
### `release/*` branches - The latest stable releases
Once the `master` branch code reaches a stable, release-ready state, we conduct a comprehensive code review and QA testing. Upon approval, we create a new release branch from `master`. These branches represent the latest stable version considered ready for production.
This branch represents the latest stable version of the project that is considered ready for production. The code in this branch should be fully tested and vetted for release. Once the code in the master branch reaches a state where it's stable and ready to be released to users,
we do a comprehensive code review and QA testing. Once the code is approved,
we merge it into the release branch and tag a new release.
For example, `release/3.5` is the branch for version 3.5.0, and `release/3.6` is for version 3.6.0. After each release, we wait a few days to ensure no critical bugs. If any are found, we fix them in the release branch and create a new release with a minor version bump, e.g., 3.5.1 in the `release/3.5` branch.
Each package is tagged with version numbers, and published to npm such as `@ohif/ui@3.5.0`
Note: `master` is always ahead of `release` branch. We publish both docker builds for beta and stable releases.
Each package is tagged with version numbers and published to npm, such as `@ohif/ui@3.5.0`. Note that `master` is always ahead of the `release` branch. We publish docker builds for both beta and stable releases.
Here is a schematic representation of our development workflow:
![Alt text](platform/docs/docs/assets/img/github-readme-branches.png)
![alt text](platform/docs/docs/assets/img/github-readme-branches-Jun2024.png)
@@ -133,19 +136,17 @@ Here is a schematic representation of our development workflow:
### Requirements
- [Yarn 1.17.3+](https://yarnpkg.com/en/docs/install)
- [Node 16+](https://nodejs.org/en/)
- [Node 18+](https://nodejs.org/en/)
- Yarn Workspaces should be enabled on your machine:
- `yarn config set workspaces-experimental true`
### Getting Started
1. [Fork this repository][how-to-fork]
2. [Clone your forked repository][how-to-clone]
- `git clone https://github.com/YOUR-USERNAME/Viewers.git`
3. Navigate to the cloned project's directory
4. Add this repo as a `remote` named `upstream`
- `git remote add upstream https://github.com/OHIF/Viewers.git`
5. `yarn install` to restore dependencies and link projects
1. Clone the repository
- `git clone https://github.com/OHIF/Viewers.git`
2. Navigate to the cloned project's directory
3. `yarn install` to restore dependencies and link projects
4. `yarn dev` to start the development server
#### To Develop
@@ -165,7 +166,7 @@ These commands are available from the root directory. Each project directory
also supports a number of commands that can be found in their respective
`README.md` and `package.json` files.
| Yarn Commands | Description |
| Commands | Description |
| ---------------------------- | ------------------------------------------------------------- |
| **Develop** | |
| `dev` or `start` | Default development experience for Viewer |
@@ -330,4 +331,4 @@ MIT © [OHIF](https://github.com/OHIF)
[vtk-npm]: https://www.npmjs.com/package/@ohif/extension-vtk
<!-- prettier-ignore-end -->
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FOHIF%2FViewers.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_large)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FOHIF%2FViewers.svg?type=large&issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_large&issueType=license)
+3
View File
@@ -0,0 +1,3 @@
# External Dependencies
This module contains optional dependencies and external dependencies for including in OHIF, such as the DICOM Microscopy Viewer component.
+539
View File
@@ -0,0 +1,539 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.9.1](https://github.com/OHIF/Viewers/compare/v3.9.0...v3.9.1) (2024-11-13)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.111...v3.9.0) (2024-11-12)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.110...v3.9.0-beta.111) (2024-11-12)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.109...v3.9.0-beta.110) (2024-11-11)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.108...v3.9.0-beta.109) (2024-11-08)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.107...v3.9.0-beta.108) (2024-11-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.106...v3.9.0-beta.107) (2024-11-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.105...v3.9.0-beta.106) (2024-11-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.104...v3.9.0-beta.105) (2024-11-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.103...v3.9.0-beta.104) (2024-10-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.102...v3.9.0-beta.103) (2024-10-29)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.101...v3.9.0-beta.102) (2024-10-29)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.100...v3.9.0-beta.101) (2024-10-18)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.99...v3.9.0-beta.100) (2024-10-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.98...v3.9.0-beta.99) (2024-10-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.97...v3.9.0-beta.98) (2024-10-15)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.96...v3.9.0-beta.97) (2024-10-11)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.95...v3.9.0-beta.96) (2024-10-10)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.94...v3.9.0-beta.95) (2024-10-08)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.93...v3.9.0-beta.94) (2024-10-04)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.92...v3.9.0-beta.93) (2024-10-04)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.91...v3.9.0-beta.92) (2024-10-01)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.90...v3.9.0-beta.91) (2024-10-01)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.89...v3.9.0-beta.90) (2024-09-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.88...v3.9.0-beta.89) (2024-09-27)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.87...v3.9.0-beta.88) (2024-09-24)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.86...v3.9.0-beta.87) (2024-09-19)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.85...v3.9.0-beta.86) (2024-09-19)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.84...v3.9.0-beta.85) (2024-09-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.83...v3.9.0-beta.84) (2024-09-12)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.82...v3.9.0-beta.83) (2024-09-11)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.81...v3.9.0-beta.82) (2024-09-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.80...v3.9.0-beta.81) (2024-08-27)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.79...v3.9.0-beta.80) (2024-08-16)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.78...v3.9.0-beta.79) (2024-08-16)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.77...v3.9.0-beta.78) (2024-08-15)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.76...v3.9.0-beta.77) (2024-08-15)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.75...v3.9.0-beta.76) (2024-08-08)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.74...v3.9.0-beta.75) (2024-08-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.73...v3.9.0-beta.74) (2024-08-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.72...v3.9.0-beta.73) (2024-08-02)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.71...v3.9.0-beta.72) (2024-07-31)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.70...v3.9.0-beta.71) (2024-07-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.69...v3.9.0-beta.70) (2024-07-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.68...v3.9.0-beta.69) (2024-07-27)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.67...v3.9.0-beta.68) (2024-07-26)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.66...v3.9.0-beta.67) (2024-07-26)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.65...v3.9.0-beta.66) (2024-07-24)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.64...v3.9.0-beta.65) (2024-07-23)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.63...v3.9.0-beta.64) (2024-07-19)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.62...v3.9.0-beta.63) (2024-07-10)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.61...v3.9.0-beta.62) (2024-07-09)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.61](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.60...v3.9.0-beta.61) (2024-07-09)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.60](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.59...v3.9.0-beta.60) (2024-07-09)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.59](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.58...v3.9.0-beta.59) (2024-07-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.58](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.57...v3.9.0-beta.58) (2024-07-04)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.57](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.56...v3.9.0-beta.57) (2024-07-02)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.56](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.55...v3.9.0-beta.56) (2024-07-02)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.55](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.54...v3.9.0-beta.55) (2024-06-28)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.54](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.53...v3.9.0-beta.54) (2024-06-28)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.53](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.52...v3.9.0-beta.53) (2024-06-28)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.52](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.51...v3.9.0-beta.52) (2024-06-27)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.51](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.50...v3.9.0-beta.51) (2024-06-27)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.50](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.49...v3.9.0-beta.50) (2024-06-26)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.49](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.48...v3.9.0-beta.49) (2024-06-26)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.48](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.47...v3.9.0-beta.48) (2024-06-25)
**Note:** Version bump only for package @externals/devDependencies
# [3.9.0-beta.47](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.46...v3.9.0-beta.47) (2024-06-21)
### Bug Fixes
* Allow the mode setup/creation to be async, and provide a few more values to extension/app config/mode setup. ([#4016](https://github.com/OHIF/Viewers/issues/4016)) ([88575c6](https://github.com/OHIF/Viewers/commit/88575c6c09fd778a31b2f91524163ce65d1639dd))
+94
View File
@@ -0,0 +1,94 @@
{
"name": "@externals/devDependencies",
"description": "External dev dependencies - put dev build dependencies here",
"version": "3.9.1",
"license": "MIT",
"private": true,
"engines": {
"node": ">=12",
"yarn": ">=1.19.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@kitware/vtk.js": "32.1.0",
"clsx": "^2.1.1",
"core-js": "^3.2.1",
"moment": "^2.9.4"
},
"peerDependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
"@svgr/webpack": "^8.1.0",
"@types/jest": "^27.5.0",
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"autoprefixer": "^10.4.4",
"babel-eslint": "9.x",
"babel-loader": "^8.2.4",
"babel-plugin-module-resolver": "^5.0.0",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^9.0.1",
"cross-env": "^5.2.0",
"css-loader": "^6.8.1",
"dotenv": "^8.1.0",
"eslint": "^8.39.0",
"eslint-config-prettier": "^7.2.0",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-flowtype": "^7.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-promise": "^5.2.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.4.0",
"eslint-plugin-tsdoc": "^0.2.11",
"eslint-webpack-plugin": "^2.5.3",
"execa": "^8.0.1",
"extract-css-chunks-webpack-plugin": "^4.5.4",
"html-webpack-plugin": "^5.3.2",
"husky": "^3.0.0",
"jest": "^29.5.0",
"jest-canvas-mock": "^2.1.0",
"jest-environment-jsdom": "^29.5.0",
"jest-junit": "^6.4.0",
"lerna": "^7.2.0",
"lint-staged": "^9.0.2",
"mini-css-extract-plugin": "^2.1.0",
"optimize-css-assets-webpack-plugin": "^6.0.1",
"postcss": "^8.3.5",
"postcss-import": "^14.0.2",
"postcss-loader": "^6.1.1",
"postcss-preset-env": "^7.4.3",
"prettier": "^3.0.3",
"prettier-plugin-tailwindcss": "^0.5.4",
"react-refresh": "^0.14.2",
"semver": "^7.5.1",
"serve": "^14.2.0",
"shader-loader": "^1.3.1",
"shx": "^0.3.3",
"source-map-loader": "^4.0.1",
"start-server-and-test": "^1.10.0",
"style-loader": "^1.0.0",
"stylus": "^0.59.0",
"stylus-loader": "^7.1.3",
"terser-webpack-plugin": "^5.1.4",
"typescript": "5.5.4",
"unused-webpack-plugin": "2.4.0",
"webpack": "5.94.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "4.7.3",
"webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^5.7.3",
"workbox-webpack-plugin": "^6.1.5",
"worker-loader": "^3.0.8"
},
"scripts": {
"build": "Included as direct dependency"
}
}
+539
View File
@@ -0,0 +1,539 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.9.1](https://github.com/OHIF/Viewers/compare/v3.9.0...v3.9.1) (2024-11-13)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.111...v3.9.0) (2024-11-12)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.110...v3.9.0-beta.111) (2024-11-12)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.109...v3.9.0-beta.110) (2024-11-11)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.108...v3.9.0-beta.109) (2024-11-08)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.107...v3.9.0-beta.108) (2024-11-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.106...v3.9.0-beta.107) (2024-11-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.105...v3.9.0-beta.106) (2024-11-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.104...v3.9.0-beta.105) (2024-11-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.103...v3.9.0-beta.104) (2024-10-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.102...v3.9.0-beta.103) (2024-10-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.101...v3.9.0-beta.102) (2024-10-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.100...v3.9.0-beta.101) (2024-10-18)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.99...v3.9.0-beta.100) (2024-10-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.98...v3.9.0-beta.99) (2024-10-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.97...v3.9.0-beta.98) (2024-10-15)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.96...v3.9.0-beta.97) (2024-10-11)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.95...v3.9.0-beta.96) (2024-10-10)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.94...v3.9.0-beta.95) (2024-10-08)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.93...v3.9.0-beta.94) (2024-10-04)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.92...v3.9.0-beta.93) (2024-10-04)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.91...v3.9.0-beta.92) (2024-10-01)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.90...v3.9.0-beta.91) (2024-10-01)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.89...v3.9.0-beta.90) (2024-09-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.88...v3.9.0-beta.89) (2024-09-27)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.87...v3.9.0-beta.88) (2024-09-24)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.86...v3.9.0-beta.87) (2024-09-19)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.85...v3.9.0-beta.86) (2024-09-19)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.84...v3.9.0-beta.85) (2024-09-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.83...v3.9.0-beta.84) (2024-09-12)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.82...v3.9.0-beta.83) (2024-09-11)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.81...v3.9.0-beta.82) (2024-09-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.80...v3.9.0-beta.81) (2024-08-27)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.79...v3.9.0-beta.80) (2024-08-16)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.78...v3.9.0-beta.79) (2024-08-16)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.77...v3.9.0-beta.78) (2024-08-15)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.76...v3.9.0-beta.77) (2024-08-15)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.75...v3.9.0-beta.76) (2024-08-08)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.74...v3.9.0-beta.75) (2024-08-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.73...v3.9.0-beta.74) (2024-08-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.72...v3.9.0-beta.73) (2024-08-02)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.71...v3.9.0-beta.72) (2024-07-31)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.70...v3.9.0-beta.71) (2024-07-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.69...v3.9.0-beta.70) (2024-07-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.68...v3.9.0-beta.69) (2024-07-27)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.67...v3.9.0-beta.68) (2024-07-26)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.66...v3.9.0-beta.67) (2024-07-26)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.65...v3.9.0-beta.66) (2024-07-24)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.64...v3.9.0-beta.65) (2024-07-23)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.63...v3.9.0-beta.64) (2024-07-19)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.62...v3.9.0-beta.63) (2024-07-10)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.61...v3.9.0-beta.62) (2024-07-09)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.61](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.60...v3.9.0-beta.61) (2024-07-09)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.60](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.59...v3.9.0-beta.60) (2024-07-09)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.59](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.58...v3.9.0-beta.59) (2024-07-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.58](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.57...v3.9.0-beta.58) (2024-07-04)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.57](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.56...v3.9.0-beta.57) (2024-07-02)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.56](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.55...v3.9.0-beta.56) (2024-07-02)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.55](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.54...v3.9.0-beta.55) (2024-06-28)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.54](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.53...v3.9.0-beta.54) (2024-06-28)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.53](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.52...v3.9.0-beta.53) (2024-06-28)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.52](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.51...v3.9.0-beta.52) (2024-06-27)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.51](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.50...v3.9.0-beta.51) (2024-06-27)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.50](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.49...v3.9.0-beta.50) (2024-06-26)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.49](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.48...v3.9.0-beta.49) (2024-06-26)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.48](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.47...v3.9.0-beta.48) (2024-06-25)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.9.0-beta.47](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.46...v3.9.0-beta.47) (2024-06-21)
### Bug Fixes
* Allow the mode setup/creation to be async, and provide a few more values to extension/app config/mode setup. ([#4016](https://github.com/OHIF/Viewers/issues/4016)) ([88575c6](https://github.com/OHIF/Viewers/commit/88575c6c09fd778a31b2f91524163ce65d1639dd))
+9
View File
@@ -0,0 +1,9 @@
{
"name": "@externals/dicom-microscopy-viewer",
"description": "External reference to dicom-microscopy-viewer",
"version": "3.9.1",
"license": "MIT",
"dependencies": {
"dicom-microscopy-viewer": "^0.46.1"
}
}
+51
View File
@@ -0,0 +1,51 @@
{
"name": "ohif-monorepo-root",
"private": true,
"packageManager": "yarn@1.22.22",
"workspaces": {
"packages": [
"../platform/i18n",
"../platform/core",
"../platform/ui",
"../platform/ui-next",
"../platform/app",
"../extensions/*",
"../modes/*",
"../addOns/externals/*"
],
"nohoist": [
"**/html-minifier-terser"
]
},
"scripts": {
"preinstall": "cd .. && node preinstall.js"
},
"devDependencies": {
"@babel/core": "7.24.7",
"@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-object-rest-spread": "^7.17.3",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-arrow-functions": "^7.16.7",
"@babel/plugin-transform-regenerator": "^7.16.7",
"@babel/plugin-transform-runtime": "7.24.7",
"@babel/plugin-transform-typescript": "^7.13.0",
"@babel/preset-env": "7.24.7",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.13.0"
},
"resolutions": {
"**/@babel/runtime": "^7.20.13",
"commander": "8.3.0",
"dcmjs": ">=0.33.0",
"dicomweb-client": ">=0.10.4",
"nth-check": "^2.1.1",
"trim-newlines": "^5.0.0",
"glob-parent": "^6.0.2",
"trim": "^1.0.0",
"package-json": "^8.1.0",
"typescript": "5.5.4",
"sharp": "^0.32.6"
}
}
+4 -18
View File
@@ -5,27 +5,11 @@ module.exports = {
babelrcRoots: ['./platform/*', './extensions/*', './modes/*'],
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],
plugins: [
[
'inline-react-svg',
{
svgo: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
},
},
},
],
},
},
],
['@babel/plugin-proposal-class-properties', { loose: true }],
'@babel/plugin-transform-typescript',
['@babel/plugin-proposal-private-property-in-object', { loose: true }],
['@babel/plugin-proposal-private-methods', { loose: true }],
'@babel/plugin-transform-class-static-block',
],
env: {
test: {
@@ -45,8 +29,10 @@ module.exports = {
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-transform-regenerator',
'@babel/transform-destructuring',
'@babel/plugin-transform-runtime',
'@babel/plugin-transform-typescript',
'@babel/plugin-transform-class-static-block',
],
},
production: {
@@ -65,7 +51,7 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-hot-loader/babel'],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},
+1 -1
View File
@@ -1 +1 @@
0d10f46b885fe54ec3dae1848134da658eb6280a
610faa5a2738da5eabc40e57e338c359f481e852
-134
View File
@@ -1,134 +0,0 @@
import ImageSet from '@ohif/core/src/classes/ImageSet';
import { IWebApiDataSource } from '@ohif/core';
/**
*
*/
export default {
id: '@ohif/extension-*',
/**
* LIFECYCLE HOOKS
*/
preRegistration() {},
beforeExtInit() {},
beforeExtDestroy() {},
/**
* MODULES
*/
getCommandsModule,
getContextModule,
getDataSourcesModule,
getLayoutTemplateModule,
getPanelModule,
getSopClassHandlerModule,
getToolbarModule() {},
getViewportModule,
};
// appConfig,
// extensionConfig,
// dataSources,
// servicesManager,
// extensionManager,
// commandsManager,
/**
*
*/
const getCommandsModule = () => ({
definitions: {
exampleActionDef: {
commandFn: ({ param1 }) => {
console.log(`param1's value is: ${param1}`);
},
// storeContexts: ['viewports'],
options: { param1: 'hello world' },
context: 'VIEWER', // optional
},
},
defaultContext: 'ACTIVE_VIEWPORT::DICOMSR',
});
const ExampleContext = React.createContext();
function ExampleContextProvider({ children }) {
return <ExampleContext.Provider value={{ example: 'value' }}>{children}</ExampleContext.Provider>;
}
const getContextModule = () => [
{
name: 'ExampleContext',
context: ExampleContext,
provider: ExampleContextProvider,
},
];
const getDataSourcesModule = () => [
{
name: 'exampleDataSource',
type: 'webApi', // 'webApi' | 'local' | 'other'
createDataSource: dataSourceConfig => {
return IWebApiDataSource.create(/* */);
},
},
];
const getLayoutTemplateModule = (/* ... */) => [
{
id: 'exampleLayout',
name: 'exampleLayout',
component: ExampleLayoutComponent,
},
];
const getPanelModule = () => {
return [
{
name: 'exampleSidePanel',
iconName: 'info-circle-o',
iconLabel: 'Example',
label: 'Hello World',
isDisabled: studies => {}, // optional
component: ExamplePanelContentComponent,
},
];
};
const getSopClassHandlerModule = (/* ... */) => {
const BASIC_TEXT_SR = '1.2.840.10008.5.1.4.1.1.88.11';
return [
{
name: 'ExampleSopClassHandle',
sopClassUids: [BASIC_TEXT_SR],
getDisplaySetsFromSeries: instances => {
const imageSet = new ImageSet(instances);
imageSet.setAttributes(/** */);
imageSet.sortBy((a, b) => 0);
return imageSet;
},
},
];
};
const getToolbarModule = () => {};
// displaySet, dataSource
const getViewportModule = () => {
const wrappedViewport = props => {
return (
<ExampleViewport
{...props}
onEvent={data => {
commandsManager.runCommand('commandName', data);
}}
/>
);
};
return [{ name: 'example', component: wrappedViewport }];
};
@@ -0,0 +1,12 @@
const path = require('path');
const webpackCommon = require('./../../../.webpack/webpack.base.js');
const SRC_DIR = path.join(__dirname, '../src');
const DIST_DIR = path.join(__dirname, '../dist');
const ENTRY = {
app: `${SRC_DIR}/index.tsx`,
};
module.exports = (env, argv) => {
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR, ENTRY });
};
@@ -0,0 +1,54 @@
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const path = require('path');
const webpackCommon = require('./../../../.webpack/webpack.base.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const pkg = require('./../package.json');
const ROOT_DIR = path.join(__dirname, '../');
const SRC_DIR = path.join(__dirname, '../src');
const DIST_DIR = path.join(__dirname, '../dist');
const ENTRY = {
app: `${SRC_DIR}/index.tsx`,
};
const outputName = `ohif-${pkg.name.split('/').pop()}`;
module.exports = (env, argv) => {
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR, ENTRY });
return merge(commonConfig, {
stats: {
colors: true,
hash: true,
timings: true,
assets: true,
chunks: false,
chunkModules: false,
modules: false,
children: false,
warnings: true,
},
optimization: {
minimize: true,
sideEffects: true,
},
output: {
path: ROOT_DIR,
library: 'ohif-extension-cornerstone-dicom-pmap',
libraryTarget: 'umd',
filename: pkg.main,
},
externals: [/\b(vtk.js)/, /\b(dcmjs)/, /\b(gl-matrix)/, /^@ohif/, /^@cornerstonejs/],
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
new MiniCssExtractPlugin({
filename: `./dist/${outputName}.css`,
chunkFilename: `./dist/${outputName}.css`,
}),
],
});
};
@@ -0,0 +1,405 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.9.1](https://github.com/OHIF/Viewers/compare/v3.9.0...v3.9.1) (2024-11-13)
### Bug Fixes
* video playback ([#4497](https://github.com/OHIF/Viewers/issues/4497)) ([610faa5](https://github.com/OHIF/Viewers/commit/610faa5a2738da5eabc40e57e338c359f481e852))
# [3.9.0](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.111...v3.9.0) (2024-11-12)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.110...v3.9.0-beta.111) (2024-11-12)
### Bug Fixes
* Measurement Tracking: Various UI and functionality improvements ([#4481](https://github.com/OHIF/Viewers/issues/4481)) ([62b2748](https://github.com/OHIF/Viewers/commit/62b27488471c9d5979142e2d15872a85778b90ed))
# [3.9.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.109...v3.9.0-beta.110) (2024-11-11)
### Bug Fixes
* **bugs:** Update dependencies and enhance UI components ([#4478](https://github.com/OHIF/Viewers/issues/4478)) ([05d41c5](https://github.com/OHIF/Viewers/commit/05d41c52068a3b7ba249f15ecdf71838c352fd30))
# [3.9.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.108...v3.9.0-beta.109) (2024-11-08)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.107...v3.9.0-beta.108) (2024-11-07)
### Bug Fixes
* **tmtv:** fix toggle one up weird behaviours ([#4473](https://github.com/OHIF/Viewers/issues/4473)) ([aa2b649](https://github.com/OHIF/Viewers/commit/aa2b649444eb4fe5422e72ea7830a709c4d24a90))
# [3.9.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.106...v3.9.0-beta.107) (2024-11-06)
### Bug Fixes
* build ([#4471](https://github.com/OHIF/Viewers/issues/4471)) ([3d11ef2](https://github.com/OHIF/Viewers/commit/3d11ef28f213361ec7586809317bd219fa70e742))
# [3.9.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.105...v3.9.0-beta.106) (2024-11-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.104...v3.9.0-beta.105) (2024-11-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.103...v3.9.0-beta.104) (2024-10-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.102...v3.9.0-beta.103) (2024-10-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.101...v3.9.0-beta.102) (2024-10-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.100...v3.9.0-beta.101) (2024-10-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.99...v3.9.0-beta.100) (2024-10-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.98...v3.9.0-beta.99) (2024-10-17)
### Features
* **SR:** SCOORD3D point annotations support for stack viewports ([#4315](https://github.com/OHIF/Viewers/issues/4315)) ([ac1cad2](https://github.com/OHIF/Viewers/commit/ac1cad25af12ee0f7d508647e3134ed724d9b4d3))
# [3.9.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.97...v3.9.0-beta.98) (2024-10-15)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.96...v3.9.0-beta.97) (2024-10-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.95...v3.9.0-beta.96) (2024-10-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.94...v3.9.0-beta.95) (2024-10-08)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.93...v3.9.0-beta.94) (2024-10-04)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.92...v3.9.0-beta.93) (2024-10-04)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.91...v3.9.0-beta.92) (2024-10-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.90...v3.9.0-beta.91) (2024-10-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.89...v3.9.0-beta.90) (2024-09-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.88...v3.9.0-beta.89) (2024-09-27)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.87...v3.9.0-beta.88) (2024-09-24)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.86...v3.9.0-beta.87) (2024-09-19)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.85...v3.9.0-beta.86) (2024-09-19)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.84...v3.9.0-beta.85) (2024-09-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.83...v3.9.0-beta.84) (2024-09-12)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.82...v3.9.0-beta.83) (2024-09-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.81...v3.9.0-beta.82) (2024-09-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.80...v3.9.0-beta.81) (2024-08-27)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.79...v3.9.0-beta.80) (2024-08-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.78...v3.9.0-beta.79) (2024-08-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.77...v3.9.0-beta.78) (2024-08-15)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.76...v3.9.0-beta.77) (2024-08-15)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.75...v3.9.0-beta.76) (2024-08-08)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.74...v3.9.0-beta.75) (2024-08-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.73...v3.9.0-beta.74) (2024-08-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.72...v3.9.0-beta.73) (2024-08-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.71...v3.9.0-beta.72) (2024-07-31)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.70...v3.9.0-beta.71) (2024-07-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.69...v3.9.0-beta.70) (2024-07-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.68...v3.9.0-beta.69) (2024-07-27)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.67...v3.9.0-beta.68) (2024-07-26)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.66...v3.9.0-beta.67) (2024-07-26)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.9.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.9.0-beta.65...v3.9.0-beta.66) (2024-07-24)
### Features
* **pmap:** added support for parametric map ([#4284](https://github.com/OHIF/Viewers/issues/4284)) ([fc0064f](https://github.com/OHIF/Viewers/commit/fc0064fd9d8cdc8fde81b81f0e71fd5d077ca22b))
+20
View File
@@ -0,0 +1,20 @@
MIT License
Copyright (c) 2023 Open Health Imaging Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,12 @@
# dicom-pmap
## Description
DICOM PMAP read workflow. This extension will allow you to load a DICOM Parametric
Map image and display it on OHIF.
## Author
OHIF
## License
MIT
@@ -0,0 +1,44 @@
module.exports = {
plugins: ['@babel/plugin-proposal-class-properties'],
env: {
test: {
presets: [
[
// TODO: https://babeljs.io/blog/2019/03/19/7.4.0#migration-from-core-js-2
'@babel/preset-env',
{
modules: 'commonjs',
debug: false,
},
'@babel/preset-typescript',
],
'@babel/preset-react',
],
plugins: [
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-transform-regenerator',
'@babel/plugin-transform-runtime',
],
},
production: {
presets: [
// WebPack handles ES6 --> Target Syntax
['@babel/preset-env', { modules: false }],
'@babel/preset-react',
'@babel/preset-typescript',
],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
development: {
presets: [
// WebPack handles ES6 --> Target Syntax
['@babel/preset-env', { modules: false }],
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-hot-loader/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},
};
@@ -0,0 +1,54 @@
{
"name": "@ohif/extension-cornerstone-dicom-pmap",
"version": "3.9.1",
"description": "DICOM Parametric Map read workflow",
"author": "OHIF",
"license": "MIT",
"main": "dist/ohif-extension-cornerstone-dicom-pmap.umd.js",
"module": "src/index.tsx",
"files": [
"dist/**",
"public/**",
"README.md"
],
"repository": "OHIF/Viewers",
"keywords": [
"ohif-extension"
],
"publishConfig": {
"access": "public"
},
"engines": {
"node": ">=14",
"npm": ">=6",
"yarn": ">=1.18.0"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:dicom-pmap": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
"build:package-1": "yarn run build",
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.9.1",
"@ohif/extension-cornerstone": "3.9.1",
"@ohif/extension-default": "3.9.1",
"@ohif/i18n": "3.9.1",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^12.2.2",
"react-router": "^6.8.1",
"react-router-dom": "^6.8.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.4",
"@cornerstonejs/core": "^2.2.4",
"@kitware/vtk.js": "32.1.0",
"react-color": "^2.19.3"
}
}
@@ -0,0 +1,244 @@
import { utils } from '@ohif/core';
import { metaData, cache, utilities as csUtils, volumeLoader } from '@cornerstonejs/core';
import { adaptersPMAP } from '@cornerstonejs/adapters';
import { SOPClassHandlerId } from './id';
import { dicomLoaderService } from '@ohif/extension-cornerstone';
const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
const sopClassUids = ['1.2.840.10008.5.1.4.1.1.30'];
function _getDisplaySetsFromSeries(
instances,
servicesManager: AppTypes.ServicesManager,
extensionManager
) {
const instance = instances[0];
const {
StudyInstanceUID,
SeriesInstanceUID,
SOPInstanceUID,
SeriesDescription,
SeriesNumber,
SeriesDate,
SOPClassUID,
wadoRoot,
wadoUri,
wadoUriRoot,
} = instance;
const displaySet = {
// Parametric map use to have the same modality as its referenced volume but
// "PMAP" is used in the viewer even though this is not a valid DICOM modality
Modality: 'PMAP',
isReconstructable: true, // by default for now
displaySetInstanceUID: `pmap.${utils.guid()}`,
SeriesDescription,
SeriesNumber,
SeriesDate,
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
SOPClassHandlerId,
SOPClassUID,
referencedImages: null,
referencedSeriesInstanceUID: null,
referencedDisplaySetInstanceUID: null,
referencedVolumeURI: null,
referencedVolumeId: null,
isDerivedDisplaySet: true,
loadStatus: {
loading: false,
loaded: false,
},
sopClassUids,
instance,
instances: [instance],
wadoRoot,
wadoUriRoot,
wadoUri,
isOverlayDisplaySet: true,
};
const referencedSeriesSequence = instance.ReferencedSeriesSequence;
if (!referencedSeriesSequence) {
console.error('ReferencedSeriesSequence is missing for the parametric map');
return;
}
const referencedSeries = referencedSeriesSequence[0] || referencedSeriesSequence;
displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
// Does not get the referenced displaySet during parametric displaySet creation
// because it is still not available (getDisplaySetByUID returns `undefined`).
displaySet.getReferenceDisplaySet = () => {
const { displaySetService } = servicesManager.services;
if (displaySet.referencedDisplaySetInstanceUID) {
return displaySetService.getDisplaySetByUID(displaySet.referencedDisplaySetInstanceUID);
}
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
displaySet.referencedSeriesInstanceUID
);
if (!referencedDisplaySets || referencedDisplaySets.length === 0) {
throw new Error('Referenced displaySet is missing for the parametric map');
}
const referencedDisplaySet = referencedDisplaySets[0];
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
return referencedDisplaySet;
};
// Does not get the referenced volumeId during parametric displaySet creation because the
// referenced displaySet is still not available (getDisplaySetByUID returns `undefined`).
displaySet.getReferencedVolumeId = () => {
if (displaySet.referencedVolumeId) {
return displaySet.referencedVolumeId;
}
const referencedDisplaySet = displaySet.getReferenceDisplaySet();
const referencedVolumeURI = referencedDisplaySet.displaySetInstanceUID;
const referencedVolumeId = `${VOLUME_LOADER_SCHEME}:${referencedVolumeURI}`;
displaySet.referencedVolumeURI = referencedVolumeURI;
displaySet.referencedVolumeId = referencedVolumeId;
return referencedVolumeId;
};
displaySet.load = async ({ headers }) =>
await _load(displaySet, servicesManager, extensionManager, headers);
return [displaySet];
}
const getRangeFromPixelData = (pixelData: Float32Array) => {
let lowest = pixelData[0];
let highest = pixelData[0];
for (let i = 1; i < pixelData.length; i++) {
if (pixelData[i] < lowest) {
lowest = pixelData[i];
}
if (pixelData[i] > highest) {
highest = pixelData[i];
}
}
return [lowest, highest];
};
async function _load(
displaySet,
servicesManager: AppTypes.ServicesManager,
extensionManager,
headers
) {
const volumeId = `${VOLUME_LOADER_SCHEME}:${displaySet.displaySetInstanceUID}`;
const volumeLoadObject = cache.getVolumeLoadObject(volumeId);
if (volumeLoadObject) {
return volumeLoadObject.promise;
}
displaySet.loading = true;
displaySet.isLoaded = false;
// We don't want to fire multiple loads, so we'll wait for the first to finish
// and also return the same promise to any other callers.
const promise = _loadParametricMap({
extensionManager,
displaySet,
headers,
});
cache.putVolumeLoadObject(volumeId, { promise }).catch(err => {
throw err;
});
promise
.then(() => {
displaySet.loading = false;
displaySet.isLoaded = true;
// Broadcast that loading is complete
servicesManager.services.segmentationService._broadcastEvent(
servicesManager.services.segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
{
pmapDisplaySet: displaySet,
}
);
})
.catch(err => {
displaySet.loading = false;
displaySet.isLoaded = false;
throw err;
});
return promise;
}
async function _loadParametricMap({ displaySet, headers }: withAppTypes) {
const arrayBuffer = await dicomLoaderService.findDicomDataPromise(displaySet, null, headers);
const referencedVolumeId = displaySet.getReferencedVolumeId();
const cachedReferencedVolume = cache.getVolume(referencedVolumeId);
// Parametric map can be loaded only if its referenced volume exists otherwise it will fail
if (!cachedReferencedVolume) {
throw new Error(
'Referenced Volume is missing for the PMAP, and stack viewport PMAP is not supported yet'
);
}
const { imageIds } = cachedReferencedVolume;
const results = await adaptersPMAP.Cornerstone3D.ParametricMap.generateToolState(
imageIds,
arrayBuffer,
metaData
);
const { pixelData } = results;
const TypedArrayConstructor = pixelData.constructor;
const paramMapId = displaySet.displaySetInstanceUID;
const derivedVolume = await volumeLoader.createAndCacheDerivedVolume(referencedVolumeId, {
volumeId: paramMapId,
targetBuffer: {
type: TypedArrayConstructor.name,
},
});
const newPixelData = new TypedArrayConstructor(pixelData.length);
for (let i = 0; i < pixelData.length; i++) {
newPixelData[i] = pixelData[i] * 100;
}
derivedVolume.voxelManager.setCompleteScalarDataArray(newPixelData);
const range = getRangeFromPixelData(newPixelData);
const windowLevel = csUtils.windowLevel.toWindowLevel(range[0], range[1]);
derivedVolume.metadata.voiLut = [windowLevel];
derivedVolume.loadStatus = { loaded: true };
return derivedVolume;
}
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
const getDisplaySetsFromSeries = instances => {
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
};
return [
{
name: 'dicom-pmap',
sopClassUids,
getDisplaySetsFromSeries,
},
];
}
export default getSopClassHandlerModule;
@@ -0,0 +1,7 @@
import packageJson from '../package.json';
const id = packageJson.name;
const SOPClassHandlerName = 'dicom-pmap';
const SOPClassHandlerId = `${id}.sopClassHandlerModule.${SOPClassHandlerName}`;
export { id, SOPClassHandlerId, SOPClassHandlerName };
@@ -0,0 +1,39 @@
import { id } from './id';
import React from 'react';
import getSopClassHandlerModule from './getSopClassHandlerModule';
const Component = React.lazy(() => {
return import(/* webpackPrefetch: true */ './viewports/OHIFCornerstonePMAPViewport');
});
const OHIFCornerstonePMAPViewport = props => {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Component {...props} />
</React.Suspense>
);
};
/**
* You can remove any of the following modules if you don't need them.
*/
const extension = {
id,
getViewportModule({ servicesManager, extensionManager, commandsManager }) {
const ExtendedOHIFCornerstonePMAPViewport = props => {
return (
<OHIFCornerstonePMAPViewport
servicesManager={servicesManager}
extensionManager={extensionManager}
commandsManager={commandsManager}
{...props}
/>
);
};
return [{ name: 'dicom-pmap', component: ExtendedOHIFCornerstonePMAPViewport }];
},
getSopClassHandlerModule,
};
export default extension;
@@ -0,0 +1,205 @@
import PropTypes from 'prop-types';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useViewportGrid, LoadingIndicatorTotalPercent } from '@ohif/ui';
function OHIFCornerstonePMAPViewport(props: withAppTypes) {
const {
displaySets,
children,
viewportOptions,
displaySetOptions,
servicesManager,
extensionManager,
} = props;
const viewportId = viewportOptions.viewportId;
const { displaySetService, segmentationService, uiNotificationService } =
servicesManager.services;
// PMAP viewport will always have a single display set
if (displaySets.length !== 1) {
throw new Error('PMAP viewport must have a single display set');
}
const pmapDisplaySet = displaySets[0];
const [viewportGrid, viewportGridService] = useViewportGrid();
const referencedDisplaySetRef = useRef(null);
const { viewports, activeViewportId } = viewportGrid;
const referencedDisplaySet = pmapDisplaySet.getReferenceDisplaySet();
const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata(
referencedDisplaySet,
pmapDisplaySet
);
referencedDisplaySetRef.current = {
displaySet: referencedDisplaySet,
metadata: referencedDisplaySetMetadata,
};
const [pmapIsLoading, setPmapIsLoading] = useState(!pmapDisplaySet.isLoaded);
// Add effect to listen for loading complete
useEffect(() => {
const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
evt => {
if (evt.pmapDisplaySet?.displaySetInstanceUID === pmapDisplaySet.displaySetInstanceUID) {
setPmapIsLoading(false);
}
}
);
return () => {
unsubscribe();
};
}, [pmapDisplaySet]);
const getCornerstoneViewport = useCallback(() => {
const { displaySet: referencedDisplaySet } = referencedDisplaySetRef.current;
const { component: Component } = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.viewportModule.cornerstone'
);
displaySetOptions.unshift({});
const [pmapDisplaySetOptions] = displaySetOptions;
// Make sure `options` exists
pmapDisplaySetOptions.options = pmapDisplaySetOptions.options ?? {};
Object.assign(pmapDisplaySetOptions.options, {
colormap: {
name: 'rainbow_2',
opacity: [
{ value: 0, opacity: 0 },
{ value: 0.25, opacity: 0.25 },
{ value: 0.5, opacity: 0.5 },
{ value: 0.75, opacity: 0.75 },
{ value: 0.9, opacity: 0.99 },
],
},
voi: {
windowCenter: 50,
windowWidth: 100,
},
});
uiNotificationService.show({
title: 'Parametric Map',
type: 'warning',
message: 'The values are multiplied by 100 in the viewport for better visibility',
});
return (
<Component
{...props}
// Referenced + PMAP displaySets must be passed as parameter in this order
displaySets={[referencedDisplaySet, pmapDisplaySet]}
viewportOptions={{
viewportType: 'volume',
orientation: viewportOptions.orientation,
viewportId: viewportOptions.viewportId,
}}
displaySetOptions={[{}, pmapDisplaySetOptions]}
></Component>
);
}, [
extensionManager,
displaySetOptions,
props,
pmapDisplaySet,
viewportOptions.orientation,
viewportOptions.viewportId,
]);
// Cleanup the PMAP viewport when the viewport is destroyed
useEffect(() => {
const onDisplaySetsRemovedSubscription = displaySetService.subscribe(
displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
({ displaySetInstanceUIDs }) => {
const activeViewport = viewports.get(activeViewportId);
if (displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)) {
viewportGridService.setDisplaySetsForViewport({
viewportId: activeViewportId,
displaySetInstanceUIDs: [],
});
}
}
);
return () => {
onDisplaySetsRemovedSubscription.unsubscribe();
};
}, [activeViewportId, displaySetService, viewportGridService, viewports]);
let childrenWithProps = null;
if (children && children.length) {
childrenWithProps = children.map((child, index) => {
return (
child &&
React.cloneElement(child, {
viewportId,
key: index,
})
);
});
}
return (
<>
<div className="relative flex h-full w-full flex-row overflow-hidden">
{pmapIsLoading && (
<LoadingIndicatorTotalPercent
className="h-full w-full"
totalNumbers={null}
percentComplete={null}
loadingText="Loading Parametric Map..."
/>
)}
{getCornerstoneViewport()}
{childrenWithProps}
</div>
</>
);
}
OHIFCornerstonePMAPViewport.propTypes = {
displaySets: PropTypes.arrayOf(PropTypes.object),
viewportId: PropTypes.string.isRequired,
dataSource: PropTypes.object,
children: PropTypes.node,
};
function _getReferencedDisplaySetMetadata(referencedDisplaySet, pmapDisplaySet) {
const { SharedFunctionalGroupsSequence } = pmapDisplaySet.instance;
const SharedFunctionalGroup = Array.isArray(SharedFunctionalGroupsSequence)
? SharedFunctionalGroupsSequence[0]
: SharedFunctionalGroupsSequence;
const { PixelMeasuresSequence } = SharedFunctionalGroup;
const PixelMeasures = Array.isArray(PixelMeasuresSequence)
? PixelMeasuresSequence[0]
: PixelMeasuresSequence;
const { SpacingBetweenSlices, SliceThickness } = PixelMeasures;
const image0 = referencedDisplaySet.images[0];
const referencedDisplaySetMetadata = {
PatientID: image0.PatientID,
PatientName: image0.PatientName,
PatientSex: image0.PatientSex,
PatientAge: image0.PatientAge,
SliceThickness: image0.SliceThickness || SliceThickness,
StudyDate: image0.StudyDate,
SeriesDescription: image0.SeriesDescription,
SeriesInstanceUID: image0.SeriesInstanceUID,
SeriesNumber: image0.SeriesNumber,
ManufacturerModelName: image0.ManufacturerModelName,
SpacingBetweenSlices: image0.SpacingBetweenSlices || SpacingBetweenSlices,
};
return referencedDisplaySetMetadata;
}
export default OHIFCornerstonePMAPViewport;
File diff suppressed because it is too large. Load diff
@@ -1,5 +1,5 @@
module.exports = {
plugins: ['inline-react-svg', '@babel/plugin-proposal-class-properties'],
plugins: ['@babel/plugin-proposal-class-properties'],
env: {
test: {
presets: [
@@ -37,7 +37,7 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-hot-loader/babel'],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},
+11 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-rt",
"version": "3.7.0-beta.71",
"version": "3.9.1",
"description": "DICOM RT read workflow",
"author": "OHIF",
"license": "MIT",
@@ -24,6 +24,8 @@
"yarn": ">=1.18.0"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:dicom-seg": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
@@ -31,16 +33,16 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.71",
"@ohif/extension-cornerstone": "3.7.0-beta.71",
"@ohif/extension-default": "3.7.0-beta.71",
"@ohif/i18n": "3.7.0-beta.71",
"@ohif/core": "3.9.1",
"@ohif/extension-cornerstone": "3.9.1",
"@ohif/extension-default": "3.9.1",
"@ohif/i18n": "3.9.1",
"prop-types": "^15.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^10.11.0",
"react-router": "^6.3.0",
"react-router-dom": "^6.3.0"
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
@@ -0,0 +1,55 @@
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
const services = servicesManager.services;
const { displaySetService, viewportGridService } = services;
const actions = {
hydrateRTSDisplaySet: ({ displaySet, viewportId }) => {
if (displaySet.Modality !== 'RTSTRUCT') {
throw new Error('Display set is not an RTSTRUCT');
}
const referencedDisplaySet = displaySetService.getDisplaySetByUID(
displaySet.referencedDisplaySetInstanceUID
);
// update the previously stored segmentationPresentation with the new viewportId
// presentation so that when we put the referencedDisplaySet back in the viewport
// it will have the correct segmentation representation hydrated
commandsManager.runCommand('updateStoredSegmentationPresentation', {
displaySet: displaySet,
type: SegmentationRepresentations.Contour,
});
// update the previously stored positionPresentation with the new viewportId
// presentation so that when we put the referencedDisplaySet back in the viewport
// it will be in the correct position zoom and pan
commandsManager.runCommand('updateStoredPositionPresentation', {
viewportId,
displaySetInstanceUID: referencedDisplaySet.displaySetInstanceUID,
});
viewportGridService.setDisplaySetsForViewport({
viewportId,
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
});
},
};
const definitions = {
hydrateRTSDisplaySet: {
commandFn: actions.hydrateRTSDisplaySet,
storeContexts: [],
options: {},
},
};
return {
actions,
definitions,
defaultContext: 'cornerstone-dicom-rt',
};
};
export default commandsModule;
@@ -5,9 +5,13 @@ import loadRTStruct from './loadRTStruct';
const sopClassUids = ['1.2.840.10008.5.1.4.1.1.481.3'];
let loadPromises = {};
const loadPromises = {};
function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
function _getDisplaySetsFromSeries(
instances,
servicesManager: AppTypes.ServicesManager,
extensionManager
) {
const instance = instances[0];
const {
@@ -68,29 +72,34 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
displaySet.getReferenceDisplaySet = () => {
const { DisplaySetService } = servicesManager.services;
const referencedDisplaySets = DisplaySetService.getDisplaySetsForSeries(
displaySet.referencedSeriesInstanceUID
const { displaySetService } = servicesManager.services;
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
displaySet.referencedSeriesInstanceUID
);
if (!referencedDisplaySets || referencedDisplaySets.length === 0) {
// Instead of throwing error, subscribe to display sets added
const { unsubscribe } = displaySetService.subscribe(
displaySetService.EVENTS.DISPLAY_SETS_ADDED,
({ displaySetsAdded }) => {
const addedDisplaySet = displaySetsAdded[0];
if (addedDisplaySet.SeriesInstanceUID === displaySet.referencedSeriesInstanceUID) {
displaySet.referencedDisplaySetInstanceUID = addedDisplaySet.displaySetInstanceUID;
unsubscribe();
}
}
);
if (!referencedDisplaySets || referencedDisplaySets.length === 0) {
throw new Error('Referenced DisplaySet is missing for the RT');
}
} else {
const referencedDisplaySet = referencedDisplaySets[0];
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
return referencedDisplaySet;
};
}
displaySet.load = ({ headers }) => _load(displaySet, servicesManager, extensionManager, headers);
return [displaySet];
}
function _load(rtDisplaySet, servicesManager, extensionManager, headers) {
function _load(rtDisplaySet, servicesManager: AppTypes.ServicesManager, extensionManager, headers) {
const { SOPInstanceUID } = rtDisplaySet;
const { segmentationService } = servicesManager.services;
if (
@@ -107,19 +116,13 @@ function _load(rtDisplaySet, servicesManager, extensionManager, headers) {
// and also return the same promise to any other callers.
loadPromises[SOPInstanceUID] = new Promise(async (resolve, reject) => {
if (!rtDisplaySet.structureSet) {
const structureSet = await loadRTStruct(
extensionManager,
rtDisplaySet,
rtDisplaySet.getReferenceDisplaySet(),
headers
);
const structureSet = await loadRTStruct(extensionManager, rtDisplaySet, headers);
rtDisplaySet.structureSet = structureSet;
}
const suppressEvents = true;
segmentationService
.createSegmentationForRTDisplaySet(rtDisplaySet, null, suppressEvents)
.createSegmentationForRTDisplaySet(rtDisplaySet)
.then(() => {
rtDisplaySet.loading = false;
resolve();
@@ -168,7 +171,10 @@ function _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
return ReferencedSeriesSequence;
}
function _segmentationExistsInCache(rtDisplaySet, segmentationService) {
function _segmentationExistsInCache(
rtDisplaySet,
segmentationService: AppTypes.SegmentationService
) {
// Todo: fix this
return false;
// This should be abstracted with the CornerstoneCacheService
@@ -2,7 +2,7 @@ import { id } from './id';
import React from 'react';
import { Types } from '@ohif/core';
import getSopClassHandlerModule from './getSopClassHandlerModule';
import hydrateRTDisplaySet from './utils/_hydrateRT';
import getCommandsModule from './getCommandsModule';
const Component = React.lazy(() => {
return import(/* webpackPrefetch: true */ './viewports/OHIFCornerstoneRTViewport');
@@ -25,6 +25,7 @@ const extension: Types.Extensions.Extension = {
* You ID can be anything you want, but it should be unique.
*/
id,
getCommandsModule,
/**
* PanelModule should provide a list of panels that will be available in OHIF
@@ -60,4 +61,3 @@ const extension: Types.Extensions.Extension = {
};
export default extension;
export { hydrateRTDisplaySet };
@@ -89,12 +89,7 @@ async function checkAndLoadContourData(instance, datasource) {
});
}
export default async function loadRTStruct(
extensionManager,
rtStructDisplaySet,
referencedDisplaySet,
headers
) {
export default async function loadRTStruct(extensionManager, rtStructDisplaySet, headers) {
const utilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.utilityModule.common'
);
@@ -102,8 +97,6 @@ export default async function loadRTStruct(
const { bulkDataURI } = dataSource.getConfig?.() || {};
const { dicomLoaderService } = utilityModule.exports;
const imageIdSopInstanceUidPairs =
_getImageIdSopInstanceUidPairsForDisplaySet(referencedDisplaySet);
// Set here is loading is asynchronous.
// If this function throws its set back to false.
@@ -149,8 +142,7 @@ export default async function loadRTStruct(
const contourPoints = [];
for (let c = 0; c < ContourSequenceArray.length; c++) {
const { ContourImageSequence, ContourData, NumberOfContourPoints, ContourGeometricType } =
ContourSequenceArray[c];
const { ContourData, NumberOfContourPoints, ContourGeometricType } = ContourSequenceArray[c];
let isSupported = false;
@@ -194,24 +186,6 @@ export default async function loadRTStruct(
return structureSet;
}
const _getImageId = (imageIdSopInstanceUidPairs, sopInstanceUID) => {
const imageIdSopInstanceUidPairsEntry = imageIdSopInstanceUidPairs.find(
imageIdSopInstanceUidPairsEntry =>
imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID
);
return imageIdSopInstanceUidPairsEntry ? imageIdSopInstanceUidPairsEntry.imageId : null;
};
function _getImageIdSopInstanceUidPairsForDisplaySet(referencedDisplaySet) {
return referencedDisplaySet.images.map(image => {
return {
imageId: image.imageId,
sopInstanceUID: image.SOPInstanceUID,
};
});
}
function _setROIContourMetadata(
structureSet,
StructureSetROISequence,
@@ -1,70 +0,0 @@
async function _hydrateRTDisplaySet({ rtDisplaySet, viewportId, servicesManager }) {
const { segmentationService, hangingProtocolService, viewportGridService } =
servicesManager.services;
const displaySetInstanceUID = rtDisplaySet.referencedDisplaySetInstanceUID;
let segmentationId = null;
// We need the hydration to notify panels about the new segmentation added
const suppressEvents = false;
segmentationId = await segmentationService.createSegmentationForRTDisplaySet(
rtDisplaySet,
segmentationId,
suppressEvents
);
segmentationService.hydrateSegmentation(rtDisplaySet.displaySetInstanceUID);
const { viewports } = viewportGridService.getState();
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
viewportId,
displaySetInstanceUID
);
viewportGridService.setDisplaySetsForViewports(updatedViewports);
// Todo: fix this after we have a better way for stack viewport segmentations
// check every viewport in the viewports to see if the displaySetInstanceUID
// is being displayed, if so we need to update the viewport to use volume viewport
// (if already is not using it) since Cornerstone3D currently only supports
// volume viewport for segmentation
viewports.forEach(viewport => {
if (viewport.viewportId === viewportId) {
return;
}
const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(
viewport.displaySetInstanceUIDs,
rtDisplaySet.displaySetInstanceUID
);
if (shouldDisplaySeg) {
updatedViewports.push({
viewportId: viewport.viewportId,
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
viewportOptions: {
// Note: This is a hack to get the grid to re-render the OHIFCornerstoneViewport component
// Used for segmentation hydration right now, since the logic to decide whether
// a viewport needs to render a segmentation lives inside the CornerstoneViewportService
// so we need to re-render (force update via change of the needsRerendering) so that React
// does the diffing and decides we should render this again (although the id and element has not changed)
// so that the CornerstoneViewportService can decide whether to render the segmentation or not.
needsRerendering: true,
initialImageOptions: {
preset: 'middle',
},
},
});
}
});
// Do the entire update at once
viewportGridService.setDisplaySetsForViewports(updatedViewports);
return true;
}
export default _hydrateRTDisplaySet;
@@ -1,7 +1,7 @@
function createRTToolGroupAndAddTools(ToolGroupService, customizationService, toolGroupId) {
const { tools } = customizationService.get('cornerstone.overlayViewportTools') ?? {};
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {});
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools);
}
export default createRTToolGroupAndAddTools;
@@ -1,5 +1,4 @@
import { ButtonEnums } from '@ohif/ui';
import hydrateRTDisplaySet from './_hydrateRT';
const RESPONSE = {
NO_NEVER: -1,
@@ -11,13 +10,16 @@ function promptHydrateRT({
servicesManager,
rtDisplaySet,
viewportId,
toolGroupId = 'default',
preHydrateCallbacks,
}) {
hydrateRTDisplaySet,
}: withAppTypes) {
const { uiViewportDialogService } = servicesManager.services;
const extensionManager = servicesManager._extensionManager;
const appConfig = extensionManager._appConfig;
return new Promise(async function (resolve, reject) {
const promptResult = await _askHydrate(uiViewportDialogService, viewportId);
const promptResult = appConfig?.disableConfirmationPrompts
? RESPONSE.HYDRATE_SEG
: await _askHydrate(uiViewportDialogService, viewportId);
if (promptResult === RESPONSE.HYDRATE_SEG) {
preHydrateCallbacks?.forEach(callback => {
@@ -27,7 +29,6 @@ function promptHydrateRT({
const isHydrated = await hydrateRTDisplaySet({
rtDisplaySet,
viewportId,
toolGroupId,
servicesManager,
});
@@ -36,16 +37,18 @@ function promptHydrateRT({
});
}
function _askHydrate(uiViewportDialogService, viewportId) {
function _askHydrate(uiViewportDialogService: AppTypes.UIViewportDialogService, viewportId) {
return new Promise(function (resolve, reject) {
const message = 'Do you want to open this Segmentation?';
const actions = [
{
id: 'no-hydrate',
type: ButtonEnums.type.secondary,
text: 'No',
value: RESPONSE.CANCEL,
},
{
id: 'yes-hydrate',
type: ButtonEnums.type.primary,
text: 'Yes',
value: RESPONSE.HYDRATE_SEG,
@@ -57,6 +60,7 @@ function _askHydrate(uiViewportDialogService, viewportId) {
};
uiViewportDialogService.show({
id: 'promptHydrateRT',
viewportId,
type: 'info',
message,
@@ -66,6 +70,11 @@ function _askHydrate(uiViewportDialogService, viewportId) {
uiViewportDialogService.hide();
resolve(RESPONSE.CANCEL);
},
onKeyPress: event => {
if (event.key === 'Enter') {
onSubmit(RESPONSE.HYDRATE_SEG);
}
},
});
});
}
@@ -1,23 +1,20 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import OHIF, { utils } from '@ohif/core';
import { ViewportActionBar, useViewportGrid, LoadingIndicatorTotalPercent } from '@ohif/ui';
import { useViewportGrid, LoadingIndicatorTotalPercent, ViewportActionArrows } from '@ohif/ui';
import _hydrateRTdisplaySet from '../utils/_hydrateRT';
import promptHydrateRT from '../utils/promptHydrateRT';
import _getStatusComponent from './_getStatusComponent';
import createRTToolGroupAndAddTools from '../utils/initRTToolGroup';
import _hydrateRTDisplaySet from '../utils/_hydrateRT';
const { formatDate } = utils;
import createRTToolGroupAndAddTools from '../utils/initRTToolGroup';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
const RT_TOOLGROUP_BASE_NAME = 'RTToolGroup';
function OHIFCornerstoneRTViewport(props) {
function OHIFCornerstoneRTViewport(props: withAppTypes) {
const {
children,
displaySets,
viewportOptions,
viewportLabel,
servicesManager,
extensionManager,
commandsManager,
@@ -29,6 +26,7 @@ function OHIFCornerstoneRTViewport(props) {
segmentationService,
uiNotificationService,
customizationService,
viewportActionCornersService,
} = servicesManager.services;
const viewportId = viewportOptions.viewportId;
@@ -66,7 +64,10 @@ function OHIFCornerstoneRTViewport(props) {
const { viewports, activeViewportId } = viewportGrid;
const referencedDisplaySet = rtDisplaySet.getReferenceDisplaySet();
const referencedDisplaySetInstanceUID = rtDisplaySet.referencedDisplaySetInstanceUID;
const referencedDisplaySet = displaySetService.getDisplaySetByUID(
referencedDisplaySetInstanceUID
);
const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata(referencedDisplaySet);
referencedDisplaySetRef.current = {
@@ -95,6 +96,16 @@ function OHIFCornerstoneRTViewport(props) {
});
}, [viewportGrid]);
const hydrateRTDisplaySet = useCallback(
({ rtDisplaySet, viewportId }) => {
commandsManager.runCommand('hydrateRTSDisplaySet', {
displaySet: rtDisplaySet,
viewportId,
});
},
[commandsManager]
);
const getCornerstoneViewport = useCallback(() => {
const { component: Component } = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.viewportModule.cornerstone'
@@ -108,12 +119,15 @@ function OHIFCornerstoneRTViewport(props) {
{...props}
displaySets={[referencedDisplaySet, rtDisplaySet]}
viewportOptions={{
viewportType: 'volume',
viewportType: 'stack',
toolGroupId: toolGroupId,
orientation: viewportOptions.orientation,
viewportId: viewportOptions.viewportId,
}}
onElementEnabled={onElementEnabled}
onElementEnabled={evt => {
props.onElementEnabled?.(evt);
onElementEnabled(evt);
}}
onElementDisabled={onElementDisabled}
></Component>
);
@@ -121,7 +135,6 @@ function OHIFCornerstoneRTViewport(props) {
const onSegmentChange = useCallback(
direction => {
direction = direction === 'left' ? -1 : 1;
const segmentationId = rtDisplaySet.displaySetInstanceUID;
const segmentation = segmentationService.getSegmentation(segmentationId);
@@ -138,10 +151,10 @@ function OHIFCornerstoneRTViewport(props) {
newSelectedSegmentIndex = numberOfSegments - 1;
}
segmentationService.jumpToSegmentCenter(segmentationId, newSelectedSegmentIndex, toolGroupId);
segmentationService.jumpToSegmentCenter(segmentationId, newSelectedSegmentIndex, viewportId);
setSelectedSegment(newSelectedSegmentIndex);
},
[selectedSegment]
[selectedSegment, segmentationService]
);
useEffect(() => {
@@ -154,6 +167,7 @@ function OHIFCornerstoneRTViewport(props) {
viewportId,
rtDisplaySet,
preHydrateCallbacks: [storePresentationState],
hydrateRTDisplaySet,
}).then(isHydrated => {
if (isHydrated) {
setIsHydrated(true);
@@ -162,6 +176,8 @@ function OHIFCornerstoneRTViewport(props) {
}, [servicesManager, viewportId, rtDisplaySet, rtIsLoading]);
useEffect(() => {
// I'm not sure what is this, since in RT we support Overlapping segments
// via contours
const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
evt => {
@@ -235,7 +251,7 @@ function OHIFCornerstoneRTViewport(props) {
return () => {
// remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentationFromToolGroup(toolGroupId);
segmentationService.removeSegmentationRepresentations(viewportId);
toolGroupService.destroyToolGroup(toolGroupId);
};
@@ -246,11 +262,27 @@ function OHIFCornerstoneRTViewport(props) {
return () => {
// remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentationFromToolGroup(toolGroupId);
segmentationService.removeSegmentationRepresentations(viewportId);
referencedDisplaySetRef.current = null;
};
}, [rtDisplaySet]);
const onStatusClick = useCallback(async () => {
// Before hydrating a RT and make it added to all viewports in the grid
// that share the same frameOfReferenceUID, we need to store the viewport grid
// presentation state, so that we can restore it after hydrating the RT. This is
// required if the user has changed the viewport (other viewport than RT viewport)
// presentation state (w/l and invert) and then opens the RT. If we don't store
// the presentation state, the viewport will be reset to the default presentation
storePresentationState();
const isHydrated = await hydrateRTDisplaySet({
rtDisplaySet,
viewportId,
});
setIsHydrated(isHydrated);
}, [hydrateRTDisplaySet, rtDisplaySet, storePresentationState, viewportId]);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
let childrenWithProps = null;
@@ -274,69 +306,45 @@ function OHIFCornerstoneRTViewport(props) {
});
}
const {
PatientID,
PatientName,
PatientSex,
PatientAge,
SliceThickness,
ManufacturerModelName,
StudyDate,
SeriesDescription,
SpacingBetweenSlices,
SeriesNumber,
} = referencedDisplaySetRef.current.metadata;
const onStatusClick = async () => {
// Before hydrating a RT and make it added to all viewports in the grid
// that share the same frameOfReferenceUID, we need to store the viewport grid
// presentation state, so that we can restore it after hydrating the RT. This is
// required if the user has changed the viewport (other viewport than RT viewport)
// presentation state (w/l and invert) and then opens the RT. If we don't store
// the presentation state, the viewport will be reset to the default presentation
storePresentationState();
const isHydrated = await _hydrateRTDisplaySet({
rtDisplaySet,
viewportId,
servicesManager,
});
setIsHydrated(isHydrated);
};
useEffect(() => {
viewportActionCornersService.addComponents([
{
viewportId,
id: 'viewportStatusComponent',
component: _getStatusComponent({
isHydrated,
onStatusClick,
}),
indexPriority: -100,
location: viewportActionCornersService.LOCATIONS.topLeft,
},
{
viewportId,
id: 'viewportActionArrowsComponent',
component: (
<ViewportActionArrows
key="actionArrows"
onArrowsClick={onSegmentChange}
className={
viewportId === activeViewportId ? 'visible' : 'invisible group-hover/pane:visible'
}
></ViewportActionArrows>
),
indexPriority: 0,
location: viewportActionCornersService.LOCATIONS.topRight,
},
]);
}, [
activeViewportId,
isHydrated,
onSegmentChange,
onStatusClick,
viewportActionCornersService,
viewportId,
]);
return (
<>
<ViewportActionBar
onDoubleClick={evt => {
evt.stopPropagation();
evt.preventDefault();
}}
onArrowsClick={onSegmentChange}
getStatusComponent={() => {
return _getStatusComponent({
isHydrated,
onStatusClick,
});
}}
studyData={{
label: viewportLabel,
useAltStyling: true,
studyDate: formatDate(StudyDate),
currentSeries: SeriesNumber,
seriesDescription: `RT Viewport ${SeriesDescription}`,
patientInformation: {
patientName: PatientName ? OHIF.utils.formatPN(PatientName.Alphabetic) : '',
patientSex: PatientSex || '',
patientAge: PatientAge || '',
MRN: PatientID || '',
thickness: SliceThickness ? `${SliceThickness.toFixed(2)}mm` : '',
spacing:
SpacingBetweenSlices !== undefined ? `${SpacingBetweenSlices.toFixed(2)}mm` : '',
scanner: ManufacturerModelName || '',
},
}}
/>
<div className="relative flex h-full w-full flex-row overflow-hidden">
{rtIsLoading && (
<LoadingIndicatorTotalPercent
@@ -358,11 +366,6 @@ OHIFCornerstoneRTViewport.propTypes = {
viewportId: PropTypes.string.isRequired,
dataSource: PropTypes.object,
children: PropTypes.node,
customProps: PropTypes.object,
};
OHIFCornerstoneRTViewport.defaultProps = {
customProps: {},
};
function _getReferencedDisplaySetMetadata(referencedDisplaySet) {
@@ -6,9 +6,6 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
let ToolTipMessage = null;
let StatusIcon = null;
const { t } = useTranslation('Common');
const loadStr = t('LOAD');
switch (isHydrated) {
case true:
StatusIcon = () => <Icon name="status-alert" />;
@@ -26,23 +23,28 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
ToolTipMessage = () => <div>Click LOAD to load RTSTRUCT.</div>;
}
const StatusArea = () => (
<div className="flex h-6 cursor-default text-sm leading-6 text-white">
<div className="bg-customgray-100 flex min-w-[45px] items-center rounded-l-xl rounded-r p-1">
<StatusIcon />
<span className="ml-1">RTSTRUCT</span>
</div>
{!isHydrated && (
<div
className="bg-primary-main hover:bg-primary-light ml-1 cursor-pointer rounded px-1.5 hover:text-black"
// Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none
onMouseUp={onStatusClick}
>
{loadStr}
const StatusArea = () => {
const { t } = useTranslation('Common');
const loadStr = t('LOAD');
return (
<div className="flex h-6 cursor-default text-sm leading-6 text-white">
<div className="bg-customgray-100 flex min-w-[45px] items-center rounded-l-xl rounded-r p-1">
<StatusIcon />
<span className="ml-1">RTSTRUCT</span>
</div>
)}
</div>
);
{!isHydrated && (
<div
className="bg-primary-main hover:bg-primary-light ml-1 cursor-pointer rounded px-1.5 hover:text-black"
// Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none
onMouseUp={onStatusClick}
>
{loadStr}
</div>
)}
</div>
);
};
return (
<>
@@ -45,10 +45,10 @@ module.exports = (env, argv) => {
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
// new MiniCssExtractPlugin({
// filename: `./dist/${outputName}.css`,
// chunkFilename: `./dist/${outputName}.css`,
// }),
new MiniCssExtractPlugin({
filename: `./dist/${outputName}.css`,
chunkFilename: `./dist/${outputName}.css`,
}),
],
});
};
File diff suppressed because it is too large. Load diff
@@ -1,5 +1,5 @@
module.exports = {
plugins: ['inline-react-svg', '@babel/plugin-proposal-class-properties'],
plugins: ['@babel/plugin-proposal-class-properties'],
env: {
test: {
presets: [
@@ -37,7 +37,7 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-hot-loader/babel'],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},
+14 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-seg",
"version": "3.7.0-beta.71",
"version": "3.9.1",
"description": "DICOM SEG read workflow",
"author": "OHIF",
"license": "MIT",
@@ -24,6 +24,8 @@
"yarn": ">=1.18.0"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:dicom-seg": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
@@ -31,19 +33,22 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.71",
"@ohif/extension-cornerstone": "3.7.0-beta.71",
"@ohif/extension-default": "3.7.0-beta.71",
"@ohif/i18n": "3.7.0-beta.71",
"@ohif/core": "3.9.1",
"@ohif/extension-cornerstone": "3.9.1",
"@ohif/extension-default": "3.9.1",
"@ohif/i18n": "3.9.1",
"prop-types": "^15.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^12.2.2",
"react-router": "^6.8.1",
"react-router-dom": "^6.8.1"
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.4",
"@cornerstonejs/core": "^2.2.4",
"@kitware/vtk.js": "32.1.0",
"react-color": "^2.19.3"
}
}
@@ -0,0 +1,379 @@
import dcmjs from 'dcmjs';
import { createReportDialogPrompt } from '@ohif/extension-default';
import { Types } from '@ohif/core';
import { cache, metaData } from '@cornerstonejs/core';
import {
segmentation as cornerstoneToolsSegmentation,
Enums as cornerstoneToolsEnums,
utilities,
} from '@cornerstonejs/tools';
import { adaptersRT, helpers, adaptersSEG } from '@cornerstonejs/adapters';
import { classes, DicomMetadataStore } from '@ohif/core';
import vtkImageMarchingSquares from '@kitware/vtk.js/Filters/General/ImageMarchingSquares';
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
const { segmentation: segmentationUtils } = utilities;
const { datasetToBlob } = dcmjs.data;
const getTargetViewport = ({ viewportId, viewportGridService }) => {
const { viewports, activeViewportId } = viewportGridService.getState();
const targetViewportId = viewportId || activeViewportId;
const viewport = viewports.get(targetViewportId);
return viewport;
};
const {
Cornerstone3D: {
Segmentation: { generateSegmentation },
},
} = adaptersSEG;
const {
Cornerstone3D: {
RTSS: { generateRTSSFromSegmentations },
},
} = adaptersRT;
const { downloadDICOMData } = helpers;
const commandsModule = ({
servicesManager,
extensionManager,
}: Types.Extensions.ExtensionParams): Types.Extensions.CommandsModule => {
const {
segmentationService,
uiDialogService,
displaySetService,
viewportGridService,
toolGroupService,
} = servicesManager.services as AppTypes.Services;
const actions = {
/**
* Loads segmentations for a specified viewport.
* The function prepares the viewport for rendering, then loads the segmentation details.
* Additionally, if the segmentation has scalar data, it is set for the corresponding label map volume.
*
* @param {Object} params - Parameters for the function.
* @param params.segmentations - Array of segmentations to be loaded.
* @param params.viewportId - the target viewport ID.
*
*/
loadSegmentationsForViewport: async ({ segmentations, viewportId }) => {
// Todo: handle adding more than one segmentation
const viewport = getTargetViewport({ viewportId, viewportGridService });
const displaySetInstanceUID = viewport.displaySetInstanceUIDs[0];
const segmentation = segmentations[0];
const segmentationId = segmentation.segmentationId;
const label = segmentation.config.label;
const segments = segmentation.config.segments;
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
await segmentationService.createLabelmapForDisplaySet(displaySet, {
segmentationId,
segments,
label,
});
segmentationService.addOrUpdateSegmentation(segmentation);
await segmentationService.addSegmentationRepresentation(viewport.viewportId, {
segmentationId,
});
return segmentationId;
},
/**
* Generates a segmentation from a given segmentation ID.
* This function retrieves the associated segmentation and
* its referenced volume, extracts label maps from the
* segmentation volume, and produces segmentation data
* alongside associated metadata.
*
* @param {Object} params - Parameters for the function.
* @param params.segmentationId - ID of the segmentation to be generated.
* @param params.options - Optional configuration for the generation process.
*
* @returns Returns the generated segmentation data.
*/
generateSegmentation: ({ segmentationId, options = {} }) => {
const segmentation = cornerstoneToolsSegmentation.state.getSegmentation(segmentationId);
const { imageIds } = segmentation.representationData.Labelmap;
const segImages = imageIds.map(imageId => cache.getImage(imageId));
const referencedImages = segImages.map(image => cache.getImage(image.referencedImageId));
const labelmaps2D = [];
let z = 0;
for (const segImage of segImages) {
const segmentsOnLabelmap = new Set();
const pixelData = segImage.getPixelData();
const { rows, columns } = segImage;
// Use a single pass through the pixel data
for (let i = 0; i < pixelData.length; i++) {
const segment = pixelData[i];
if (segment !== 0) {
segmentsOnLabelmap.add(segment);
}
}
labelmaps2D[z++] = {
segmentsOnLabelmap: Array.from(segmentsOnLabelmap),
pixelData,
rows,
columns,
};
}
const allSegmentsOnLabelmap = labelmaps2D.map(labelmap => labelmap.segmentsOnLabelmap);
const labelmap3D = {
segmentsOnLabelmap: Array.from(new Set(allSegmentsOnLabelmap.flat())),
metadata: [],
labelmaps2D,
};
const segmentationInOHIF = segmentationService.getSegmentation(segmentationId);
const representations = segmentationService.getRepresentationsForSegmentation(segmentationId);
Object.entries(segmentationInOHIF.segments).forEach(([segmentIndex, segment]) => {
// segmentation service already has a color for each segment
if (!segment) {
return;
}
const { label } = segment;
const firstRepresentation = representations[0];
const color = segmentationService.getSegmentColor(
firstRepresentation.viewportId,
segmentationId,
segment.segmentIndex
);
const RecommendedDisplayCIELabValue = dcmjs.data.Colors.rgb2DICOMLAB(
color.slice(0, 3).map(value => value / 255)
).map(value => Math.round(value));
const segmentMetadata = {
SegmentNumber: segmentIndex.toString(),
SegmentLabel: label,
SegmentAlgorithmType: segment?.algorithmType || 'MANUAL',
SegmentAlgorithmName: segment?.algorithmName || 'OHIF Brush',
RecommendedDisplayCIELabValue,
SegmentedPropertyCategoryCodeSequence: {
CodeValue: 'T-D0050',
CodingSchemeDesignator: 'SRT',
CodeMeaning: 'Tissue',
},
SegmentedPropertyTypeCodeSequence: {
CodeValue: 'T-D0050',
CodingSchemeDesignator: 'SRT',
CodeMeaning: 'Tissue',
},
};
labelmap3D.metadata[segmentIndex] = segmentMetadata;
});
const generatedSegmentation = generateSegmentation(
referencedImages,
labelmap3D,
metaData,
options
);
return generatedSegmentation;
},
/**
* Downloads a segmentation based on the provided segmentation ID.
* This function retrieves the associated segmentation and
* uses it to generate the corresponding DICOM dataset, which
* is then downloaded with an appropriate filename.
*
* @param {Object} params - Parameters for the function.
* @param params.segmentationId - ID of the segmentation to be downloaded.
*
*/
downloadSegmentation: ({ segmentationId }) => {
const segmentationInOHIF = segmentationService.getSegmentation(segmentationId);
const generatedSegmentation = actions.generateSegmentation({
segmentationId,
});
downloadDICOMData(generatedSegmentation.dataset, `${segmentationInOHIF.label}`);
},
/**
* Stores a segmentation based on the provided segmentationId into a specified data source.
* The SeriesDescription is derived from user input or defaults to the segmentation label,
* and in its absence, defaults to 'Research Derived Series'.
*
* @param {Object} params - Parameters for the function.
* @param params.segmentationId - ID of the segmentation to be stored.
* @param params.dataSource - Data source where the generated segmentation will be stored.
*
* @returns {Object|void} Returns the naturalized report if successfully stored,
* otherwise throws an error.
*/
storeSegmentation: async ({ segmentationId, dataSource }) => {
const promptResult = await createReportDialogPrompt(uiDialogService, {
extensionManager,
});
if (promptResult.action !== 1 && !promptResult.value) {
return;
}
const segmentation = segmentationService.getSegmentation(segmentationId);
if (!segmentation) {
throw new Error('No segmentation found');
}
const { label } = segmentation;
const SeriesDescription = promptResult.value || label || 'Research Derived Series';
const generatedData = actions.generateSegmentation({
segmentationId,
options: {
SeriesDescription,
},
});
if (!generatedData || !generatedData.dataset) {
throw new Error('Error during segmentation generation');
}
const { dataset: naturalizedReport } = generatedData;
await dataSource.store.dicom(naturalizedReport);
// The "Mode" route listens for DicomMetadataStore changes
// When a new instance is added, it listens and
// automatically calls makeDisplaySets
// add the information for where we stored it to the instance as well
naturalizedReport.wadoRoot = dataSource.getConfig().wadoRoot;
DicomMetadataStore.addInstances([naturalizedReport], true);
return naturalizedReport;
},
/**
* Converts segmentations into RTSS for download.
* This sample function retrieves all segentations and passes to
* cornerstone tool adapter to convert to DICOM RTSS format. It then
* converts dataset to downloadable blob.
*
*/
downloadRTSS: ({ segmentationId }) => {
const segmentations = segmentationService.getSegmentation(segmentationId);
const vtkUtils = {
vtkImageMarchingSquares,
vtkDataArray,
vtkImageData,
};
const RTSS = generateRTSSFromSegmentations(
segmentations,
classes.MetadataProvider,
DicomMetadataStore,
cache,
cornerstoneToolsEnums,
vtkUtils
);
try {
const reportBlob = datasetToBlob(RTSS);
//Create a URL for the binary.
const objectUrl = URL.createObjectURL(reportBlob);
window.location.assign(objectUrl);
} catch (e) {
console.warn(e);
}
},
setBrushSize: ({ value, toolNames }) => {
const brushSize = Number(value);
toolGroupService.getToolGroupIds()?.forEach(toolGroupId => {
if (toolNames?.length === 0) {
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize);
} else {
toolNames?.forEach(toolName => {
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize, toolName);
});
}
});
},
setThresholdRange: ({
value,
toolNames = ['ThresholdCircularBrush', 'ThresholdSphereBrush'],
}) => {
toolGroupService.getToolGroupIds()?.forEach(toolGroupId => {
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
toolNames?.forEach(toolName => {
toolGroup.setToolConfiguration(toolName, {
strategySpecificConfiguration: {
THRESHOLD: {
threshold: value,
},
},
});
});
});
},
};
const definitions = {
/**
* Obsolete?
*/
loadSegmentationDisplaySetsForViewport: {
commandFn: actions.loadSegmentationDisplaySetsForViewport,
},
/**
* Obsolete?
*/
loadSegmentationsForViewport: {
commandFn: actions.loadSegmentationsForViewport,
},
generateSegmentation: {
commandFn: actions.generateSegmentation,
},
downloadSegmentation: {
commandFn: actions.downloadSegmentation,
},
storeSegmentation: {
commandFn: actions.storeSegmentation,
},
downloadRTSS: {
commandFn: actions.downloadRTSS,
},
setBrushSize: {
commandFn: actions.setBrushSize,
},
setThresholdRange: {
commandFn: actions.setThresholdRange,
},
};
return {
actions,
definitions,
defaultContext: 'SEGMENTATION',
};
};
export default commandsModule;
@@ -20,6 +20,17 @@ const segProtocol: Types.HangingProtocol.Protocol = {
viewportType: 'stack',
toolGroupId: 'default',
allowUnmatchedView: true,
syncGroups: [
{
type: 'hydrateseg',
id: 'sameFORId',
source: true,
target: true,
// options: {
// matchingRules: ['sameFOR'],
// },
},
],
},
displaySets: [
{
@@ -52,7 +63,20 @@ const segProtocol: Types.HangingProtocol.Protocol = {
},
viewports: [
{
viewportOptions: { allowUnmatchedView: true },
viewportOptions: {
allowUnmatchedView: true,
syncGroups: [
{
type: 'hydrateseg',
id: 'sameFORId',
source: true,
target: true,
// options: {
// matchingRules: ['sameFOR'],
// },
},
],
},
displaySets: [
{
id: 'segDisplaySetId',
@@ -1,5 +1,6 @@
import { utils } from '@ohif/core';
import { metaData, cache, triggerEvent, eventTarget } from '@cornerstonejs/core';
import { metaData, triggerEvent, eventTarget } from '@cornerstonejs/core';
import { CONSTANTS, segmentation as cstSegmentation } from '@cornerstonejs/tools';
import { adaptersSEG, Enums } from '@cornerstonejs/adapters';
import { SOPClassHandlerId } from './id';
@@ -7,9 +8,13 @@ import { dicomlabToRGB } from './utils/dicomlabToRGB';
const sopClassUids = ['1.2.840.10008.5.1.4.1.1.66.4'];
let loadPromises = {};
const loadPromises = {};
function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
function _getDisplaySetsFromSeries(
instances,
servicesManager: AppTypes.ServicesManager,
extensionManager
) {
const instance = instances[0];
const {
@@ -57,35 +62,40 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
const referencedSeriesSequence = instance.ReferencedSeriesSequence;
if (!referencedSeriesSequence) {
throw new Error('ReferencedSeriesSequence is missing for the SEG');
console.error('ReferencedSeriesSequence is missing for the SEG');
return;
}
const referencedSeries = referencedSeriesSequence[0];
const referencedSeries = referencedSeriesSequence[0] || referencedSeriesSequence;
displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
const { displaySetService } = servicesManager.services;
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
displaySet.referencedSeriesInstanceUID
);
displaySet.getReferenceDisplaySet = () => {
const { displaySetService } = servicesManager.services;
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
displaySet.referencedSeriesInstanceUID
const referencedDisplaySet = referencedDisplaySets[0];
if (!referencedDisplaySet) {
// subscribe to display sets added which means at some point it will be available
const { unsubscribe } = displaySetService.subscribe(
displaySetService.EVENTS.DISPLAY_SETS_ADDED,
({ displaySetsAdded }) => {
// here we can also do a little bit of search, since sometimes DICOM SEG
// does not contain the referenced display set uid , and we can just
// see which of the display sets added is more similar and assign it
// to the referencedDisplaySet
const addedDisplaySet = displaySetsAdded[0];
if (addedDisplaySet.SeriesInstanceUID === displaySet.referencedSeriesInstanceUID) {
displaySet.referencedDisplaySetInstanceUID = addedDisplaySet.displaySetInstanceUID;
unsubscribe();
}
}
);
if (!referencedDisplaySets || referencedDisplaySets.length === 0) {
throw new Error('Referenced DisplaySet is missing for the SEG');
}
const referencedDisplaySet = referencedDisplaySets[0];
} else {
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
// Todo: this needs to be able to work with other reference volumes (other than streaming) such as nifti, etc.
displaySet.referencedVolumeURI = referencedDisplaySet.displaySetInstanceUID;
const referencedVolumeId = `cornerstoneStreamingImageVolume:${displaySet.referencedVolumeURI}`;
displaySet.referencedVolumeId = referencedVolumeId;
return referencedDisplaySet;
};
}
displaySet.load = async ({ headers }) =>
await _load(displaySet, servicesManager, extensionManager, headers);
@@ -93,14 +103,19 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
return [displaySet];
}
function _load(segDisplaySet, servicesManager, extensionManager, headers) {
function _load(
segDisplaySet,
servicesManager: AppTypes.ServicesManager,
extensionManager,
headers
) {
const { SOPInstanceUID } = segDisplaySet;
const { segmentationService } = servicesManager.services;
if (
(segDisplaySet.loading || segDisplaySet.isLoaded) &&
loadPromises[SOPInstanceUID] &&
_segmentationExists(segDisplaySet, segmentationService)
_segmentationExists(segDisplaySet)
) {
return loadPromises[SOPInstanceUID];
}
@@ -111,17 +126,21 @@ function _load(segDisplaySet, servicesManager, extensionManager, headers) {
// and also return the same promise to any other callers.
loadPromises[SOPInstanceUID] = new Promise(async (resolve, reject) => {
if (!segDisplaySet.segments || Object.keys(segDisplaySet.segments).length === 0) {
await _loadSegments({
extensionManager,
servicesManager,
segDisplaySet,
headers,
});
try {
await _loadSegments({
extensionManager,
servicesManager,
segDisplaySet,
headers,
});
} catch (e) {
segDisplaySet.loading = false;
return reject(e);
}
}
const suppressEvents = true;
segmentationService
.createSegmentationForSEGDisplaySet(segDisplaySet, null, suppressEvents)
.createSegmentationForSEGDisplaySet(segDisplaySet)
.then(() => {
segDisplaySet.loading = false;
resolve();
@@ -135,30 +154,35 @@ function _load(segDisplaySet, servicesManager, extensionManager, headers) {
return loadPromises[SOPInstanceUID];
}
async function _loadSegments({ extensionManager, servicesManager, segDisplaySet, headers }) {
async function _loadSegments({
extensionManager,
servicesManager,
segDisplaySet,
headers,
}: withAppTypes) {
const utilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.utilityModule.common'
);
const { segmentationService } = servicesManager.services;
const { segmentationService, uiNotificationService } = servicesManager.services;
const { dicomLoaderService } = utilityModule.exports;
const arrayBuffer = await dicomLoaderService.findDicomDataPromise(segDisplaySet, null, headers);
const cachedReferencedVolume = cache.getVolume(segDisplaySet.referencedVolumeId);
const referencedDisplaySet = servicesManager.services.displaySetService.getDisplaySetByUID(
segDisplaySet.referencedDisplaySetInstanceUID
);
if (!cachedReferencedVolume) {
throw new Error(
'Referenced Volume is missing for the SEG, and stack viewport SEG is not supported yet'
);
if (!referencedDisplaySet) {
throw new Error('referencedDisplaySet is missing for SEG');
}
const { imageIds } = cachedReferencedVolume;
const { instances: images } = referencedDisplaySet;
const imageIds = images.map(({ imageId }) => imageId);
// Todo: what should be defaults here
const tolerance = 0.001;
const skipOverlapping = true;
eventTarget.addEventListener(Enums.Events.SEGMENTATION_LOAD_PROGRESS, evt => {
const { percentComplete } = evt.detail;
segmentationService._broadcastEvent(segmentationService.EVENTS.SEGMENT_LOADING_COMPLETE, {
@@ -173,18 +197,45 @@ async function _loadSegments({ extensionManager, servicesManager, segDisplaySet,
{ skipOverlapping, tolerance, eventTarget, triggerEvent }
);
let usedRecommendedDisplayCIELabValue = true;
results.segMetadata.data.forEach((data, i) => {
if (i > 0) {
data.rgba = dicomlabToRGB(data.RecommendedDisplayCIELabValue);
data.rgba = data.RecommendedDisplayCIELabValue;
if (data.rgba) {
data.rgba = dicomlabToRGB(data.rgba);
} else {
usedRecommendedDisplayCIELabValue = false;
data.rgba = CONSTANTS.COLOR_LUT[i % CONSTANTS.COLOR_LUT.length];
}
}
});
if (results.overlappingSegments) {
uiNotificationService.show({
title: 'Overlapping Segments',
message:
'Unsupported overlapping segments detected, segmentation rendering results may be incorrect.',
type: 'warning',
});
}
if (!usedRecommendedDisplayCIELabValue) {
// Display a notification about the non-utilization of RecommendedDisplayCIELabValue
uiNotificationService.show({
title: 'DICOM SEG import',
message:
'RecommendedDisplayCIELabValue not found for one or more segments. The default color was used instead.',
type: 'warning',
duration: 5000,
});
}
Object.assign(segDisplaySet, results);
}
function _segmentationExists(segDisplaySet, segmentationService) {
// This should be abstracted with the CornerstoneCacheService
return segmentationService.getSegmentation(segDisplaySet.displaySetInstanceUID);
function _segmentationExists(segDisplaySet) {
return cstSegmentation.state.getSegmentation(segDisplaySet.displaySetInstanceUID);
}
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
@@ -0,0 +1,57 @@
export function getToolbarModule({ servicesManager }: withAppTypes) {
const { segmentationService, toolbarService, toolGroupService } = servicesManager.services;
return [
{
name: 'evaluate.cornerstone.segmentation',
evaluate: ({ viewportId, button, toolNames, disabledText }) => {
// Todo: we need to pass in the button section Id since we are kind of
// forcing the button to have black background since initially
// it is designed for the toolbox not the toolbar on top
// we should then branch the buttonSectionId to have different styles
const segmentations = segmentationService.getSegmentationRepresentations(viewportId);
if (!segmentations?.length) {
return {
disabled: true,
className: '!text-common-bright !bg-black opacity-50',
disabledText: disabledText ?? 'No segmentations available',
};
}
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
if (!toolGroup) {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
disabledText: disabledText ?? 'Not available on the current viewport',
};
}
const toolName = toolbarService.getToolNameForButton(button);
if (!toolGroup.hasTool(toolName) && !toolNames) {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
disabledText: disabledText ?? 'Not available on the current viewport',
};
}
const isPrimaryActive = toolNames
? toolNames.includes(toolGroup.getActivePrimaryMouseButtonTool())
: toolGroup.getActivePrimaryMouseButtonTool() === toolName;
return {
disabled: false,
className: isPrimaryActive
? '!text-black !bg-primary-light hover:bg-primary-light hover-text-black hover:cursor-pointer'
: '!text-common-bright !bg-black hover:bg-primary-light hover:cursor-pointer hover:text-black',
// Todo: isActive right now is used for nested buttons where the primary
// button needs to be fully rounded (vs partial rounded) when active
// otherwise it does not have any other use
isActive: isPrimaryActive,
};
},
},
];
}
+5 -35
View File
@@ -1,12 +1,10 @@
import { id } from './id';
import React from 'react';
import { Types } from '@ohif/core';
import getSopClassHandlerModule from './getSopClassHandlerModule';
import PanelSegmentation from './panels/PanelSegmentation';
import getHangingProtocolModule from './getHangingProtocolModule';
import hydrateSEGDisplaySet from './utils/_hydrateSEG';
import getCommandsModule from './commandsModule';
import { getToolbarModule } from './getToolbarModule';
const Component = React.lazy(() => {
return import(/* webpackPrefetch: true */ './viewports/OHIFCornerstoneSEGViewport');
@@ -29,36 +27,9 @@ const extension = {
* You ID can be anything you want, but it should be unique.
*/
id,
/**
* PanelModule should provide a list of panels that will be available in OHIF
* for Modes to consume and render. Each panel is defined by a {name,
* iconName, iconLabel, label, component} object. Example of a panel module
* is the StudyBrowserPanel that is provided by the default extension in OHIF.
*/
getPanelModule: ({ servicesManager, commandsManager, extensionManager }): Types.Panel[] => {
const wrappedPanelSegmentation = () => {
return (
<PanelSegmentation
commandsManager={commandsManager}
servicesManager={servicesManager}
extensionManager={extensionManager}
/>
);
};
return [
{
name: 'panelSegmentation',
iconName: 'tab-segmentation',
iconLabel: 'Segmentation',
label: 'Segmentation',
component: wrappedPanelSegmentation,
},
];
},
getViewportModule({ servicesManager, extensionManager }) {
getCommandsModule,
getToolbarModule,
getViewportModule({ servicesManager, extensionManager, commandsManager }) {
const ExtendedOHIFCornerstoneSEGViewport = props => {
return (
<OHIFCornerstoneSEGViewport
@@ -83,4 +54,3 @@ const extension = {
};
export default extension;
export { hydrateSEGDisplaySet };
@@ -1,240 +0,0 @@
import React, { useEffect, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { SegmentationGroupTable } from '@ohif/ui';
import callInputDialog from './callInputDialog';
import { useAppConfig } from '@state';
import { useTranslation } from 'react-i18next';
export default function PanelSegmentation({ servicesManager, commandsManager }) {
const { segmentationService, uiDialogService } = servicesManager.services;
const [appConfig] = useAppConfig();
const disableEditing = appConfig?.disableEditing;
const { t } = useTranslation('PanelSegmentation');
const [selectedSegmentationId, setSelectedSegmentationId] = useState(null);
const [segmentationConfiguration, setSegmentationConfiguration] = useState(
segmentationService.getConfiguration()
);
const [segmentations, setSegmentations] = useState(() => segmentationService.getSegmentations());
const [isMinimized, setIsMinimized] = useState({});
const onToggleMinimizeSegmentation = useCallback(
id => {
setIsMinimized(prevState => ({
...prevState,
[id]: !prevState[id],
}));
},
[setIsMinimized]
);
// Only expand the last segmentation added to the list and collapse the rest
useEffect(() => {
const lastSegmentationId = segmentations[segmentations.length - 1]?.id;
if (lastSegmentationId) {
setIsMinimized(prevState => ({
...prevState,
[lastSegmentationId]: false,
}));
}
}, [segmentations, setIsMinimized]);
useEffect(() => {
// ~~ Subscription
const added = segmentationService.EVENTS.SEGMENTATION_ADDED;
const updated = segmentationService.EVENTS.SEGMENTATION_UPDATED;
const removed = segmentationService.EVENTS.SEGMENTATION_REMOVED;
const subscriptions = [];
[added, updated, removed].forEach(evt => {
const { unsubscribe } = segmentationService.subscribe(evt, () => {
const segmentations = segmentationService.getSegmentations();
setSegmentations(segmentations);
setSegmentationConfiguration(segmentationService.getConfiguration());
});
subscriptions.push(unsubscribe);
});
return () => {
subscriptions.forEach(unsub => {
unsub();
});
};
}, []);
const onSegmentationClick = (segmentationId: string) => {
segmentationService.setActiveSegmentationForToolGroup(segmentationId);
};
const onSegmentationDelete = (segmentationId: string) => {
segmentationService.remove(segmentationId);
};
const getToolGroupIds = segmentationId => {
const toolGroupIds = segmentationService.getToolGroupIdsWithSegmentation(segmentationId);
return toolGroupIds;
};
const onSegmentClick = (segmentationId, segmentIndex) => {
segmentationService.setActiveSegmentForSegmentation(segmentationId, segmentIndex);
const toolGroupIds = getToolGroupIds(segmentationId);
toolGroupIds.forEach(toolGroupId => {
// const toolGroupId =
segmentationService.setActiveSegmentationForToolGroup(segmentationId, toolGroupId);
segmentationService.jumpToSegmentCenter(segmentationId, segmentIndex, toolGroupId);
});
};
const onSegmentEdit = (segmentationId, segmentIndex) => {
const segmentation = segmentationService.getSegmentation(segmentationId);
const segment = segmentation.segments[segmentIndex];
const { label } = segment;
callInputDialog(uiDialogService, label, (label, actionId) => {
if (label === '') {
return;
}
segmentationService.setSegmentLabelForSegmentation(segmentationId, segmentIndex, label);
});
};
const onSegmentationEdit = segmentationId => {
const segmentation = segmentationService.getSegmentation(segmentationId);
const { label } = segmentation;
callInputDialog(uiDialogService, label, (label, actionId) => {
if (label === '') {
return;
}
segmentationService.addOrUpdateSegmentation(
{
id: segmentationId,
label,
},
false, // suppress event
true // notYetUpdatedAtSource
);
});
};
const onSegmentColorClick = (segmentationId, segmentIndex) => {
// Todo: Implement color picker later
return;
};
const onSegmentDelete = (segmentationId, segmentIndex) => {
// segmentationService.removeSegmentFromSegmentation(
// segmentationId,
// segmentIndex
// );
console.warn('not implemented yet');
};
const onToggleSegmentVisibility = (segmentationId, segmentIndex) => {
const segmentation = segmentationService.getSegmentation(segmentationId);
const segmentInfo = segmentation.segments[segmentIndex];
const isVisible = !segmentInfo.isVisible;
const toolGroupIds = getToolGroupIds(segmentationId);
// Todo: right now we apply the visibility to all tool groups
toolGroupIds.forEach(toolGroupId => {
segmentationService.setSegmentVisibility(
segmentationId,
segmentIndex,
isVisible,
toolGroupId
);
});
};
const onToggleSegmentationVisibility = segmentationId => {
segmentationService.toggleSegmentationVisibility(segmentationId);
};
const _setSegmentationConfiguration = useCallback(
(segmentationId, key, value) => {
segmentationService.setConfiguration({
segmentationId,
[key]: value,
});
},
[segmentationService]
);
return (
<div className="mt-1 flex min-h-0 flex-auto flex-col justify-between">
{/* show segmentation table */}
{segmentations?.length ? (
<SegmentationGroupTable
title={t('Segmentations')}
showAddSegmentation={false}
segmentations={segmentations}
isMinimized={isMinimized}
activeSegmentationId={selectedSegmentationId || ''}
onSegmentationClick={onSegmentationClick}
onSegmentationDelete={onSegmentationDelete}
onSegmentationEdit={onSegmentationEdit}
onSegmentClick={onSegmentClick}
onSegmentEdit={onSegmentEdit}
disableEditing={disableEditing}
onSegmentColorClick={onSegmentColorClick}
onSegmentDelete={onSegmentDelete}
onToggleSegmentVisibility={onToggleSegmentVisibility}
onToggleSegmentationVisibility={onToggleSegmentationVisibility}
onToggleMinimizeSegmentation={onToggleMinimizeSegmentation}
segmentationConfig={{ initialConfig: segmentationConfiguration }}
setRenderOutline={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'renderOutline', value)
}
setOutlineOpacityActive={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'outlineOpacity', value)
}
setRenderFill={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'renderFill', value)
}
setRenderInactiveSegmentations={value =>
_setSegmentationConfiguration(
selectedSegmentationId,
'renderInactiveSegmentations',
value
)
}
setOutlineWidthActive={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'outlineWidthActive', value)
}
setFillAlpha={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'fillAlpha', value)
}
setFillAlphaInactive={value =>
_setSegmentationConfiguration(selectedSegmentationId, 'fillAlphaInactive', value)
}
/>
) : null}
</div>
);
}
PanelSegmentation.propTypes = {
commandsManager: PropTypes.shape({
runCommand: PropTypes.func.isRequired,
}),
servicesManager: PropTypes.shape({
services: PropTypes.shape({
segmentationService: PropTypes.shape({
getSegmentation: PropTypes.func.isRequired,
getSegmentations: PropTypes.func.isRequired,
toggleSegmentationVisibility: PropTypes.func.isRequired,
subscribe: PropTypes.func.isRequired,
EVENTS: PropTypes.object.isRequired,
}).isRequired,
}).isRequired,
}).isRequired,
};
@@ -1,62 +0,0 @@
import React from 'react';
import { Input, Dialog, ButtonEnums } from '@ohif/ui';
function callInputDialog(uiDialogService, label, callback) {
const dialogId = 'enter-segment-label';
const onSubmitHandler = ({ action, value }) => {
switch (action.id) {
case 'save':
callback(value.label, action.id);
break;
case 'cancel':
callback('', action.id);
break;
}
uiDialogService.dismiss({ id: dialogId });
};
if (uiDialogService) {
uiDialogService.create({
id: dialogId,
centralize: true,
isDraggable: false,
showOverlay: true,
content: Dialog,
contentProps: {
title: 'Segment',
value: { label },
noCloseButton: true,
onClose: () => uiDialogService.dismiss({ id: dialogId }),
actions: [
{ id: 'cancel', text: 'Cancel', type: ButtonEnums.type.secondary },
{ id: 'save', text: 'Confirm', type: ButtonEnums.type.primary },
],
onSubmit: onSubmitHandler,
body: ({ value, setValue }) => {
return (
<Input
label="Enter the segment label"
labelClassName="text-white text-[14px] leading-[1.2]"
autoFocus
className="border-primary-main bg-black"
type="text"
value={value.label}
onChange={event => {
event.persist();
setValue(value => ({ ...value, label: event.target.value }));
}}
onKeyPress={event => {
if (event.key === 'Enter') {
onSubmitHandler({ value, action: { id: 'save' } });
}
}}
/>
);
},
},
});
}
}
export default callInputDialog;
@@ -1,33 +0,0 @@
// Todo: use defaults in cs3d
const initialState = {
renderOutline: true,
renderFill: true,
outlineOpacity: 0.9,
outlineWidth: 3,
fillOpacity: 0.9,
fillOpacityInactive: 0.8,
renderInactiveSegmentations: true,
};
const reducer = (state, action) => {
switch (action.type) {
case 'RENDER_OUTLINE':
return { ...state, renderOutline: action.payload.value };
case 'RENDER_FILL':
return { ...state, renderFill: action.payload.value };
case 'SET_OUTLINE_OPACITY':
return { ...state, outlineOpacity: action.payload.value };
case 'SET_OUTLINE_WIDTH':
return { ...state, outlineWidth: action.payload.value };
case 'SET_FILL_OPACITY':
return { ...state, fillOpacity: action.payload.value };
case 'SET_FILL_OPACITY_INACTIVE':
return { ...state, fillOpacityInactive: action.payload.value };
case 'RENDER_INACTIVE_SEGMENTATIONS':
return { ...state, renderInactiveSegmentations: action.payload.value };
default:
return state;
}
};
export { initialState, reducer };
@@ -0,0 +1,4 @@
export enum SegmentationPanelMode {
Expanded = 'expanded',
Dropdown = 'dropdown',
}
@@ -1,73 +0,0 @@
async function _hydrateSEGDisplaySet({
segDisplaySet,
viewportId: targetViewportId,
servicesManager,
}) {
const { segmentationService, hangingProtocolService, viewportGridService } =
servicesManager.services;
const displaySetInstanceUID = segDisplaySet.referencedDisplaySetInstanceUID;
let segmentationId = null;
// We need the hydration to notify panels about the new segmentation added
const suppressEvents = false;
segmentationId = await segmentationService.createSegmentationForSEGDisplaySet(
segDisplaySet,
segmentationId,
suppressEvents
);
segmentationService.hydrateSegmentation(segDisplaySet.displaySetInstanceUID);
const { viewports } = viewportGridService.getState();
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
targetViewportId,
displaySetInstanceUID
);
// Todo: fix this after we have a better way for stack viewport segmentations
// check every viewport in the viewports to see if the displaySetInstanceUID
// is being displayed, if so we need to update the viewport to use volume viewport
// (if already is not using it) since Cornerstone3D currently only supports
// volume viewport for segmentation
viewports.forEach((viewport, viewportId) => {
if (targetViewportId === viewportId) {
return;
}
const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(
viewport.displaySetInstanceUIDs,
segDisplaySet.displaySetInstanceUID
);
if (shouldDisplaySeg) {
updatedViewports.push({
viewportId,
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
viewportOptions: {
// Note: This is a hack to get the grid to re-render the OHIFCornerstoneViewport component
// Used for segmentation hydration right now, since the logic to decide whether
// a viewport needs to render a segmentation lives inside the CornerstoneViewportService
// so we need to re-render (force update via change of the needsRerendering) so that React
// does the diffing and decides we should render this again (although the id and element has not changed)
// so that the CornerstoneViewportService can decide whether to render the segmentation or not.
needsRerendering: true,
initialImageOptions: {
preset: 'middle',
},
},
});
}
});
// Do the entire update at once
viewportGridService.setDisplaySetsForViewports(updatedViewports);
return true;
}
export default _hydrateSEGDisplaySet;
@@ -1,7 +1,7 @@
function createSEGToolGroupAndAddTools(ToolGroupService, customizationService, toolGroupId) {
const { tools } = customizationService.get('cornerstone.overlayViewportTools') ?? {};
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {});
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools);
}
export default createSEGToolGroupAndAddTools;
@@ -1,5 +1,4 @@
import { ButtonEnums } from '@ohif/ui';
import hydrateSEGDisplaySet from './_hydrateSEG';
const RESPONSE = {
NO_NEVER: -1,
@@ -7,24 +6,35 @@ const RESPONSE = {
HYDRATE_SEG: 5,
};
function promptHydrateSEG({ servicesManager, segDisplaySet, viewportId, preHydrateCallbacks }) {
function promptHydrateSEG({
servicesManager,
segDisplaySet,
viewportId,
preHydrateCallbacks,
hydrateCallback,
}: withAppTypes) {
const { uiViewportDialogService } = servicesManager.services;
const extensionManager = servicesManager._extensionManager;
const appConfig = extensionManager._appConfig;
return new Promise(async function (resolve, reject) {
const promptResult = await _askHydrate(uiViewportDialogService, viewportId);
const promptResult = appConfig?.disableConfirmationPrompts
? RESPONSE.HYDRATE_SEG
: await _askHydrate(uiViewportDialogService, viewportId);
if (promptResult === RESPONSE.HYDRATE_SEG) {
preHydrateCallbacks?.forEach(callback => {
callback();
});
const isHydrated = await hydrateSEGDisplaySet({
segDisplaySet,
viewportId,
servicesManager,
});
window.setTimeout(async () => {
const isHydrated = await hydrateCallback({
segDisplaySet,
viewportId,
});
resolve(isHydrated);
resolve(isHydrated);
}, 0);
}
});
}
@@ -34,11 +44,13 @@ function _askHydrate(uiViewportDialogService, viewportId) {
const message = 'Do you want to open this Segmentation?';
const actions = [
{
id: 'no-hydrate',
type: ButtonEnums.type.secondary,
text: 'No',
value: RESPONSE.CANCEL,
},
{
id: 'yes-hydrate',
type: ButtonEnums.type.primary,
text: 'Yes',
value: RESPONSE.HYDRATE_SEG,
@@ -59,6 +71,11 @@ function _askHydrate(uiViewportDialogService, viewportId) {
uiViewportDialogService.hide();
resolve(RESPONSE.CANCEL);
},
onKeyPress: event => {
if (event.key === 'Enter') {
onSubmit(RESPONSE.HYDRATE_SEG);
}
},
});
});
}
@@ -1,22 +1,18 @@
import PropTypes from 'prop-types';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import OHIF, { utils } from '@ohif/core';
import { LoadingIndicatorTotalPercent, useViewportGrid, ViewportActionBar } from '@ohif/ui';
import { LoadingIndicatorTotalPercent, useViewportGrid, ViewportActionArrows } from '@ohif/ui';
import createSEGToolGroupAndAddTools from '../utils/initSEGToolGroup';
import promptHydrateSEG from '../utils/promptHydrateSEG';
import hydrateSEGDisplaySet from '../utils/_hydrateSEG';
import _getStatusComponent from './_getStatusComponent';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
const { formatDate } = utils;
const SEG_TOOLGROUP_BASE_NAME = 'SEGToolGroup';
function OHIFCornerstoneSEGViewport(props) {
function OHIFCornerstoneSEGViewport(props: withAppTypes) {
const {
children,
displaySets,
viewportOptions,
viewportLabel,
servicesManager,
extensionManager,
commandsManager,
@@ -29,8 +25,8 @@ function OHIFCornerstoneSEGViewport(props) {
displaySetService,
toolGroupService,
segmentationService,
uiNotificationService,
customizationService,
viewportActionCornersService,
} = servicesManager.services;
const toolGroupId = `${SEG_TOOLGROUP_BASE_NAME}-${viewportId}`;
@@ -41,7 +37,6 @@ function OHIFCornerstoneSEGViewport(props) {
}
const segDisplaySet = displaySets[0];
const [viewportGrid, viewportGridService] = useViewportGrid();
// States
@@ -65,7 +60,11 @@ function OHIFCornerstoneSEGViewport(props) {
const { viewports, activeViewportId } = viewportGrid;
const referencedDisplaySet = segDisplaySet.getReferenceDisplaySet();
const referencedDisplaySetInstanceUID = segDisplaySet.referencedDisplaySetInstanceUID;
const referencedDisplaySet = displaySetService.getDisplaySetByUID(
referencedDisplaySetInstanceUID
);
const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata(
referencedDisplaySet,
segDisplaySet
@@ -102,29 +101,29 @@ function OHIFCornerstoneSEGViewport(props) {
'@ohif/extension-cornerstone.viewportModule.cornerstone'
);
const { displaySet: referencedDisplaySet } = referencedDisplaySetRef.current;
// Todo: jump to the center of the first segment
return (
<Component
{...props}
displaySets={[referencedDisplaySet, segDisplaySet]}
displaySets={[segDisplaySet]}
viewportOptions={{
viewportType: 'volume',
viewportType: viewportOptions.viewportType,
toolGroupId: toolGroupId,
orientation: viewportOptions.orientation,
viewportId: viewportOptions.viewportId,
presentationIds: viewportOptions.presentationIds,
}}
onElementEnabled={evt => {
props.onElementEnabled?.(evt);
onElementEnabled(evt);
}}
onElementEnabled={onElementEnabled}
onElementDisabled={onElementDisabled}
// initialImageIndex={initialImageIndex}
></Component>
);
}, [viewportId, segDisplaySet, toolGroupId]);
const onSegmentChange = useCallback(
direction => {
direction = direction === 'left' ? -1 : 1;
const segmentationId = segDisplaySet.displaySetInstanceUID;
const segmentation = segmentationService.getSegmentation(segmentationId);
@@ -142,12 +141,35 @@ function OHIFCornerstoneSEGViewport(props) {
newSelectedSegmentIndex = numberOfSegments - 1;
}
segmentationService.jumpToSegmentCenter(segmentationId, newSelectedSegmentIndex, toolGroupId);
segmentationService.jumpToSegmentCenter(segmentationId, newSelectedSegmentIndex, viewportId);
setSelectedSegment(newSelectedSegmentIndex);
},
[selectedSegment]
);
const hydrateSEG = useCallback(() => {
// update the previously stored segmentationPresentation with the new viewportId
// presentation so that when we put the referencedDisplaySet back in the viewport
// it will have the correct segmentation representation hydrated
commandsManager.runCommand('updateStoredSegmentationPresentation', {
displaySet: segDisplaySet,
type: SegmentationRepresentations.Labelmap,
});
// update the previously stored positionPresentation with the new viewportId
// presentation so that when we put the referencedDisplaySet back in the viewport
// it will be in the correct position zoom and pan
commandsManager.runCommand('updateStoredPositionPresentation', {
viewportId,
displaySetInstanceUID: referencedDisplaySet.displaySetInstanceUID,
});
viewportGridService.setDisplaySetsForViewport({
viewportId,
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
});
}, [commandsManager, viewportId, referencedDisplaySet, segDisplaySet]);
useEffect(() => {
if (segIsLoading) {
return;
@@ -158,28 +180,24 @@ function OHIFCornerstoneSEGViewport(props) {
viewportId,
segDisplaySet,
preHydrateCallbacks: [storePresentationState],
hydrateCallback: hydrateSEG,
}).then(isHydrated => {
if (isHydrated) {
setIsHydrated(true);
}
});
}, [servicesManager, viewportId, segDisplaySet, segIsLoading]);
}, [servicesManager, viewportId, segDisplaySet, segIsLoading, hydrateSEG]);
useEffect(() => {
// on new seg display set, remove all segmentations from all viewports
segmentationService.clearSegmentationRepresentations(viewportId);
const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
evt => {
if (evt.segDisplaySet.displaySetInstanceUID === segDisplaySet.displaySetInstanceUID) {
setSegIsLoading(false);
}
if (evt.overlappingSegments) {
uiNotificationService.show({
title: 'Overlapping Segments',
message: 'Overlapping segments detected which is not currently supported',
type: 'warning',
});
}
}
);
@@ -233,28 +251,78 @@ function OHIFCornerstoneSEGViewport(props) {
return;
}
// keep the already stored segmentationPresentation for this viewport in memory
// so that we can restore it after hydrating the SEG
commandsManager.runCommand('updateStoredSegmentationPresentation', {
displaySet: segDisplaySet,
type: SegmentationRepresentations.Labelmap,
});
// always start fresh for this viewport since it is special type of viewport
// that should only show one segmentation at a time.
segmentationService.clearSegmentationRepresentations(viewportId);
// This creates a custom tool group which has the lifetime of this view
// only, and does NOT interfere with currently displayed segmentations.
toolGroup = createSEGToolGroupAndAddTools(toolGroupService, customizationService, toolGroupId);
return () => {
// remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentationFromToolGroup(toolGroupId);
// e.g., another seg displayset is dragged into the viewport
segmentationService.clearSegmentationRepresentations(viewportId);
// Only destroy the viewport specific implementation
toolGroupService.destroyToolGroup(toolGroupId);
};
}, []);
useEffect(() => {
setIsHydrated(segDisplaySet.isHydrated);
const onStatusClick = useCallback(async () => {
// Before hydrating a SEG and make it added to all viewports in the grid
// that share the same frameOfReferenceUID, we need to store the viewport grid
// presentation state, so that we can restore it after hydrating the SEG. This is
// required if the user has changed the viewport (other viewport than SEG viewport)
// presentation state (w/l and invert) and then opens the SEG. If we don't store
// the presentation state, the viewport will be reset to the default presentation
storePresentationState();
hydrateSEG();
}, [storePresentationState, hydrateSEG]);
return () => {
// remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentationFromToolGroup(toolGroupId);
referencedDisplaySetRef.current = null;
};
}, [segDisplaySet]);
useEffect(() => {
viewportActionCornersService.addComponents([
{
viewportId,
id: 'viewportStatusComponent',
component: _getStatusComponent({
isHydrated,
onStatusClick,
}),
indexPriority: -100,
location: viewportActionCornersService.LOCATIONS.topLeft,
},
{
viewportId,
id: 'viewportActionArrowsComponent',
component: (
<ViewportActionArrows
key="actionArrows"
onArrowsClick={onSegmentChange}
className={
viewportId === activeViewportId ? 'visible' : 'invisible group-hover/pane:visible'
}
></ViewportActionArrows>
),
indexPriority: 0,
location: viewportActionCornersService.LOCATIONS.topRight,
},
]);
}, [
activeViewportId,
isHydrated,
onSegmentChange,
onStatusClick,
viewportActionCornersService,
viewportId,
]);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
let childrenWithProps = null;
@@ -279,67 +347,8 @@ function OHIFCornerstoneSEGViewport(props) {
});
}
const {
PatientID,
PatientName,
PatientSex,
PatientAge,
SliceThickness,
ManufacturerModelName,
StudyDate,
SeriesDescription,
SpacingBetweenSlices,
} = referencedDisplaySetRef.current.metadata;
const onStatusClick = async () => {
// Before hydrating a SEG and make it added to all viewports in the grid
// that share the same frameOfReferenceUID, we need to store the viewport grid
// presentation state, so that we can restore it after hydrating the SEG. This is
// required if the user has changed the viewport (other viewport than SEG viewport)
// presentation state (w/l and invert) and then opens the SEG. If we don't store
// the presentation state, the viewport will be reset to the default presentation
storePresentationState();
const isHydrated = await hydrateSEGDisplaySet({
segDisplaySet,
viewportId,
servicesManager,
});
setIsHydrated(isHydrated);
};
return (
<>
<ViewportActionBar
onDoubleClick={evt => {
evt.stopPropagation();
evt.preventDefault();
}}
onArrowsClick={onSegmentChange}
getStatusComponent={() => {
return _getStatusComponent({
isHydrated,
onStatusClick,
});
}}
studyData={{
label: viewportLabel,
useAltStyling: true,
studyDate: formatDate(StudyDate),
seriesDescription: `SEG Viewport ${SeriesDescription}`,
patientInformation: {
patientName: PatientName ? OHIF.utils.formatPN(PatientName.Alphabetic) : '',
patientSex: PatientSex || '',
patientAge: PatientAge || '',
MRN: PatientID || '',
thickness: SliceThickness ? utils.roundNumber(SliceThickness, 2) : '',
thicknessUnits: SliceThickness !== undefined ? 'mm' : '',
spacing:
SpacingBetweenSlices !== undefined ? utils.roundNumber(SpacingBetweenSlices, 2) : '',
scanner: ManufacturerModelName || '',
},
}}
/>
<div className="relative flex h-full w-full flex-row overflow-hidden">
{segIsLoading && (
<LoadingIndicatorTotalPercent
@@ -356,25 +365,19 @@ function OHIFCornerstoneSEGViewport(props) {
);
}
OHIFCornerstoneSEGViewport.propTypes = {
displaySets: PropTypes.arrayOf(PropTypes.object),
viewportId: PropTypes.string.isRequired,
dataSource: PropTypes.object,
children: PropTypes.node,
customProps: PropTypes.object,
};
OHIFCornerstoneSEGViewport.defaultProps = {
customProps: {},
};
function _getReferencedDisplaySetMetadata(referencedDisplaySet, segDisplaySet) {
const {
SharedFunctionalGroupsSequence: [SharedFunctionalGroup],
} = segDisplaySet.instance;
const {
PixelMeasuresSequence: [PixelMeasures],
} = SharedFunctionalGroup;
const { SharedFunctionalGroupsSequence } = segDisplaySet.instance;
const SharedFunctionalGroup = Array.isArray(SharedFunctionalGroupsSequence)
? SharedFunctionalGroupsSequence[0]
: SharedFunctionalGroupsSequence;
const { PixelMeasuresSequence } = SharedFunctionalGroup;
const PixelMeasures = Array.isArray(PixelMeasuresSequence)
? PixelMeasuresSequence[0]
: PixelMeasuresSequence;
const { SpacingBetweenSlices, SliceThickness } = PixelMeasures;
const image0 = referencedDisplaySet.images[0];
@@ -6,9 +6,6 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
let ToolTipMessage = null;
let StatusIcon = null;
const { t } = useTranslation('Common');
const loadStr = t('LOAD');
switch (isHydrated) {
case true:
StatusIcon = () => <Icon name="status-alert" />;
@@ -26,23 +23,28 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
ToolTipMessage = () => <div>Click LOAD to load segmentation.</div>;
}
const StatusArea = () => (
<div className="flex h-6 cursor-default text-sm leading-6 text-white">
<div className="bg-customgray-100 flex min-w-[45px] items-center rounded-l-xl rounded-r p-1">
<StatusIcon />
<span className="ml-1">SEG</span>
</div>
{!isHydrated && (
<div
className="bg-primary-main hover:bg-primary-light ml-1 cursor-pointer rounded px-1.5 hover:text-black"
// Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none
onMouseUp={onStatusClick}
>
{loadStr}
const StatusArea = () => {
const { t } = useTranslation('Common');
const loadStr = t('LOAD');
return (
<div className="flex h-6 cursor-default text-sm leading-6 text-white">
<div className="bg-customgray-100 flex min-w-[45px] items-center rounded-l-xl rounded-r p-1">
<StatusIcon />
<span className="ml-1">SEG</span>
</div>
)}
</div>
);
{!isHydrated && (
<div
className="bg-primary-main hover:bg-primary-light ml-1 cursor-pointer rounded px-1.5 hover:text-black"
// Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none
onMouseUp={onStatusClick}
>
{loadStr}
</div>
)}
</div>
);
};
return (
<>
File diff suppressed because it is too large. Load diff
+12 -10
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-sr",
"version": "3.7.0-beta.71",
"version": "3.9.1",
"description": "OHIF extension for an SR Cornerstone Viewport",
"author": "OHIF",
"license": "MIT",
@@ -23,6 +23,8 @@
"ohif-extension"
],
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:cornerstone": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
@@ -32,21 +34,21 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.71",
"@ohif/extension-cornerstone": "3.7.0-beta.71",
"@ohif/extension-measurement-tracking": "3.7.0-beta.71",
"@ohif/ui": "3.7.0-beta.71",
"dcmjs": "^0.29.5",
"@ohif/core": "3.9.1",
"@ohif/extension-cornerstone": "3.9.1",
"@ohif/extension-measurement-tracking": "3.9.1",
"@ohif/ui": "3.9.1",
"dcmjs": "*",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
"react": "^17.0.2"
"react": "^18.3.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^1.13.2",
"@cornerstonejs/core": "^1.13.2",
"@cornerstonejs/tools": "^1.13.2",
"@cornerstonejs/adapters": "^2.2.4",
"@cornerstonejs/core": "^2.2.4",
"@cornerstonejs/tools": "^2.2.4",
"classnames": "^2.3.2"
}
}
@@ -40,7 +40,9 @@ const _generateReport = (measurementData, additionalFindingTypes, options = {})
return dataset;
};
const commandsModule = ({}) => {
const commandsModule = (props: withAppTypes) => {
const { servicesManager } = props;
const { customizationService } = servicesManager.services;
const actions = {
/**
*
@@ -55,7 +57,7 @@ const commandsModule = ({}) => {
const reportBlob = dcmjs.data.datasetToBlob(srDataset);
//Create a URL for the binary.
var objectUrl = URL.createObjectURL(reportBlob);
const objectUrl = URL.createObjectURL(reportBlob);
window.location.assign(objectUrl);
},
@@ -95,7 +97,15 @@ const commandsModule = ({}) => {
throw new Error('Invalid report, no content');
}
await dataSource.store.dicom(naturalizedReport);
const onBeforeDicomStore =
customizationService.getModeCustomization('onBeforeDicomStore')?.value;
let dicomDict;
if (typeof onBeforeDicomStore === 'function') {
dicomDict = onBeforeDicomStore({ measurementData, naturalizedReport });
}
await dataSource.store.dicom(naturalizedReport, null, dicomDict);
if (StudyInstanceUID) {
dataSource.deleteStudyMetadataPromise(StudyInstanceUID);
@@ -118,13 +128,9 @@ const commandsModule = ({}) => {
const definitions = {
downloadReport: {
commandFn: actions.downloadReport,
storeContexts: [],
options: {},
},
storeMeasurements: {
commandFn: actions.storeMeasurements,
storeContexts: [],
options: {},
},
};
@@ -0,0 +1,78 @@
import PropTypes from 'prop-types';
import React from 'react';
import { OHIFCornerstoneSRContentItem } from './OHIFCornerstoneSRContentItem';
export function OHIFCornerstoneSRContainer(props) {
const { container, nodeIndexesTree = [0], containerNumberedTree = [1] } = props;
const { ContinuityOfContent, ConceptNameCodeSequence } = container;
const { CodeMeaning } = ConceptNameCodeSequence ?? {};
let childContainerIndex = 1;
const contentItems = container.ContentSequence?.map((contentItem, i) => {
const { ValueType } = contentItem;
const childNodeLevel = [...nodeIndexesTree, i];
const key = childNodeLevel.join('.');
let Component;
let componentProps;
if (ValueType === 'CONTAINER') {
const childContainerNumberedTree = [...containerNumberedTree, childContainerIndex++];
Component = OHIFCornerstoneSRContainer;
componentProps = {
container: contentItem,
nodeIndexesTree: childNodeLevel,
containerNumberedTree: childContainerNumberedTree,
};
} else {
Component = OHIFCornerstoneSRContentItem;
componentProps = {
contentItem,
nodeIndexesTree: childNodeLevel,
continuityOfContent: ContinuityOfContent,
};
}
return (
<Component
key={key}
{...componentProps}
/>
);
});
return (
<div>
<div className="font-bold">
{containerNumberedTree.join('.')}.&nbsp;
{CodeMeaning}
</div>
<div className="ml-4 mb-2">{contentItems}</div>
</div>
);
}
OHIFCornerstoneSRContainer.propTypes = {
/**
* A tree node that may contain another container or one or more content items
* (text, code, uidref, pname, etc.)
*/
container: PropTypes.object,
/**
* A 0-based index list
*/
nodeIndexesTree: PropTypes.arrayOf(PropTypes.number),
/**
* A 1-based index list that represents a container in a multi-level numbered
* list (tree).
*
* Example:
* 1. History
* 1.1. Chief Complaint
* 1.2. Present Illness
* 1.3. Past History
* 1.4. Family History
* 2. Findings
* */
containerNumberedTree: PropTypes.arrayOf(PropTypes.number),
};
@@ -0,0 +1,63 @@
import PropTypes from 'prop-types';
import React from 'react';
import { CodeNameCodeSequenceValues } from '../enums';
import formatContentItemValue from '../utils/formatContentItem';
const EMPTY_TAG_VALUE = '[empty]';
function OHIFCornerstoneSRContentItem(props) {
const { contentItem, nodeIndexesTree, continuityOfContent } = props;
const { ConceptNameCodeSequence } = contentItem;
const { CodeValue, CodeMeaning } = ConceptNameCodeSequence;
const isChildFirstNode = nodeIndexesTree[nodeIndexesTree.length - 1] === 0;
const formattedValue = formatContentItemValue(contentItem) ?? EMPTY_TAG_VALUE;
const startWithAlphaNumCharRegEx = /^[a-zA-Z0-9]/;
const isContinuous = continuityOfContent === 'CONTINUOUS';
const isFinding = CodeValue === CodeNameCodeSequenceValues.Finding;
const addExtraSpace =
isContinuous && !isChildFirstNode && startWithAlphaNumCharRegEx.test(formattedValue?.[0]);
// Collapse sequences of white space preserving newline characters
let className = 'whitespace-pre-line';
if (CodeValue === CodeNameCodeSequenceValues.Finding) {
// Preserve spaces because it is common to see tabular text in a
// "Findings" ConceptNameCodeSequence
className = 'whitespace-pre-wrap';
}
if (isContinuous) {
return (
<>
<span
className={className}
title={CodeMeaning}
>
{addExtraSpace ? ' ' : ''}
{formattedValue}
</span>
</>
);
}
return (
<>
<div className="mb-2">
<span className="font-bold">{CodeMeaning}: </span>
{isFinding ? (
<pre>{formattedValue}</pre>
) : (
<span className={className}>{formattedValue}</span>
)}
</div>
</>
);
}
OHIFCornerstoneSRContentItem.propTypes = {
contentItem: PropTypes.object,
nodeIndexesTree: PropTypes.arrayOf(PropTypes.number),
continuityOfContent: PropTypes.string,
};
export { OHIFCornerstoneSRContentItem };
@@ -1,26 +1,25 @@
import PropTypes from 'prop-types';
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import OHIF, { utils, ServicesManager, ExtensionManager } from '@ohif/core';
import { ExtensionManager } from '@ohif/core';
import { setTrackingUniqueIdentifiersForElement } from '../tools/modules/dicomSRModule';
import { Icon, Tooltip, useViewportGrid, ViewportActionBar } from '@ohif/ui';
import { Icon, Tooltip, useViewportGrid, ViewportActionArrows } from '@ohif/ui';
import hydrateStructuredReport from '../utils/hydrateStructuredReport';
import { useAppConfig } from '@state';
const { formatDate } = utils;
import createReferencedImageDisplaySet from '../utils/createReferencedImageDisplaySet';
const MEASUREMENT_TRACKING_EXTENSION_ID = '@ohif/extension-measurement-tracking';
const SR_TOOLGROUP_BASE_NAME = 'SRToolGroup';
function OHIFCornerstoneSRViewport(props) {
function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) {
const {
commandsManager,
children,
dataSource,
displaySets,
viewportLabel,
viewportOptions,
servicesManager,
extensionManager,
@@ -28,8 +27,12 @@ function OHIFCornerstoneSRViewport(props) {
const [appConfig] = useAppConfig();
const { displaySetService, cornerstoneViewportService, measurementService } =
servicesManager.services;
const {
displaySetService,
cornerstoneViewportService,
measurementService,
viewportActionCornersService,
} = servicesManager.services;
const viewportId = viewportOptions.viewportId;
@@ -48,6 +51,8 @@ function OHIFCornerstoneSRViewport(props) {
const [element, setElement] = useState(null);
const { viewports, activeViewportId } = viewportGrid;
const { t } = useTranslation('Common');
// Optional hook into tracking extension, if present.
let trackedMeasurements;
let sendTrackedMeasurementsEvent;
@@ -65,6 +70,7 @@ function OHIFCornerstoneSRViewport(props) {
trackedMeasurements = tracked?.[0];
sendTrackedMeasurementsEvent = tracked?.[1];
}
if (!sendTrackedMeasurementsEvent) {
// if no panels from measurement-tracking extension is used, this code will run
trackedMeasurements = null;
@@ -74,6 +80,7 @@ function OHIFCornerstoneSRViewport(props) {
{ servicesManager, extensionManager, appConfig },
displaySetInstanceUID
);
const displaySets = displaySetService.getDisplaySetsForSeries(SeriesInstanceUIDs[0]);
if (displaySets.length) {
viewportGridService.setDisplaySetsForViewports([
@@ -86,6 +93,11 @@ function OHIFCornerstoneSRViewport(props) {
};
}
/**
* Todo: what is this, not sure what it does regarding the react aspect,
* it is updating a local variable? which is not state.
*/
const [isLocked, setIsLocked] = useState(trackedMeasurements?.context?.trackedSeries?.length > 0);
/**
* Store the tracking identifiers per viewport in order to be able to
* show the SR measurements on the referenced image on the correct viewport,
@@ -128,11 +140,19 @@ function OHIFCornerstoneSRViewport(props) {
console.warn('More than one SOPClassUID in the same series is not yet supported.');
}
// if (!srDisplaySet.measurements || !srDisplaySet.measurements.length) {
// return;
// }
_getViewportReferencedDisplaySetData(
srDisplaySet,
newMeasurementSelected,
displaySetService
).then(({ referencedDisplaySet, referencedDisplaySetMetadata }) => {
if (!referencedDisplaySet || !referencedDisplaySetMetadata) {
return;
}
setMeasurementSelected(newMeasurementSelected);
setActiveImageDisplaySetData(referencedDisplaySet);
setReferencedDisplaySetMetadata(referencedDisplaySetMetadata);
@@ -149,6 +169,10 @@ function OHIFCornerstoneSRViewport(props) {
// new measurement
const csViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
if (!csViewport) {
return;
}
const imageIds = csViewport.getImageIds();
const imageIdIndex = imageIds.indexOf(measurements[newMeasurementSelected].imageId);
@@ -202,7 +226,10 @@ function OHIFCornerstoneSRViewport(props) {
// The positionIds for the viewport aren't meaningful for the child display sets
positionIds: null,
}}
onElementEnabled={onElementEnabled}
onElementEnabled={evt => {
props.onElementEnabled?.(evt);
onElementEnabled(evt);
}}
initialImageIndex={initialImageIndex}
isJumpToMeasurementDisabled={true}
></Component>
@@ -213,18 +240,11 @@ function OHIFCornerstoneSRViewport(props) {
direction => {
let newMeasurementSelected = measurementSelected;
if (direction === 'right') {
newMeasurementSelected++;
if (newMeasurementSelected >= measurementCount) {
newMeasurementSelected = 0;
}
} else {
newMeasurementSelected--;
if (newMeasurementSelected < 0) {
newMeasurementSelected = measurementCount - 1;
}
newMeasurementSelected += direction;
if (newMeasurementSelected >= measurementCount) {
newMeasurementSelected = 0;
} else if (newMeasurementSelected < 0) {
newMeasurementSelected = measurementCount - 1;
}
setTrackingIdentifiers(newMeasurementSelected);
@@ -240,7 +260,7 @@ function OHIFCornerstoneSRViewport(props) {
const onDisplaySetsRemovedSubscription = displaySetService.subscribe(
displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
({ displaySetInstanceUIDs }) => {
const activeViewport = viewports[activeViewportId];
const activeViewport = viewports.get(activeViewportId);
if (displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)) {
viewportGridService.setDisplaySetsForViewport({
viewportId: activeViewportId,
@@ -263,11 +283,15 @@ function OHIFCornerstoneSRViewport(props) {
* if it is hydrated we don't even use the SR viewport.
*/
useEffect(() => {
if (!srDisplaySet.isLoaded) {
srDisplaySet.load();
}
const numMeasurements = srDisplaySet.measurements.length;
setMeasurementCount(numMeasurements);
const loadSR = async () => {
if (!srDisplaySet.isLoaded) {
await srDisplaySet.load();
}
const numMeasurements = srDisplaySet.measurements.length;
setMeasurementCount(numMeasurements);
updateViewport(measurementSelected);
};
loadSR();
}, [srDisplaySet]);
/**
@@ -275,28 +299,61 @@ function OHIFCornerstoneSRViewport(props) {
* the element changes
*/
useEffect(() => {
if (!element || !srDisplaySet.isLoaded) {
return;
}
setTrackingIdentifiers(measurementSelected);
const updateSR = async () => {
if (!srDisplaySet.isLoaded) {
await srDisplaySet.load();
}
if (!element || !srDisplaySet.isLoaded) {
return;
}
setTrackingIdentifiers(measurementSelected);
};
updateSR();
}, [measurementSelected, element, setTrackingIdentifiers, srDisplaySet]);
/**
* Todo: what is this, not sure what it does regarding the react aspect,
* it is updating a local variable? which is not state.
*/
let isLocked = trackedMeasurements?.context?.trackedSeries?.length > 0;
useEffect(() => {
isLocked = trackedMeasurements?.context?.trackedSeries?.length > 0;
setIsLocked(trackedMeasurements?.context?.trackedSeries?.length > 0);
}, [trackedMeasurements]);
/**
* Data fetching for the SR displaySet, which updates the measurements and
* also gets the referenced image displaySet that SR is based on.
*/
useEffect(() => {
updateViewport(measurementSelected);
}, [dataSource, srDisplaySet]);
viewportActionCornersService.addComponents([
{
viewportId,
id: 'viewportStatusComponent',
component: _getStatusComponent({
srDisplaySet,
viewportId,
isRehydratable: srDisplaySet.isRehydratable,
isLocked,
sendTrackedMeasurementsEvent,
t,
}),
indexPriority: -100,
location: viewportActionCornersService.LOCATIONS.topLeft,
},
{
viewportId,
id: 'viewportActionArrowsComponent',
index: 0,
component: (
<ViewportActionArrows
key="actionArrows"
onArrowsClick={onMeasurementChange}
></ViewportActionArrows>
),
indexPriority: 0,
location: viewportActionCornersService.LOCATIONS.topRight,
},
]);
}, [
isLocked,
onMeasurementChange,
sendTrackedMeasurementsEvent,
srDisplaySet,
t,
viewportActionCornersService,
viewportId,
]);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
let childrenWithProps = null;
@@ -317,57 +374,8 @@ function OHIFCornerstoneSRViewport(props) {
});
}
const {
PatientID,
PatientName,
PatientSex,
PatientAge,
SliceThickness,
ManufacturerModelName,
StudyDate,
SeriesDescription,
SpacingBetweenSlices,
SeriesNumber,
} = referencedDisplaySetMetadata;
// TODO -> disabled double click for now: onDoubleClick={_onDoubleClick}
return (
<>
<ViewportActionBar
onDoubleClick={evt => {
evt.stopPropagation();
evt.preventDefault();
}}
onArrowsClick={onMeasurementChange}
getStatusComponent={() =>
_getStatusComponent({
srDisplaySet,
viewportId,
isTracked: false,
isRehydratable: srDisplaySet.isRehydratable,
isLocked,
sendTrackedMeasurementsEvent,
})
}
studyData={{
label: viewportLabel,
useAltStyling: true,
studyDate: formatDate(StudyDate),
currentSeries: SeriesNumber,
seriesDescription: SeriesDescription || '',
patientInformation: {
patientName: PatientName ? OHIF.utils.formatPN(PatientName.Alphabetic) : '',
patientSex: PatientSex || '',
patientAge: PatientAge || '',
MRN: PatientID || '',
thickness: SliceThickness ? `${SliceThickness.toFixed(2)}mm` : '',
spacing:
SpacingBetweenSlices !== undefined ? `${SpacingBetweenSlices.toFixed(2)}mm` : '',
scanner: ManufacturerModelName || '',
},
}}
/>
<div className="relative flex h-full w-full flex-row overflow-hidden">
{getCornerstoneViewport()}
{childrenWithProps}
@@ -376,23 +384,17 @@ function OHIFCornerstoneSRViewport(props) {
);
}
OHIFCornerstoneSRViewport.propTypes = {
OHIFCornerstoneSRMeasurementViewport.propTypes = {
displaySets: PropTypes.arrayOf(PropTypes.object),
viewportId: PropTypes.string.isRequired,
dataSource: PropTypes.object,
children: PropTypes.node,
viewportLabel: PropTypes.string,
customProps: PropTypes.object,
viewportOptions: PropTypes.object,
viewportLabel: PropTypes.string,
servicesManager: PropTypes.instanceOf(ServicesManager).isRequired,
servicesManager: PropTypes.object.isRequired,
extensionManager: PropTypes.instanceOf(ExtensionManager).isRequired,
};
OHIFCornerstoneSRViewport.defaultProps = {
customProps: {},
};
async function _getViewportReferencedDisplaySetData(
displaySet,
measurementSelected,
@@ -402,6 +404,17 @@ async function _getViewportReferencedDisplaySetData(
const measurement = measurements[measurementSelected];
const { displaySetInstanceUID } = measurement;
if (!displaySet.keyImageDisplaySet) {
// Create a new display set, and preserve a reference to it here,
// so that it can be re-displayed and shown inside the SR viewport.
// This is only for ease of redisplay - the display set is stored in the
// usual manner in the display set service.
displaySet.keyImageDisplaySet = createReferencedImageDisplaySet(displaySetService, displaySet);
}
if (!displaySetInstanceUID) {
return { referencedDisplaySetMetadata: null, referencedDisplaySet: null };
}
const referencedDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
@@ -429,6 +442,7 @@ function _getStatusComponent({
isRehydratable,
isLocked,
sendTrackedMeasurementsEvent,
t,
}) {
const handleMouseUp = () => {
sendTrackedMeasurementsEvent('HYDRATE_SR', {
@@ -437,7 +451,6 @@ function _getStatusComponent({
});
};
const { t } = useTranslation('Common');
const loadStr = t('LOAD');
// 1 - Incompatible
@@ -516,4 +529,4 @@ function _getStatusComponent({
);
}
export default OHIFCornerstoneSRViewport;
export default OHIFCornerstoneSRMeasurementViewport;
@@ -0,0 +1,32 @@
import PropTypes from 'prop-types';
import React from 'react';
import { ExtensionManager } from '@ohif/core';
import { OHIFCornerstoneSRContainer } from './OHIFCornerstoneSRContainer';
function OHIFCornerstoneSRTextViewport(props: withAppTypes) {
const { displaySets } = props;
const displaySet = displaySets[0];
const instance = displaySet.instances[0];
return (
<div className="relative flex h-full w-full flex-col overflow-auto p-4 text-white">
<div>
{/* The root level is always a container */}
<OHIFCornerstoneSRContainer container={instance} />
</div>
</div>
);
}
OHIFCornerstoneSRTextViewport.propTypes = {
displaySets: PropTypes.arrayOf(PropTypes.object),
viewportId: PropTypes.string.isRequired,
dataSource: PropTypes.object,
children: PropTypes.node,
viewportLabel: PropTypes.string,
viewportOptions: PropTypes.object,
servicesManager: PropTypes.object.isRequired,
extensionManager: PropTypes.instanceOf(ExtensionManager).isRequired,
};
export default OHIFCornerstoneSRTextViewport;
@@ -0,0 +1,30 @@
import PropTypes from 'prop-types';
import React from 'react';
import { ExtensionManager } from '@ohif/core';
import OHIFCornerstoneSRMeasurementViewport from './OHIFCornerstoneSRMeasurementViewport';
import OHIFCornerstoneSRTextViewport from './OHIFCornerstoneSRTextViewport';
function OHIFCornerstoneSRViewport(props: withAppTypes) {
const { displaySets } = props;
const { isImagingMeasurementReport } = displaySets[0];
if (isImagingMeasurementReport) {
return <OHIFCornerstoneSRMeasurementViewport {...props}></OHIFCornerstoneSRMeasurementViewport>;
}
return <OHIFCornerstoneSRTextViewport {...props}></OHIFCornerstoneSRTextViewport>;
}
OHIFCornerstoneSRViewport.propTypes = {
displaySets: PropTypes.arrayOf(PropTypes.object),
viewportId: PropTypes.string.isRequired,
dataSource: PropTypes.object,
children: PropTypes.node,
viewportLabel: PropTypes.string,
viewportOptions: PropTypes.object,
servicesManager: PropTypes.object.isRequired,
extensionManager: PropTypes.instanceOf(ExtensionManager).isRequired,
};
export default OHIFCornerstoneSRViewport;
@@ -1,7 +0,0 @@
export default {
POINT: 'POINT',
MULTIPOINT: 'MULTIPOINT',
POLYLINE: 'POLYLINE',
CIRCLE: 'CIRCLE',
ELLIPSE: 'ELLIPSE',
};
@@ -0,0 +1,44 @@
import { adaptersSR } from '@cornerstonejs/adapters';
const { CodeScheme: Cornerstone3DCodeScheme } = adaptersSR.Cornerstone3D;
export const SCOORDTypes = {
POINT: 'POINT',
MULTIPOINT: 'MULTIPOINT',
POLYLINE: 'POLYLINE',
CIRCLE: 'CIRCLE',
ELLIPSE: 'ELLIPSE',
};
export const CodeNameCodeSequenceValues = {
ImagingMeasurementReport: '126000',
ImageLibrary: '111028',
ImagingMeasurements: '126010',
MeasurementGroup: '125007',
ImageLibraryGroup: '126200',
TrackingUniqueIdentifier: '112040',
TrackingIdentifier: '112039',
Finding: '121071',
FindingSite: 'G-C0E3', // SRT
FindingSiteSCT: '363698007', // SCT
};
export const CodingSchemeDesignators = {
SRT: 'SRT',
SCT: 'SCT',
CornerstoneCodeSchemes: [Cornerstone3DCodeScheme.CodingSchemeDesignator, 'CST4'],
};
export const RelationshipType = {
INFERRED_FROM: 'INFERRED FROM',
CONTAINS: 'CONTAINS',
};
const enums = {
CodeNameCodeSequenceValues,
CodingSchemeDesignators,
RelationshipType,
SCOORDTypes,
};
export default enums;
@@ -1,30 +1,37 @@
import { SOPClassHandlerName, SOPClassHandlerId } from './id';
import { utils, classes, DisplaySetService, Types } from '@ohif/core';
import addMeasurement from './utils/addMeasurement';
import isRehydratable from './utils/isRehydratable';
import { Enums as CSExtensionEnums } from '@ohif/extension-cornerstone';
import { adaptersSR } from '@cornerstonejs/adapters';
import addSRAnnotation from './utils/addSRAnnotation';
import isRehydratable from './utils/isRehydratable';
import {
SOPClassHandlerName,
SOPClassHandlerId,
SOPClassHandlerId3D,
SOPClassHandlerName3D,
} from './id';
import { CodeNameCodeSequenceValues, CodingSchemeDesignators } from './enums';
const { sopClassDictionary } = utils;
const { CORNERSTONE_3D_TOOLS_SOURCE_NAME, CORNERSTONE_3D_TOOLS_SOURCE_VERSION } = CSExtensionEnums;
const { ImageSet, MetadataProvider: metadataProvider } = classes;
const { CodeScheme: Cornerstone3DCodeScheme } = adaptersSR.Cornerstone3D;
type InstanceMetadata = Types.InstanceMetadata;
const { CodeScheme: Cornerstone3DCodeScheme } = adaptersSR.Cornerstone3D;
const { ImageSet, MetadataProvider: metadataProvider } = classes;
// TODO ->
// Add SR thumbnail
// Make viewport
// Get stacks from referenced displayInstanceUID and load into wrapped CornerStone viewport.
/**
* TODO
* - [ ] Add SR thumbnail
* - [ ] Make viewport
* - [ ] Get stacks from referenced displayInstanceUID and load into wrapped CornerStone viewport
*/
const sopClassUids = [
'1.2.840.10008.5.1.4.1.1.88.11', //BASIC_TEXT_SR:
'1.2.840.10008.5.1.4.1.1.88.22', //ENHANCED_SR:
'1.2.840.10008.5.1.4.1.1.88.33', //COMPREHENSIVE_SR:
'1.2.840.10008.5.1.4.1.1.88.34', //COMPREHENSIVE_3D_SR:
sopClassDictionary.BasicTextSR,
sopClassDictionary.EnhancedSR,
sopClassDictionary.ComprehensiveSR,
];
const CORNERSTONE_3D_TOOLS_SOURCE_NAME = 'Cornerstone3DTools';
const CORNERSTONE_3D_TOOLS_SOURCE_VERSION = '0.1';
const validateSameStudyUID = (uid: string, instances): void => {
instances.forEach(it => {
if (it.StudyInstanceUID !== uid) {
@@ -34,31 +41,6 @@ const validateSameStudyUID = (uid: string, instances): void => {
});
};
const CodeNameCodeSequenceValues = {
ImagingMeasurementReport: '126000',
ImageLibrary: '111028',
ImagingMeasurements: '126010',
MeasurementGroup: '125007',
ImageLibraryGroup: '126200',
TrackingUniqueIdentifier: '112040',
TrackingIdentifier: '112039',
Finding: '121071',
FindingSite: 'G-C0E3', // SRT
CornerstoneFreeText: Cornerstone3DCodeScheme.codeValues.CORNERSTONEFREETEXT, //
};
const CodingSchemeDesignators = {
SRT: 'SRT',
CornerstoneCodeSchemes: [Cornerstone3DCodeScheme.CodingSchemeDesignator, 'CST4'],
};
const RELATIONSHIP_TYPE = {
INFERRED_FROM: 'INFERRED FROM',
CONTAINS: 'CONTAINS',
};
const CORNERSTONE_FREETEXT_CODE_VALUE = 'CORNERSTONEFREETEXT';
/**
* Adds instances to the DICOM SR series, rather than creating a new
* series, so that as SR's are saved, they append to the series, and the
@@ -85,7 +67,11 @@ function addInstances(instances: InstanceMetadata[], displaySetService: DisplayS
* @param servicesManager is the services that can be used for creating
* @returns The list of display sets created for the given instances object
*/
function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
function _getDisplaySetsFromSeries(
instances,
servicesManager: AppTypes.ServicesManager,
extensionManager
) {
// If the series has no instances, stop here
if (!instances || !instances.length) {
throw new Error('No instances were provided');
@@ -109,22 +95,12 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
} = instance;
validateSameStudyUID(instance.StudyInstanceUID, instances);
if (
!ConceptNameCodeSequence ||
ConceptNameCodeSequence.CodeValue !== CodeNameCodeSequenceValues.ImagingMeasurementReport
) {
servicesManager.services.uiNotificationService.show({
title: 'DICOM SR',
message:
'OHIF only supports TID1500 Imaging Measurement Report Structured Reports. The SR you’re trying to view is not supported.',
type: 'warning',
duration: 6000,
});
return [];
}
const is3DSR = SOPClassUID === sopClassDictionary.Comprehensive3DSR;
const isImagingMeasurementReport =
ConceptNameCodeSequence?.CodeValue === CodeNameCodeSequenceValues.ImagingMeasurementReport;
const displaySet = {
//plugin: id,
Modality: 'SR',
displaySetInstanceUID: utils.guid(),
SeriesDescription,
@@ -133,13 +109,14 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
SOPClassHandlerId,
SOPClassHandlerId: is3DSR ? SOPClassHandlerId3D : SOPClassHandlerId,
SOPClassUID,
instances,
referencedImages: null,
measurements: null,
isDerivedDisplaySet: true,
isLoaded: false,
isImagingMeasurementReport,
sopClassUids,
instance,
addInstances,
@@ -150,115 +127,206 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
return [displaySet];
}
function _load(displaySet, servicesManager, extensionManager) {
/**
* Loads the display set with the given services and extension manager.
* @param srDisplaySet - The display set to load.
* @param servicesManager - The services manager containing displaySetService and measurementService.
* @param extensionManager - The extension manager containing data sources.
*/
async function _load(
srDisplaySet: Types.DisplaySet,
servicesManager: AppTypes.ServicesManager,
extensionManager: AppTypes.ExtensionManager
) {
const { displaySetService, measurementService } = servicesManager.services;
const dataSources = extensionManager.getDataSources();
const dataSource = dataSources[0];
const { ContentSequence } = srDisplaySet.instance;
const { ContentSequence } = displaySet.instance;
async function retrieveBulkData(obj, parentObj = null, key = null) {
for (const prop in obj) {
if (typeof obj[prop] === 'object' && obj[prop] !== null) {
await retrieveBulkData(obj[prop], obj, prop);
} else if (Array.isArray(obj[prop])) {
await Promise.all(obj[prop].map(item => retrieveBulkData(item, obj, prop)));
} else if (prop === 'BulkDataURI') {
const value = await dataSource.retrieve.bulkDataURI({
BulkDataURI: obj[prop],
StudyInstanceUID: srDisplaySet.instance.StudyInstanceUID,
SeriesInstanceUID: srDisplaySet.instance.SeriesInstanceUID,
SOPInstanceUID: srDisplaySet.instance.SOPInstanceUID,
});
if (parentObj && key) {
parentObj[key] = new Float32Array(value);
}
}
}
}
displaySet.referencedImages = _getReferencedImagesList(ContentSequence);
displaySet.measurements = _getMeasurements(ContentSequence);
if (srDisplaySet.isLoaded !== true) {
await retrieveBulkData(ContentSequence);
}
if (srDisplaySet.isImagingMeasurementReport) {
srDisplaySet.referencedImages = _getReferencedImagesList(ContentSequence);
srDisplaySet.measurements = _getMeasurements(ContentSequence);
} else {
srDisplaySet.referencedImages = [];
srDisplaySet.measurements = [];
}
const mappings = measurementService.getSourceMappings(
CORNERSTONE_3D_TOOLS_SOURCE_NAME,
CORNERSTONE_3D_TOOLS_SOURCE_VERSION
);
displaySet.isHydrated = false;
displaySet.isRehydratable = isRehydratable(displaySet, mappings);
displaySet.isLoaded = true;
srDisplaySet.isHydrated = false;
srDisplaySet.isRehydratable = isRehydratable(srDisplaySet, mappings);
srDisplaySet.isLoaded = true;
// Check currently added displaySets and add measurements if the sources exist.
/** Check currently added displaySets and add measurements if the sources exist */
displaySetService.activeDisplaySets.forEach(activeDisplaySet => {
_checkIfCanAddMeasurementsToDisplaySet(displaySet, activeDisplaySet, dataSource);
_checkIfCanAddMeasurementsToDisplaySet(
srDisplaySet,
activeDisplaySet,
dataSource,
servicesManager
);
});
// Subscribe to new displaySets as the source may come in after.
/** Subscribe to new displaySets as the source may come in after */
displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SETS_ADDED, data => {
const { displaySetsAdded } = data;
// If there are still some measurements that have not yet been loaded into cornerstone,
// See if we can load them onto any of the new displaySets.
/**
* If there are still some measurements that have not yet been loaded into cornerstone,
* See if we can load them onto any of the new displaySets.
*/
displaySetsAdded.forEach(newDisplaySet => {
_checkIfCanAddMeasurementsToDisplaySet(displaySet, newDisplaySet, dataSource);
_checkIfCanAddMeasurementsToDisplaySet(
srDisplaySet,
newDisplaySet,
dataSource,
servicesManager
);
});
});
}
function _checkIfCanAddMeasurementsToDisplaySet(srDisplaySet, newDisplaySet, dataSource) {
let unloadedMeasurements = srDisplaySet.measurements.filter(
/**
* Checks if measurements can be added to a display set.
*
* @param srDisplaySet - The source display set containing measurements.
* @param newDisplaySet - The new display set to check if measurements can be added.
* @param dataSource - The data source used to retrieve image IDs.
* @param servicesManager - The services manager.
*/
function _checkIfCanAddMeasurementsToDisplaySet(
srDisplaySet,
newDisplaySet,
dataSource,
servicesManager: AppTypes.ServicesManager
) {
const { customizationService } = servicesManager.services;
const unloadedMeasurements = srDisplaySet.measurements.filter(
measurement => measurement.loaded === false
);
if (unloadedMeasurements.length === 0) {
// All already loaded!
if (
unloadedMeasurements.length === 0 ||
!(newDisplaySet instanceof ImageSet) ||
newDisplaySet.unsupported
) {
return;
}
if (!newDisplaySet instanceof ImageSet) {
// This also filters out _this_ displaySet, as it is not an ImageSet.
// const { sopClassUids } = newDisplaySet;
// Create a Set for faster lookups
// const sopClassUidSet = new Set(sopClassUids);
// Create a Map to efficiently look up ImageIds by SOPInstanceUID and frame number
const imageIdMap = new Map<string, string>();
const imageIds = dataSource.getImageIdsForDisplaySet(newDisplaySet);
for (const imageId of imageIds) {
const { SOPInstanceUID, frameNumber } = metadataProvider.getUIDsFromImageID(imageId);
const key = `${SOPInstanceUID}:${frameNumber || 1}`;
imageIdMap.set(key, imageId);
}
if (!unloadedMeasurements?.length) {
return;
}
const { sopClassUids, images } = newDisplaySet;
const is3DSR = srDisplaySet.SOPClassUID === sopClassDictionary.Comprehensive3DSR;
// Check if any have the newDisplaySet is the correct SOPClass.
unloadedMeasurements = unloadedMeasurements.filter(measurement =>
measurement.coords.some(coord =>
sopClassUids.includes(coord.ReferencedSOPSequence.ReferencedSOPClassUID)
)
);
for (let j = unloadedMeasurements.length - 1; j >= 0; j--) {
let measurement = unloadedMeasurements[j];
if (unloadedMeasurements.length === 0) {
// New displaySet isn't the correct SOPClass, so can't contain the referenced images.
return;
}
const onBeforeSRAddMeasurement = customizationService.getModeCustomization(
'onBeforeSRAddMeasurement'
)?.value;
const SOPInstanceUIDs = [];
unloadedMeasurements.forEach(measurement => {
const { coords } = measurement;
coords.forEach(coord => {
const SOPInstanceUID = coord.ReferencedSOPSequence.ReferencedSOPInstanceUID;
if (!SOPInstanceUIDs.includes(SOPInstanceUID)) {
SOPInstanceUIDs.push(SOPInstanceUID);
}
});
});
const imageIdsForDisplaySet = dataSource.getImageIdsForDisplaySet(newDisplaySet);
for (const imageId of imageIdsForDisplaySet) {
if (!unloadedMeasurements.length) {
// All measurements loaded.
return;
if (typeof onBeforeSRAddMeasurement === 'function') {
measurement = onBeforeSRAddMeasurement({
measurement,
StudyInstanceUID: srDisplaySet.StudyInstanceUID,
SeriesInstanceUID: srDisplaySet.SeriesInstanceUID,
});
}
const { SOPInstanceUID, frameNumber } = metadataProvider.getUIDsFromImageID(imageId);
// if it is 3d SR we can just add the SR annotation
if (is3DSR) {
addSRAnnotation(measurement, null, null);
measurement.loaded = true;
continue;
}
if (SOPInstanceUIDs.includes(SOPInstanceUID)) {
for (let j = unloadedMeasurements.length - 1; j >= 0; j--) {
const measurement = unloadedMeasurements[j];
if (_measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frameNumber)) {
addMeasurement(measurement, imageId, newDisplaySet.displaySetInstanceUID);
const referencedSOPSequence = measurement.coords[0].ReferencedSOPSequence;
if (!referencedSOPSequence) {
continue;
}
unloadedMeasurements.splice(j, 1);
}
}
const { ReferencedSOPInstanceUID } = referencedSOPSequence;
const frame = referencedSOPSequence.ReferencedFrameNumber || 1;
const key = `${ReferencedSOPInstanceUID}:${frame}`;
const imageId = imageIdMap.get(key);
if (
imageId &&
_measurementReferencesSOPInstanceUID(measurement, ReferencedSOPInstanceUID, frame)
) {
addSRAnnotation(measurement, imageId, frame);
// Update measurement properties
measurement.loaded = true;
measurement.imageId = imageId;
measurement.displaySetInstanceUID = newDisplaySet.displaySetInstanceUID;
measurement.ReferencedSOPInstanceUID = ReferencedSOPInstanceUID;
measurement.frameNumber = frame;
unloadedMeasurements.splice(j, 1);
}
}
}
/**
* Checks if a measurement references a specific SOP Instance UID.
* @param measurement - The measurement object.
* @param SOPInstanceUID - The SOP Instance UID to check against.
* @param frameNumber - The frame number to check against (optional).
* @returns True if the measurement references the specified SOP Instance UID, false otherwise.
*/
function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frameNumber) {
const { coords } = measurement;
// NOTE: The ReferencedFrameNumber can be multiple values according to the DICOM
// Standard. But for now, we will support only one ReferenceFrameNumber.
/**
* NOTE: The ReferencedFrameNumber can be multiple values according to the DICOM
* Standard. But for now, we will support only one ReferenceFrameNumber.
*/
const ReferencedFrameNumber =
(measurement.coords[0].ReferencedSOPSequence &&
measurement.coords[0].ReferencedSOPSequence[0]?.ReferencedFrameNumber) ||
measurement.coords[0].ReferencedSOPSequence?.ReferencedFrameNumber) ||
1;
if (frameNumber && Number(frameNumber) !== Number(ReferencedFrameNumber)) {
@@ -268,40 +336,62 @@ function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frame
for (let j = 0; j < coords.length; j++) {
const coord = coords[j];
const { ReferencedSOPInstanceUID } = coord.ReferencedSOPSequence;
if (ReferencedSOPInstanceUID === SOPInstanceUID) {
return true;
}
}
return false;
}
/**
* Retrieves the SOP class handler module.
*
* @param {Object} options - The options for retrieving the SOP class handler module.
* @param {Object} options.servicesManager - The services manager.
* @param {Object} options.extensionManager - The extension manager.
* @returns {Array} An array containing the SOP class handler module.
*/
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
const getDisplaySetsFromSeries = instances => {
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
};
return [
{
name: SOPClassHandlerName,
sopClassUids,
getDisplaySetsFromSeries,
},
{
name: SOPClassHandlerName3D,
sopClassUids: [sopClassDictionary.Comprehensive3DSR],
getDisplaySetsFromSeries,
},
];
}
/**
* Retrieves the measurements from the ImagingMeasurementReportContentSequence.
*
* @param {Array} ImagingMeasurementReportContentSequence - The ImagingMeasurementReportContentSequence array.
* @returns {Array} - The array of measurements.
*/
function _getMeasurements(ImagingMeasurementReportContentSequence) {
const ImagingMeasurements = ImagingMeasurementReportContentSequence.find(
item =>
item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.ImagingMeasurements
);
if (!ImagingMeasurements) {
return [];
}
const MeasurementGroups = _getSequenceAsArray(ImagingMeasurements.ContentSequence).filter(
item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.MeasurementGroup
);
const mergedContentSequencesByTrackingUniqueIdentifiers =
_getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups);
const measurements = [];
Object.keys(mergedContentSequencesByTrackingUniqueIdentifiers).forEach(
@@ -310,7 +400,6 @@ function _getMeasurements(ImagingMeasurementReportContentSequence) {
mergedContentSequencesByTrackingUniqueIdentifiers[trackingUniqueIdentifier];
const measurement = _processMeasurement(mergedContentSequence);
if (measurement) {
measurements.push(measurement);
}
@@ -320,6 +409,12 @@ function _getMeasurements(ImagingMeasurementReportContentSequence) {
return measurements;
}
/**
* Retrieves merged content sequences by tracking unique identifiers.
*
* @param {Array} MeasurementGroups - The measurement groups.
* @returns {Object} - The merged content sequences by tracking unique identifiers.
*/
function _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups) {
const mergedContentSequencesByTrackingUniqueIdentifiers = {};
@@ -331,7 +426,6 @@ function _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.TrackingUniqueIdentifier
);
if (!TrackingUniqueIdentifierItem) {
console.warn('No Tracking Unique Identifier, skipping ambiguous measurement.');
}
@@ -360,6 +454,15 @@ function _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups
return mergedContentSequencesByTrackingUniqueIdentifiers;
}
/**
* Processes the measurement based on the merged content sequence.
* If the merged content sequence contains SCOORD or SCOORD3D value types,
* it calls the _processTID1410Measurement function.
* Otherwise, it calls the _processNonGeometricallyDefinedMeasurement function.
*
* @param {Array<Object>} mergedContentSequence - The merged content sequence to process.
* @returns {any} - The processed measurement result.
*/
function _processMeasurement(mergedContentSequence) {
if (
mergedContentSequence.some(
@@ -372,11 +475,21 @@ function _processMeasurement(mergedContentSequence) {
return _processNonGeometricallyDefinedMeasurement(mergedContentSequence);
}
/**
* Processes TID 1410 style measurements from the mergedContentSequence.
* TID 1410 style measurements have a SCOORD or SCOORD3D at the top level,
* and non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D.
*
* @param mergedContentSequence - The merged content sequence containing the measurements.
* @returns The measurement object containing the loaded status, labels, coordinates, tracking unique identifier, and tracking identifier.
*/
function _processTID1410Measurement(mergedContentSequence) {
// Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level,
// And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D
const graphicItem = mergedContentSequence.find(group => group.ValueType === 'SCOORD');
const graphicItem = mergedContentSequence.find(
group => group.ValueType === 'SCOORD' || group.ValueType === 'SCOORD3D'
);
const UIDREFContentItem = mergedContentSequence.find(group => group.ValueType === 'UIDREF');
@@ -403,7 +516,6 @@ function _processTID1410Measurement(mergedContentSequence) {
NUMContentItems.forEach(item => {
const { ConceptNameCodeSequence, MeasuredValueSequence } = item;
if (MeasuredValueSequence) {
measurement.labels.push(
_getLabelFromMeasuredValueSequence(ConceptNameCodeSequence, MeasuredValueSequence)
@@ -411,12 +523,29 @@ function _processTID1410Measurement(mergedContentSequence) {
}
});
const findingSites = mergedContentSequence.filter(
item =>
item.ConceptNameCodeSequence.CodingSchemeDesignator === CodingSchemeDesignators.SCT &&
item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.FindingSiteSCT
);
if (findingSites.length) {
measurement.labels.push({
label: CodeNameCodeSequenceValues.FindingSiteSCT,
value: findingSites[0].ConceptCodeSequence.CodeMeaning,
});
}
return measurement;
}
/**
* Processes the non-geometrically defined measurement from the merged content sequence.
*
* @param mergedContentSequence The merged content sequence containing the measurement data.
* @returns The processed measurement object.
*/
function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
const NUMContentItems = mergedContentSequence.filter(group => group.ValueType === 'NUM');
const UIDREFContentItem = mergedContentSequence.find(group => group.ValueType === 'UIDREF');
const TrackingIdentifierContentItem = mergedContentSequence.find(
@@ -446,10 +575,10 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
CodingSchemeDesignators.CornerstoneCodeSchemes.includes(
finding.ConceptCodeSequence.CodingSchemeDesignator
) &&
finding.ConceptCodeSequence.CodeValue === CodeNameCodeSequenceValues.CornerstoneFreeText
finding.ConceptCodeSequence.CodeValue === Cornerstone3DCodeScheme.codeValues.CORNERSTONEFREETEXT
) {
measurement.labels.push({
label: CORNERSTONE_FREETEXT_CODE_VALUE,
label: Cornerstone3DCodeScheme.codeValues.CORNERSTONEFREETEXT,
value: finding.ConceptCodeSequence.CodeMeaning,
});
}
@@ -461,12 +590,13 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
CodingSchemeDesignators.CornerstoneCodeSchemes.includes(
FindingSite.ConceptCodeSequence.CodingSchemeDesignator
) &&
FindingSite.ConceptCodeSequence.CodeValue === CodeNameCodeSequenceValues.CornerstoneFreeText
FindingSite.ConceptCodeSequence.CodeValue ===
Cornerstone3DCodeScheme.codeValues.CORNERSTONEFREETEXT
);
if (cornerstoneFreeTextFindingSite) {
measurement.labels.push({
label: CORNERSTONE_FREETEXT_CODE_VALUE,
label: Cornerstone3DCodeScheme.codeValues.CORNERSTONEFREETEXT,
value: cornerstoneFreeTextFindingSite.ConceptCodeSequence.CodeMeaning,
});
}
@@ -476,15 +606,12 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
const { ConceptNameCodeSequence, ContentSequence, MeasuredValueSequence } = item;
const { ValueType } = ContentSequence;
if (!ValueType === 'SCOORD') {
console.warn(`Graphic ${ValueType} not currently supported, skipping annotation.`);
return;
}
const coords = _getCoordsFromSCOORDOrSCOORD3D(ContentSequence);
if (coords) {
measurement.coords.push(coords);
}
@@ -499,59 +626,62 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
return measurement;
}
function _getCoordsFromSCOORDOrSCOORD3D(item) {
const { ValueType, RelationshipType, GraphicType, GraphicData } = item;
if (
!(
RelationshipType == RELATIONSHIP_TYPE.INFERRED_FROM ||
RelationshipType == RELATIONSHIP_TYPE.CONTAINS
)
) {
console.warn(
`Relationshiptype === ${RelationshipType}. Cannot deal with NON TID-1400 SCOORD group with RelationshipType !== "INFERRED FROM" or "CONTAINS"`
);
return;
}
/**
* Extracts coordinates from a graphic item of type SCOORD or SCOORD3D.
* @param {object} graphicItem - The graphic item containing the coordinates.
* @returns {object} - The extracted coordinates.
*/
const _getCoordsFromSCOORDOrSCOORD3D = graphicItem => {
const { ValueType, GraphicType, GraphicData } = graphicItem;
const coords = { ValueType, GraphicType, GraphicData };
// ContentSequence has length of 1 as RelationshipType === 'INFERRED FROM'
if (ValueType === 'SCOORD') {
const { ReferencedSOPSequence } = item.ContentSequence;
coords.ReferencedSOPSequence = ReferencedSOPSequence;
} else if (ValueType === 'SCOORD3D') {
const { ReferencedFrameOfReferenceSequence } = item.ContentSequence;
coords.ReferencedFrameOfReferenceSequence = ReferencedFrameOfReferenceSequence;
}
coords.ReferencedSOPSequence = graphicItem.ContentSequence?.ReferencedSOPSequence;
coords.ReferencedFrameOfReferenceSequence =
graphicItem.ReferencedFrameOfReferenceUID ||
graphicItem.ContentSequence?.ReferencedFrameOfReferenceSequence;
return coords;
}
};
/**
* Retrieves the label and value from the provided ConceptNameCodeSequence and MeasuredValueSequence.
* @param {Object} ConceptNameCodeSequence - The ConceptNameCodeSequence object.
* @param {Object} MeasuredValueSequence - The MeasuredValueSequence object.
* @returns {Object} - An object containing the label and value.
* The label represents the CodeMeaning from the ConceptNameCodeSequence.
* The value represents the formatted NumericValue and CodeValue from the MeasuredValueSequence.
* Example: { label: 'Long Axis', value: '31.00 mm' }
*/
function _getLabelFromMeasuredValueSequence(ConceptNameCodeSequence, MeasuredValueSequence) {
const { CodeMeaning } = ConceptNameCodeSequence;
const { NumericValue, MeasurementUnitsCodeSequence } = MeasuredValueSequence;
const { CodeValue } = MeasurementUnitsCodeSequence;
const formatedNumericValue = NumericValue ? Number(NumericValue).toFixed(2) : '';
return {
label: CodeMeaning,
value: `${formatedNumericValue} ${CodeValue}`,
}; // E.g. Long Axis: 31.0 mm
}
/**
* Retrieves a list of referenced images from the Imaging Measurement Report Content Sequence.
*
* @param {Array} ImagingMeasurementReportContentSequence - The Imaging Measurement Report Content Sequence.
* @returns {Array} - The list of referenced images.
*/
function _getReferencedImagesList(ImagingMeasurementReportContentSequence) {
const ImageLibrary = ImagingMeasurementReportContentSequence.find(
item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.ImageLibrary
);
if (!ImageLibrary) {
return [];
}
const ImageLibraryGroup = _getSequenceAsArray(ImageLibrary.ContentSequence).find(
item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.ImageLibraryGroup
);
if (!ImageLibraryGroup) {
return [];
}
const referencedImages = [];
@@ -575,6 +705,15 @@ function _getReferencedImagesList(ImagingMeasurementReportContentSequence) {
return referencedImages;
}
/**
* Converts a DICOM sequence to an array.
* If the sequence is null or undefined, an empty array is returned.
* If the sequence is already an array, it is returned as is.
* Otherwise, the sequence is wrapped in an array and returned.
*
* @param {any} sequence - The DICOM sequence to convert.
* @returns {any[]} - The converted array.
*/
function _getSequenceAsArray(sequence) {
if (!sequence) {
return [];
+4 -1
View File
@@ -5,4 +5,7 @@ const id = packageJson.name;
const SOPClassHandlerName = 'dicom-sr';
const SOPClassHandlerId = `${id}.sopClassHandlerModule.${SOPClassHandlerName}`;
export { SOPClassHandlerName, SOPClassHandlerId, id };
const SOPClassHandlerName3D = 'dicom-sr-3d';
const SOPClassHandlerId3D = `${id}.sopClassHandlerModule.${SOPClassHandlerName3D}`;
export { SOPClassHandlerName, SOPClassHandlerId, SOPClassHandlerName3D, SOPClassHandlerId3D, id };
@@ -1,6 +1,6 @@
import React from 'react';
import getSopClassHandlerModule from './getSopClassHandlerModule';
import getHangingProtocolModule, { srProtocol } from './getHangingProtocolModule';
import { srProtocol } from './getHangingProtocolModule';
import onModeEnter from './onModeEnter';
import getCommandsModule from './commandsModule';
import preRegistration from './init';
@@ -8,9 +8,10 @@ import { id } from './id.js';
import toolNames from './tools/toolNames';
import hydrateStructuredReport from './utils/hydrateStructuredReport';
import createReferencedImageDisplaySet from './utils/createReferencedImageDisplaySet';
import Enums from './enums';
const Component = React.lazy(() => {
return import(/* webpackPrefetch: true */ './viewports/OHIFCornerstoneSRViewport');
return import(/* webpackPrefetch: true */ './components/OHIFCornerstoneSRViewport');
});
const OHIFCornerstoneSRViewport = props => {
@@ -54,7 +55,7 @@ const dicomSRExtension = {
},
getCommandsModule,
getSopClassHandlerModule,
// Include dynmically computed values such as toolNames not known till instantiation
// Include dynamically computed values such as toolNames not known till instantiation
getUtilityModule({ servicesManager }) {
return [
{
@@ -70,4 +71,4 @@ const dicomSRExtension = {
export default dicomSRExtension;
// Put static exports here so they can be type checked
export { hydrateStructuredReport, createReferencedImageDisplaySet, srProtocol };
export { hydrateStructuredReport, createReferencedImageDisplaySet, srProtocol, Enums, toolNames };
+62 -8
View File
@@ -1,5 +1,4 @@
import {
addTool,
AngleTool,
annotation,
ArrowAnnotateTool,
@@ -9,34 +8,64 @@ import {
CircleROITool,
LengthTool,
PlanarFreehandROITool,
RectangleROITool,
utilities as csToolsUtils,
} from '@cornerstonejs/tools';
import { Types, MeasurementService } from '@ohif/core';
import { StackViewport, utilities as csUtils } from '@cornerstonejs/core';
import { Enums as CSExtensionEnums } from '@ohif/extension-cornerstone';
import DICOMSRDisplayTool from './tools/DICOMSRDisplayTool';
import SCOORD3DPointTool from './tools/SCOORD3DPointTool';
import SRSCOOR3DProbeMapper from './utils/SRSCOOR3DProbeMapper';
import addToolInstance from './utils/addToolInstance';
import { Types } from '@ohif/core';
import toolNames from './tools/toolNames';
const { CORNERSTONE_3D_TOOLS_SOURCE_NAME, CORNERSTONE_3D_TOOLS_SOURCE_VERSION } = CSExtensionEnums;
/**
* @param {object} configuration
*/
export default function init({ configuration = {} }: Types.Extensions.ExtensionParams): void {
addTool(DICOMSRDisplayTool);
addToolInstance(toolNames.SRLength, LengthTool, {});
export default function init({
configuration = {},
servicesManager,
}: Types.Extensions.ExtensionParams): void {
const { measurementService, cornerstoneViewportService } = servicesManager.services;
addToolInstance(toolNames.DICOMSRDisplay, DICOMSRDisplayTool);
addToolInstance(toolNames.SRLength, LengthTool);
addToolInstance(toolNames.SRBidirectional, BidirectionalTool);
addToolInstance(toolNames.SREllipticalROI, EllipticalROITool);
addToolInstance(toolNames.SRCircleROI, CircleROITool);
addToolInstance(toolNames.SRArrowAnnotate, ArrowAnnotateTool);
addToolInstance(toolNames.SRAngle, AngleTool);
addToolInstance(toolNames.SRPlanarFreehandROI, PlanarFreehandROITool);
addToolInstance(toolNames.SRRectangleROI, RectangleROITool);
addToolInstance(toolNames.SRSCOORD3DPoint, SCOORD3DPointTool);
// TODO - fix the SR display of Cobb Angle, as it joins the two lines
addToolInstance(toolNames.SRCobbAngle, CobbAngleTool);
// TODO - fix the rehydration of Freehand, as it throws an exception
// on a missing polyline. The fix is probably in CS3D
addToolInstance(toolNames.SRPlanarFreehandROI, PlanarFreehandROITool);
const csTools3DVer1MeasurementSource = measurementService.getSource(
CORNERSTONE_3D_TOOLS_SOURCE_NAME,
CORNERSTONE_3D_TOOLS_SOURCE_VERSION
);
const { POINT } = measurementService.VALUE_TYPES;
measurementService.addMapping(
csTools3DVer1MeasurementSource,
'SRSCOORD3DPoint',
POINT,
SRSCOOR3DProbeMapper.toAnnotation,
SRSCOOR3DProbeMapper.toMeasurement
);
// Modify annotation tools to use dashed lines on SR
const dashedLine = {
lineDash: '4,4',
};
annotation.config.style.setToolGroupToolStyles('SRToolGroup', {
[toolNames.DICOMSRDisplay]: dashedLine,
SRLength: dashedLine,
SRBidirectional: dashedLine,
SREllipticalROI: dashedLine,
@@ -45,6 +74,31 @@ export default function init({ configuration = {} }: Types.Extensions.ExtensionP
SRCobbAngle: dashedLine,
SRAngle: dashedLine,
SRPlanarFreehandROI: dashedLine,
SRRectangleROI: dashedLine,
global: {},
});
measurementService.subscribe(
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_LAYOUT,
({ viewportId, measurement, isConsumed }) => {
if (isConsumed) {
return;
}
try {
const currentViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
const { viewPlaneNormal } = currentViewport.getCamera();
const referencedImageId = csToolsUtils.getClosestImageIdForStackViewport(
currentViewport as StackViewport,
measurement.points[0],
viewPlaneNormal
);
const imageIndex = (currentViewport as StackViewport)
.getImageIds()
.indexOf(referencedImageId);
csUtils.jumpToSlice(currentViewport.element, { imageIndex });
} catch (error) {
console.warn('Unable to jump to image based on measurement coordinate', error);
}
}
);
}
@@ -1,11 +1,11 @@
import { SOPClassHandlerId } from './id';
import { SOPClassHandlerId, SOPClassHandlerId3D } from './id';
export default function onModeEnter({ servicesManager }) {
const { displaySetService } = servicesManager.services;
const displaySetCache = displaySetService.getDisplaySetCache();
const srDisplaySets = [...displaySetCache.values()].filter(
ds => ds.SOPClassHandlerId === SOPClassHandlerId
ds => ds.SOPClassHandlerId === SOPClassHandlerId || ds.SOPClassHandlerId === SOPClassHandlerId3D
);
srDisplaySets.forEach(ds => {
@@ -7,10 +7,11 @@ import {
Types as cs3DToolsTypes,
} from '@cornerstonejs/tools';
import { getTrackingUniqueIdentifiersForElement } from './modules/dicomSRModule';
import SCOORD_TYPES from '../constants/scoordTypes';
import { SCOORDTypes } from '../enums';
import toolNames from './toolNames';
export default class DICOMSRDisplayTool extends AnnotationTool {
static toolName = 'DICOMSRDisplay';
static toolName = toolNames.DICOMSRDisplay;
constructor(
toolProps = {},
@@ -22,14 +23,14 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
}
_getTextBoxLinesFromLabels(labels) {
// TODO -> max 3 for now (label + shortAxis + longAxis), need a generic solution for this!
// TODO -> max 5 for now (label + shortAxis + longAxis), need a generic solution for this!
const labelLength = Math.min(labels.length, 3);
const labelLength = Math.min(labels.length, 5);
const lines = [];
for (let i = 0; i < labelLength; i++) {
const labelEntry = labels[i];
lines.push(`${_labelToShorthand(labelEntry.label)}${labelEntry.value}`);
lines.push(`${_labelToShorthand(labelEntry.label)}: ${labelEntry.value}`);
}
return lines;
@@ -65,7 +66,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
// Filter toolData to only render the data for the active SR.
const filteredAnnotations = annotations.filter(annotation =>
trackingUniqueIdentifiers.includes(annotation.data?.cachedStats?.TrackingUniqueIdentifier)
trackingUniqueIdentifiers.includes(annotation.data?.TrackingUniqueIdentifier)
);
if (!viewport._actors?.size) {
@@ -77,20 +78,24 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
toolName: this.getToolName(),
viewportId: enabledElement.viewport.id,
};
const { style: annotationStyle } = annotation.config;
for (let i = 0; i < filteredAnnotations.length; i++) {
const annotation = filteredAnnotations[i];
const annotationUID = annotation.annotationUID;
const { renderableData } = annotation.data.cachedStats;
const { cachedStats } = annotation.data;
const { renderableData, TrackingUniqueIdentifier } = annotation.data;
const { referencedImageId } = annotation.metadata;
styleSpecifier.annotationUID = annotationUID;
const groupStyle = annotationStyle.getToolGroupToolStyles(this.toolGroupId)[
this.getToolName()
];
const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
const color =
cachedStats.TrackingUniqueIdentifier === activeTrackingUniqueIdentifier
TrackingUniqueIdentifier === activeTrackingUniqueIdentifier
? 'rgb(0, 255, 0)'
: this.getStyle('color', styleSpecifier, annotation);
@@ -98,6 +103,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
color,
lineDash,
lineWidth,
...groupStyle,
};
Object.keys(renderableData).forEach(GraphicType => {
@@ -107,19 +113,19 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
let canvasCoordinatesAdapter;
switch (GraphicType) {
case SCOORD_TYPES.POINT:
case SCOORDTypes.POINT:
renderMethod = this.renderPoint;
break;
case SCOORD_TYPES.MULTIPOINT:
case SCOORDTypes.MULTIPOINT:
renderMethod = this.renderMultipoint;
break;
case SCOORD_TYPES.POLYLINE:
case SCOORDTypes.POLYLINE:
renderMethod = this.renderPolyLine;
break;
case SCOORD_TYPES.CIRCLE:
case SCOORDTypes.CIRCLE:
renderMethod = this.renderEllipse;
break;
case SCOORD_TYPES.ELLIPSE:
case SCOORDTypes.ELLIPSE:
renderMethod = this.renderEllipse;
canvasCoordinatesAdapter = utilities.math.ellipse.getCanvasEllipseCorners;
break;
@@ -160,6 +166,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
const drawingOptions = {
color: options.color,
width: options.lineWidth,
lineDash: options.lineDash,
};
let allCanvasCoordinates = [];
renderableData.map((data, index) => {
@@ -223,7 +230,11 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
// This gives us one point for arrow
canvasCoordinates.push(viewport.worldToCanvas(point));
// We get the other point for the arrow by using the image size
if (data[1] !== undefined) {
canvasCoordinates.push(viewport.worldToCanvas(data[1]));
}
else{
// We get the other point for the arrow by using the image size
const imagePixelModule = metaData.get('imagePixelModule', referencedImageId);
let xOffset = 10;
@@ -242,6 +253,9 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
]);
canvasCoordinates.push(viewport.worldToCanvas(arrowEnd));
}
const arrowUID = `${index}`;
@@ -307,6 +321,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
{
color: options.color,
width: options.lineWidth,
lineDash: options.lineDash,
}
);
});
@@ -328,7 +343,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
}
const { annotationUID, data = {} } = annotation;
const { label } = data;
const { labels } = data;
const { color } = options;
let adaptedCanvasCoordinates = canvasCoordinates;
@@ -336,10 +351,12 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
if (typeof canvasCoordinatesAdapter === 'function') {
adaptedCanvasCoordinates = canvasCoordinatesAdapter(canvasCoordinates);
}
const textLines = this._getTextBoxLinesFromLabels(label);
const textLines = this._getTextBoxLinesFromLabels(labels);
const canvasTextBoxCoords = utilities.drawing.getTextBoxCoordsCanvas(adaptedCanvasCoordinates);
annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld(canvasTextBoxCoords);
if (!annotation.data?.handles?.textBox?.worldPosition) {
annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld(canvasTextBoxCoords);
}
const textBoxPosition = viewport.worldToCanvas(annotation.data.handles.textBox.worldPosition);
@@ -0,0 +1,203 @@
import { Types, metaData, utilities as csUtils } from '@cornerstonejs/core';
import {
annotation,
drawing,
utilities,
Types as cs3DToolsTypes,
AnnotationDisplayTool,
} from '@cornerstonejs/tools';
import toolNames from './toolNames';
import { Annotation } from '@cornerstonejs/tools/dist/types/types';
export default class SCOORD3DPointTool extends AnnotationDisplayTool {
static toolName = toolNames.SRSCOORD3DPoint;
constructor(
toolProps = {},
defaultToolProps = {
configuration: {},
}
) {
super(toolProps, defaultToolProps);
}
_getTextBoxLinesFromLabels(labels) {
// TODO -> max 5 for now (label + shortAxis + longAxis), need a generic solution for this!
const labelLength = Math.min(labels.length, 5);
const lines = [];
return lines;
}
// This tool should not inherit from AnnotationTool and we should not need
// to add the following lines.
isPointNearTool = () => null;
getHandleNearImagePoint = () => null;
renderAnnotation = (enabledElement: Types.IEnabledElement, svgDrawingHelper: any): void => {
const { viewport } = enabledElement;
const { element } = viewport;
const annotations = annotation.state.getAnnotations(this.getToolName(), element);
// Todo: We don't need this anymore, filtering happens in triggerAnnotationRender
if (!annotations?.length) {
return;
}
// Filter toolData to only render the data for the active SR.
const filteredAnnotations = annotations;
if (!viewport._actors?.size) {
return;
}
const styleSpecifier: cs3DToolsTypes.AnnotationStyle.StyleSpecifier = {
toolGroupId: this.toolGroupId,
toolName: this.getToolName(),
viewportId: enabledElement.viewport.id,
};
for (let i = 0; i < filteredAnnotations.length; i++) {
const annotation = filteredAnnotations[i];
const annotationUID = annotation.annotationUID;
const { renderableData } = annotation.data;
const { POINT: points } = renderableData;
styleSpecifier.annotationUID = annotationUID;
const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
const color = this.getStyle('color', styleSpecifier, annotation);
const options = {
color,
lineDash,
lineWidth,
};
const point = points[0][0];
// check if viewport can render it
const viewable = viewport.isReferenceViewable(
{ FrameOfReferenceUID: annotation.metadata.FrameOfReferenceUID, cameraFocalPoint: point },
{ asNearbyProjection: true }
);
if (!viewable) {
continue;
}
// render the point
const arrowPointCanvas = viewport.worldToCanvas(point);
// Todo: configure this
const arrowEndCanvas = [arrowPointCanvas[0] + 20, arrowPointCanvas[1] + 20];
const canvasCoordinates = [arrowPointCanvas, arrowEndCanvas];
drawing.drawArrow(
svgDrawingHelper,
annotationUID,
'1',
canvasCoordinates[1],
canvasCoordinates[0],
{
color: options.color,
width: options.lineWidth,
}
);
this.renderTextBox(
svgDrawingHelper,
viewport,
canvasCoordinates,
annotation,
styleSpecifier,
options
);
}
};
renderTextBox(
svgDrawingHelper,
viewport,
canvasCoordinates,
annotation,
styleSpecifier,
options = {}
) {
if (!canvasCoordinates || !annotation) {
return;
}
const { annotationUID, data = {} } = annotation;
const { labels } = data;
const textLines = [];
for (const label of labels) {
// make this generic
// fix this
if (label.label === '363698007') {
textLines.push(`Finding Site: ${label.value}`);
}
}
const { color } = options;
const adaptedCanvasCoordinates = canvasCoordinates;
// adapt coordinates if there is an adapter
const canvasTextBoxCoords = utilities.drawing.getTextBoxCoordsCanvas(adaptedCanvasCoordinates);
if (!annotation.data?.handles?.textBox?.worldPosition) {
annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld(canvasTextBoxCoords);
}
const textBoxPosition = viewport.worldToCanvas(annotation.data.handles.textBox.worldPosition);
const textBoxUID = '1';
const textBoxOptions = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
const boundingBox = drawing.drawLinkedTextBox(
svgDrawingHelper,
annotationUID,
textBoxUID,
textLines,
textBoxPosition,
canvasCoordinates,
{},
{
...textBoxOptions,
color,
}
);
const { x: left, y: top, width, height } = boundingBox;
annotation.data.handles.textBox.worldBoundingBox = {
topLeft: viewport.canvasToWorld([left, top]),
topRight: viewport.canvasToWorld([left + width, top]),
bottomLeft: viewport.canvasToWorld([left, top + height]),
bottomRight: viewport.canvasToWorld([left + width, top + height]),
};
}
public getLinkedTextBoxStyle(
specifications: cs3DToolsTypes.AnnotationStyle.StyleSpecifier,
annotation?: Annotation
): Record<string, unknown> {
// Todo: this function can be used to set different styles for different toolMode
// for the textBox.
return {
visibility: this.getStyle('textBoxVisibility', specifications, annotation),
fontFamily: this.getStyle('textBoxFontFamily', specifications, annotation),
fontSize: this.getStyle('textBoxFontSize', specifications, annotation),
color: this.getStyle('textBoxColor', specifications, annotation),
shadow: this.getStyle('textBoxShadow', specifications, annotation),
background: this.getStyle('textBoxBackground', specifications, annotation),
lineWidth: this.getStyle('textBoxLinkLineWidth', specifications, annotation),
lineDash: this.getStyle('textBoxLinkLineDash', specifications, annotation),
};
}
}
@@ -1,7 +1,5 @@
import DICOMSRDisplayTool from './DICOMSRDisplayTool';
const toolNames = {
DICOMSRDisplay: DICOMSRDisplayTool.toolName,
DICOMSRDisplay: 'DICOMSRDisplay',
SRLength: 'SRLength',
SRBidirectional: 'SRBidirectional',
SREllipticalROI: 'SREllipticalROI',
@@ -11,6 +9,7 @@ const toolNames = {
SRCobbAngle: 'SRCobbAngle',
SRRectangleROI: 'SRRectangleROI',
SRPlanarFreehandROI: 'SRPlanarFreehandROI',
SRSCOORD3DPoint: 'SRSCOORD3DPoint',
};
export default toolNames;
@@ -0,0 +1,62 @@
const SRSCOOR3DProbe = {
toAnnotation: measurement => {},
/**
* Maps cornerstone annotation event data to measurement service format.
*
* @param {Object} cornerstone Cornerstone event data
* @return {Measurement} Measurement instance
*/
toMeasurement: (
csToolsEventDetail,
displaySetService,
CornerstoneViewportService,
getValueTypeFromToolType,
customizationService
) => {
const { annotation } = csToolsEventDetail;
const { metadata, data, annotationUID } = annotation;
if (!metadata || !data) {
console.warn('Probe tool: Missing metadata or data');
return null;
}
const { toolName } = metadata;
const { points } = data.handles;
const displayText = getDisplayText(annotation);
return {
uid: annotationUID,
points,
metadata,
toolName: metadata.toolName,
label: data.label,
displayText: displayText,
data: data.cachedStats,
type: getValueTypeFromToolType?.(toolName) ?? null,
};
},
};
function getDisplayText(annotation) {
const { data } = annotation;
if (!data) {
return [''];
}
const { labels } = data;
const displayText = [];
for (const label of labels) {
// make this generic
if (label.label === '33636980076') {
displayText.push(`Finding Site: ${label.value}`);
}
}
return displayText;
}
export default SRSCOOR3DProbe;
@@ -0,0 +1,67 @@
import { Types, annotation } from '@cornerstonejs/tools';
import { metaData } from '@cornerstonejs/core';
import getRenderableData from './getRenderableData';
import toolNames from '../tools/toolNames';
export default function addSRAnnotation(measurement, imageId, frameNumber) {
let toolName = toolNames.DICOMSRDisplay;
const renderableData = measurement.coords.reduce((acc, coordProps) => {
acc[coordProps.GraphicType] = acc[coordProps.GraphicType] || [];
acc[coordProps.GraphicType].push(getRenderableData({ ...coordProps, imageId }));
return acc;
}, {});
const { TrackingUniqueIdentifier } = measurement;
const { ValueType: valueType, GraphicType: graphicType } = measurement.coords[0];
const graphicTypePoints = renderableData[graphicType];
/** TODO: Read the tool name from the DICOM SR identification type in the future. */
let frameOfReferenceUID = null;
if (imageId) {
const imagePlaneModule = metaData.get('imagePlaneModule', imageId);
frameOfReferenceUID = imagePlaneModule?.frameOfReferenceUID;
}
if (valueType === 'SCOORD3D') {
toolName = toolNames.SRSCOORD3DPoint;
// get the ReferencedFrameOfReferenceUID from the measurement
frameOfReferenceUID = measurement.coords[0].ReferencedFrameOfReferenceSequence;
}
const SRAnnotation: Types.Annotation = {
annotationUID: TrackingUniqueIdentifier,
highlighted: false,
isLocked: false,
invalidated: false,
metadata: {
toolName,
valueType,
graphicType,
FrameOfReferenceUID: frameOfReferenceUID,
referencedImageId: imageId,
},
data: {
label: measurement.labels?.[0]?.value || undefined,
displayText: measurement.displayText || undefined,
handles: {
textBox: measurement.textBox ?? {},
points: graphicTypePoints[0],
},
cachedStats: {},
frameNumber,
renderableData,
TrackingUniqueIdentifier,
labels: measurement.labels,
},
};
/**
* const annotationManager = annotation.annotationState.getAnnotationManager();
* was not triggering annotation_added events.
*/
annotation.state.addAnnotation(SRAnnotation);
console.debug('Adding SR annotation:', SRAnnotation);
}
@@ -1,8 +1,14 @@
import { addTool } from '@cornerstonejs/tools';
export default function addToolInstance(name: string, toolClass, configuration?): void {
export default function addToolInstance(name: string, toolClass, configuration = {}): void {
class InstanceClass extends toolClass {
static toolName = name;
constructor(toolProps, defaultToolProps) {
toolProps.configuration = toolProps.configuration
? { ...toolProps.configuration, ...configuration }
: configuration;
super(toolProps, defaultToolProps);
}
}
addTool(InstanceClass);
}
@@ -61,6 +61,11 @@ const createReferencedImageDisplaySet = (displaySetService, displaySet) => {
const imageSet = new ImageSet(instances);
const instance = instances[0];
if (!instance) {
return;
}
imageSet.setAttributes({
displaySetInstanceUID: imageSet.uid, // create a local alias for the imageSet UID
SeriesDate: instance.SeriesDate,
@@ -44,7 +44,7 @@ const isStructuredReportSeries = series => {
};
/**
* Checkes if series1 is newer than series2
* Checks if series1 is newer than series2
*
* @param {Object} series1 - Series Metadata 1
* @param {Object} series2 - Series Metadata 2
@@ -0,0 +1,67 @@
import { utils } from '@ohif/core';
/**
* Formatters used to format each of the content items (SR "nodes") which can be
* text, code, UID ref, number, person name, date, time and date time. Each
* formatter must be a function with the following signature:
*
* [VALUE_TYPE]: (contentItem) => string
*
*/
const contentItemFormatters = {
TEXT: contentItem => contentItem.TextValue,
CODE: contentItem => contentItem.ConceptCodeSequence?.[0]?.CodeMeaning,
UIDREF: contentItem => contentItem.UID,
NUM: contentItem => {
const measuredValue = contentItem.MeasuredValueSequence?.[0];
if (!measuredValue) {
return;
}
const { NumericValue, MeasurementUnitsCodeSequence } = measuredValue;
const { CodeValue } = MeasurementUnitsCodeSequence;
return `${NumericValue} ${CodeValue}`;
},
PNAME: contentItem => {
const personName = contentItem.PersonName?.[0]?.Alphabetic;
return personName ? utils.formatPN(personName) : undefined;
},
DATE: contentItem => {
const { Date } = contentItem;
return Date ? utils.formatDate(Date) : undefined;
},
TIME: contentItem => {
const { Time } = contentItem;
return Time ? utils.formatTime(Time) : undefined;
},
DATETIME: contentItem => {
const { DateTime } = contentItem;
if (typeof DateTime !== 'string') {
return;
}
// 14 characters because it should be something like 20180614113714
if (DateTime.length < 14) {
return DateTime;
}
const dicomDate = DateTime.substring(0, 8);
const dicomTime = DateTime.substring(8, 14);
const formattedDate = utils.formatDate(dicomDate);
const formattedTime = utils.formatTime(dicomTime);
return `${formattedDate} ${formattedTime}`;
},
};
function formatContentItemValue(contentItem) {
const { ValueType } = contentItem;
const fnFormat = contentItemFormatters[ValueType];
return fnFormat ? fnFormat(contentItem) : `[${ValueType} is not supported]`;
}
export { formatContentItemValue as default, formatContentItemValue };
@@ -1,3 +1,7 @@
import { adaptersSR } from '@cornerstonejs/adapters';
const { CodeScheme: Cornerstone3DCodeScheme } = adaptersSR.Cornerstone3D;
/**
* Extracts the label from the toolData imported from dcmjs. We need to do this
* as dcmjs does not depeend on OHIF/the measurementService, it just produces data for cornestoneTools.
@@ -9,13 +13,15 @@
export default function getLabelFromDCMJSImportedToolData(toolData) {
const { findingSites = [], finding } = toolData;
let freeTextLabel = findingSites.find(fs => fs.CodeValue === 'CORNERSTONEFREETEXT');
let freeTextLabel = findingSites.find(
fs => fs.CodeValue === Cornerstone3DCodeScheme.codeValues.CORNERSTONEFREETEXT
);
if (freeTextLabel) {
return freeTextLabel.CodeMeaning;
}
if (finding && finding.CodeValue === 'CORNERSTONEFREETEXT') {
if (finding && finding.CodeValue === Cornerstone3DCodeScheme.codeValues.CORNERSTONEFREETEXT) {
return finding.CodeMeaning;
}
}
@@ -1,124 +1,49 @@
import { vec3 } from 'gl-matrix';
import { Types, annotation } from '@cornerstonejs/tools';
import { metaData, utilities, Types as csTypes } from '@cornerstonejs/core';
import toolNames from '../tools/toolNames';
import SCOORD_TYPES from '../constants/scoordTypes';
import { SCOORDTypes } from '../enums';
const EPSILON = 1e-4;
const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
export default function addMeasurement(measurement, imageId, displaySetInstanceUID) {
// TODO -> Render rotated ellipse .
const toolName = toolNames.DICOMSRDisplay;
const measurementData = {
TrackingUniqueIdentifier: measurement.TrackingUniqueIdentifier,
renderableData: {},
labels: measurement.labels,
imageId,
};
measurement.coords.forEach(coord => {
const { GraphicType, GraphicData } = coord;
if (measurementData.renderableData[GraphicType] === undefined) {
measurementData.renderableData[GraphicType] = [];
const getRenderableCoords = ({ GraphicData, ValueType, imageId }) => {
const renderableData = [];
if (ValueType === 'SCOORD3D') {
for (let i = 0; i < GraphicData.length; i += 3) {
renderableData.push([GraphicData[i], GraphicData[i + 1], GraphicData[i + 2]]);
}
} else {
for (let i = 0; i < GraphicData.length; i += 2) {
const worldPos = utilities.imageToWorldCoords(imageId, [GraphicData[i], GraphicData[i + 1]]);
renderableData.push(worldPos);
}
}
return renderableData;
};
measurementData.renderableData[GraphicType].push(
_getRenderableData(GraphicType, GraphicData, imageId, measurement.TrackingIdentifier)
);
});
// Use the metadata provider to grab its imagePlaneModule metadata
const imagePlaneModule = metaData.get('imagePlaneModule', imageId);
const annotationManager = annotation.state.getAnnotationManager();
// Create Cornerstone3D Annotation from measurement
const frameNumber =
(measurement.coords[0].ReferencedSOPSequence &&
measurement.coords[0].ReferencedSOPSequence[0]?.ReferencedFrameNumber) ||
1;
const SRAnnotation: Types.Annotation = {
annotationUID: measurement.TrackingUniqueIdentifier,
metadata: {
FrameOfReferenceUID: imagePlaneModule.frameOfReferenceUID,
toolName: toolName,
referencedImageId: imageId,
},
data: {
label: measurement.labels,
handles: {
textBox: {},
},
cachedStats: {
TrackingUniqueIdentifier: measurementData.TrackingUniqueIdentifier,
renderableData: measurementData.renderableData,
},
frameNumber: frameNumber,
},
};
annotationManager.addAnnotation(SRAnnotation);
measurement.loaded = true;
measurement.imageId = imageId;
measurement.displaySetInstanceUID = displaySetInstanceUID;
// Remove the unneeded coord now its processed, but keep the SOPInstanceUID.
// NOTE: We assume that each SCOORD in the MeasurementGroup maps onto one frame,
// It'd be super weird if it didn't anyway as a SCOORD.
measurement.ReferencedSOPInstanceUID =
measurement.coords[0].ReferencedSOPSequence.ReferencedSOPInstanceUID;
measurement.frameNumber = frameNumber;
delete measurement.coords;
}
function _getRenderableData(GraphicType, GraphicData, imageId, TrackingIdentifier) {
const [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
let renderableData: csTypes.Point3[];
function getRenderableData({ GraphicType, GraphicData, ValueType, imageId }) {
let renderableData = [];
switch (GraphicType) {
case SCOORD_TYPES.POINT:
case SCOORD_TYPES.MULTIPOINT:
case SCOORD_TYPES.POLYLINE:
renderableData = [];
for (let i = 0; i < GraphicData.length; i += 2) {
const worldPos = utilities.imageToWorldCoords(imageId, [
GraphicData[i],
GraphicData[i + 1],
]);
renderableData.push(worldPos);
}
case SCOORDTypes.POINT:
case SCOORDTypes.MULTIPOINT:
case SCOORDTypes.POLYLINE: {
renderableData = getRenderableCoords({ GraphicData, ValueType, imageId });
break;
case SCOORD_TYPES.CIRCLE: {
const pointsWorld = [];
for (let i = 0; i < GraphicData.length; i += 2) {
const worldPos = utilities.imageToWorldCoords(imageId, [
GraphicData[i],
GraphicData[i + 1],
]);
pointsWorld.push(worldPos);
}
}
case SCOORDTypes.CIRCLE: {
const pointsWorld: csTypes.Point3[] = getRenderableCoords({
GraphicData,
ValueType,
imageId,
});
// We do not have an explicit draw circle svg helper in Cornerstone3D at
// this time, but we can use the ellipse svg helper to draw a circle, so
// here we reshape the data for that purpose.
const center = pointsWorld[0];
const onPerimeter = pointsWorld[1];
const radius = vec3.distance(center, onPerimeter);
const imagePlaneModule = metaData.get('imagePlaneModule', imageId);
if (!imagePlaneModule) {
throw new Error('No imagePlaneModule found');
}
@@ -133,14 +58,12 @@ function _getRenderableData(GraphicType, GraphicData, imageId, TrackingIdentifie
// we need to get major/minor axis (which are both the same size major = minor)
// first axisStart
const firstAxisStart = vec3.create();
vec3.scaleAndAdd(firstAxisStart, center, columnCosines, radius);
const firstAxisEnd = vec3.create();
vec3.scaleAndAdd(firstAxisEnd, center, columnCosines, -radius);
// second axisStart
const secondAxisStart = vec3.create();
vec3.scaleAndAdd(secondAxisStart, center, rowCosines, radius);
@@ -156,21 +79,16 @@ function _getRenderableData(GraphicType, GraphicData, imageId, TrackingIdentifie
break;
}
case SCOORD_TYPES.ELLIPSE: {
case SCOORDTypes.ELLIPSE: {
// GraphicData is ordered as [majorAxisStartX, majorAxisStartY, majorAxisEndX, majorAxisEndY, minorAxisStartX, minorAxisStartY, minorAxisEndX, minorAxisEndY]
// But Cornerstone3D points are ordered as top, bottom, left, right for the
// ellipse so we need to identify if the majorAxis is horizontal or vertical
// and then choose the correct points to use for the ellipse.
const pointsWorld: csTypes.Point3[] = [];
for (let i = 0; i < GraphicData.length; i += 2) {
const worldPos = utilities.imageToWorldCoords(imageId, [
GraphicData[i],
GraphicData[i + 1],
]);
pointsWorld.push(worldPos);
}
const pointsWorld: csTypes.Point3[] = getRenderableCoords({
GraphicData,
ValueType,
imageId,
});
const majorAxisStart = vec3.fromValues(...pointsWorld[0]);
const majorAxisEnd = vec3.fromValues(...pointsWorld[1]);
@@ -220,3 +138,5 @@ function _getRenderableData(GraphicType, GraphicData, imageId, TrackingIdentifie
return renderableData;
}
export default getRenderableData;
@@ -3,14 +3,12 @@ import OHIF, { DicomMetadataStore } from '@ohif/core';
import getLabelFromDCMJSImportedToolData from './getLabelFromDCMJSImportedToolData';
import { adaptersSR } from '@cornerstonejs/adapters';
import { annotation as CsAnnotation } from '@cornerstonejs/tools';
const { locking } = CsAnnotation;
import { Enums as CSExtensionEnums } from '@ohif/extension-cornerstone';
const { locking } = CsAnnotation;
const { guid } = OHIF.utils;
const { MeasurementReport, CORNERSTONE_3D_TAG } = adaptersSR.Cornerstone3D;
const CORNERSTONE_3D_TOOLS_SOURCE_NAME = 'Cornerstone3DTools';
const CORNERSTONE_3D_TOOLS_SOURCE_VERSION = '0.1';
const { CORNERSTONE_3D_TOOLS_SOURCE_NAME, CORNERSTONE_3D_TOOLS_SOURCE_VERSION } = CSExtensionEnums;
const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
const convertCode = (codingValues, code) => {
@@ -43,15 +41,23 @@ const convertSites = (codingValues, sites) => {
*
*/
export default function hydrateStructuredReport(
{ servicesManager, extensionManager, appConfig },
{ servicesManager, extensionManager, appConfig }: withAppTypes,
displaySetInstanceUID
) {
const annotationManager = CsAnnotation.state.getAnnotationManager();
const disableEditing = appConfig?.disableEditing;
const dataSource = extensionManager.getActiveDataSource()[0];
const { measurementService, displaySetService, customizationService } = servicesManager.services;
const codingValues = customizationService.getCustomization('codingValues', {});
const { disableEditing } = customizationService.getCustomization(
'PanelMeasurement.disableEditing',
{
id: 'default.disableEditing',
disableEditing: false,
}
);
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
// TODO -> We should define a strict versioning somewhere.
@@ -90,7 +96,7 @@ export default function hydrateStructuredReport(
const datasetToUse = _mapLegacyDataSet(instance);
// Use dcmjs to generate toolState.
const storedMeasurementByAnnotationType = MeasurementReport.generateToolState(
let storedMeasurementByAnnotationType = MeasurementReport.generateToolState(
datasetToUse,
// NOTE: we need to pass in the imageIds to dcmjs since the we use them
// for the imageToWorld transformation. The following assumes that the order
@@ -101,6 +107,16 @@ export default function hydrateStructuredReport(
metaData
);
const onBeforeSRHydration =
customizationService.getModeCustomization('onBeforeSRHydration')?.value;
if (typeof onBeforeSRHydration === 'function') {
storedMeasurementByAnnotationType = onBeforeSRHydration({
storedMeasurementByAnnotationType,
displaySet,
});
}
// Filter what is found by DICOM SR to measurements we support.
const mappingDefinitions = mappings.map(m => m.annotationType);
const hydratableMeasurementsInSR = {};
@@ -4,7 +4,7 @@ const cornerstoneAdapters =
adaptersSR.Cornerstone3D.MeasurementReport.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
const CORNERSTONE_3D_TAG = cornerstoneAdapters.CORNERSTONE_3D_TAG;
const CORNERSTONE_3D_TAG = adaptersSR.Cornerstone3D.CORNERSTONE_3D_TAG;
/**
* Checks if the given `displaySet`can be rehydrated into the `measurementService`.
@@ -0,0 +1,8 @@
const path = require('path');
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
const SRC_DIR = path.join(__dirname, '../src');
const DIST_DIR = path.join(__dirname, '../dist');
module.exports = (env, argv) => {
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
};
@@ -0,0 +1,54 @@
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const path = require('path');
const webpackCommon = require('./../../../.webpack/webpack.base.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const pkg = require('./../package.json');
const ROOT_DIR = path.join(__dirname, '../');
const SRC_DIR = path.join(__dirname, '../src');
const DIST_DIR = path.join(__dirname, '../dist');
const ENTRY = {
app: `${SRC_DIR}/index.ts`,
};
const outputName = `ohif-${pkg.name.split('/').pop()}`;
module.exports = (env, argv) => {
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR, ENTRY });
return merge(commonConfig, {
stats: {
colors: true,
hash: true,
timings: true,
assets: true,
chunks: false,
chunkModules: false,
modules: false,
children: false,
warnings: true,
},
optimization: {
minimize: true,
sideEffects: true,
},
output: {
path: ROOT_DIR,
library: 'ohif-extension-cornerstone',
libraryTarget: 'umd',
filename: pkg.main,
},
externals: [/\b(vtk.js)/, /\b(dcmjs)/, /\b(gl-matrix)/, /^@ohif/, /^@cornerstonejs/],
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
new MiniCssExtractPlugin({
filename: `./dist/${outputName}.css`,
chunkFilename: `./dist/${outputName}.css`,
}),
],
});
};
Loaded 100 of 2602 files, more files were not shown because too many files have changed in this diff. Show more