Compare commits

...
Author SHA1 Message Date
ohif-bot f1b017dc08 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.3.7
 - @ohif/core@2.6.6
 - @ohif/viewer@3.8.15
2020-04-02 17:32:56 +00:00
81dcbe294d refactor: Reduce bundle size (#1575)
* async load cornerstoneWADOImageLoader chunk

* Lazy initializing of CornerstoneWADIImageLoader - delayed till Viewer creation

* Async loading of react-vtkjs-viewport

* withCornerstone HOC to wrap routes requiring CornerstoneWADOImageLoader initiazation

* removed dead code in comments

* initWeWorkers is no more. Remove al its references.

Co-authored-by: kedar.netelixir <kedar.choudhary@netelixir.com>
Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
2020-04-02 13:31:14 -04:00
Pierre-Antoine Ganaye e09a3d2e93 docs: Remove dead links in docs and improve SOP Class Handler docs (#1593)
* fix docs dead links

* add examples to SOP handler docs
2020-04-02 09:24:49 -04:00
ohif-bot 978a86bf12 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.5.1
 - @ohif/extension-dicom-html@1.1.2
 - @ohif/extension-dicom-microscopy@0.50.9
 - @ohif/extension-dicom-pdf@1.0.3
 - @ohif/viewer@3.8.14
2020-04-02 07:01:58 +00:00
0139a32213 chore: Switch asyncComponent to React Lazy/Suspense (Closes #1577) (#1582)
* Attempted Lazy load for OHIFCornerStoneViewport

* Converted dicom-(html, microscopy, pdf) & cornerstone extensions to React Lazy components

* Fixed incorrect import from OHIFDicomPDFSopClassHandler to ConnectedOHIFDicomPDFViewer

Co-authored-by: Ankit Mohan <ankit.mohan@live.in>
Co-authored-by: Erik Ziegler <erik.sweed@gmail.com>
2020-04-02 08:59:17 +02:00
ohif-bot d57158b59d chore(release): publish [skip ci]
- @ohif/extension-dicom-segmentation@0.1.1
 - @ohif/extension-vtk@1.3.6
 - @ohif/core@2.6.5
 - @ohif/viewer@3.8.13
2020-04-01 03:55:23 +00:00
Igor Octaviano 4a7ce1c093 fix: segmentation not loading (#1566)
* Fix bug

* Refactor the state of the component RTPanel
2020-03-31 23:54:02 -04:00
ohif-bot 333677c40e chore(release): publish [skip ci]
- @ohif/extension-dicom-microscopy@0.50.8
 - @ohif/viewer@3.8.12
2020-03-31 13:30:26 +00:00
James Petts b459b894bd Sm fix (#1585)
* Fix broken SM viewport.

* fix: 🐛 Fix broken SM viewport
2020-03-31 15:28:11 +02:00
Andrey Fedorov c5a4860209 update stale.yml to not close IDC related issues (#1573) 2020-03-29 16:10:11 -04:00
66 changed files with 554 additions and 487 deletions

No files matched your search

+3
View File
@@ -18,6 +18,9 @@ exemptLabels:
- 'Triage :white_flag:' - 'Triage :white_flag:'
- 'Extension: Discussion' - 'Extension: Discussion'
- 'Announcement 🎉' - 'Announcement 🎉'
- 'IDC:priority'
- 'IDC:candidate'
- 'IDC:collaboration'
# Label to use when marking an issue as stale # Label to use when marking an issue as stale
staleLabel: 'Stale :baguette_bread:' staleLabel: 'Stale :baguette_bread:'
# Comment to post when marking an issue as stale. Set to `false` to disable # Comment to post when marking an issue as stale. Set to `false` to disable
+1 -1
View File
@@ -48,7 +48,7 @@ Check out these helpful links:
--> -->
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
[ohif-org]: https://www.ohif.org [ohif-org]: http://www.ohif.org
[dicom-web]: https://en.wikipedia.org/wiki/DICOMweb [dicom-web]: https://en.wikipedia.org/wiki/DICOMweb
[gh-issues]: https://github.com/OHIF/Viewers/issues [gh-issues]: https://github.com/OHIF/Viewers/issues
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
+1
View File
@@ -5,6 +5,7 @@
- [Getting Started](development/getting-started.md) - [Getting Started](development/getting-started.md)
- [Contributing](development/contributing.md) - [Contributing](development/contributing.md)
- [Continuous Integration](development/continous-integration.md) - [Continuous Integration](development/continous-integration.md)
- [Testing](development/testing.md)
- [Configuring](configuring/index.md) - [Configuring](configuring/index.md)
- [Data Source](configuring/data-source.md) - [Data Source](configuring/data-source.md)
+5 -10
View File
@@ -13,7 +13,6 @@ architected, and the role each of it's dependent libraries plays.
- [Overview](#overview) - [Overview](#overview)
- [Business Logic](#business-logic) - [Business Logic](#business-logic)
- [Component Library](#react-component-library) - [Component Library](#react-component-library)
- [Internationalization](#internationalization-i18n)
- [Extensions & Configuration](#extensions--configuration) - [Extensions & Configuration](#extensions--configuration)
- [Common Questions](#common-questions) - [Common Questions](#common-questions)
@@ -74,10 +73,10 @@ This diagram is a conceptual illustration of how the Viewer is architected.
The [`@ohif/core`][core-github] project offers pre-packaged solutions for The [`@ohif/core`][core-github] project offers pre-packaged solutions for
features common to Web-based medical imaging viewers. For example: features common to Web-based medical imaging viewers. For example:
- [Hotkeys](#) - Hotkeys
- DICOM Web requests - DICOM Web requests
- Hanging Protocols - Hanging Protocols
- [Managing a study's measurements](../services/default/measurements.md) - Managing a study's measurements
- Managing a study's DICOM metadata - Managing a study's DICOM metadata
- [A flexible pattern for extensions](../extensions/index.md) - [A flexible pattern for extensions](../extensions/index.md)
- And many others - And many others
@@ -99,10 +98,6 @@ application.
[Check out our component library!](https://react.ohif.org/) [Check out our component library!](https://react.ohif.org/)
## Internationalization (i18n)
...
## Extensions & Configuration ## Extensions & Configuration
While OHIF maintains several high value and commonly requested features in its While OHIF maintains several high value and commonly requested features in its
@@ -120,7 +115,7 @@ the viewer. Some common use cases include:
We expose common integration points via [extensions](../extensions/index.md) to We expose common integration points via [extensions](../extensions/index.md) to
make this possible. The viewer and many of our own extensions also offer make this possible. The viewer and many of our own extensions also offer
[configuration][configuration]. For a list of extensions maintained by OHIF, [configuration][configuration]. For a list of extensions maintained by OHIF,
[check out this helpful table](../extensions/index.mdl#maintained-extensions). [check out this helpful table](../extensions/index.md#maintained-extensions).
If you find yourself thinking "I wish the Viewer could do X", and you can't If you find yourself thinking "I wish the Viewer could do X", and you can't
accomplish it with an extension today, create a GitHub issue! We're actively accomplish it with an extension today, create a GitHub issue! We're actively
@@ -147,12 +142,12 @@ you would need to recreate.
--> -->
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
[monorepo]: # [monorepo]: https://github.com/OHIF/Viewers/issues/768
[viewers-project]: https://github.com/OHIF/Viewers [viewers-project]: https://github.com/OHIF/Viewers
[viewer-npm]: https://www.npmjs.com/package/@ohif/viewer [viewer-npm]: https://www.npmjs.com/package/@ohif/viewer
[pwa]: https://developers.google.com/web/progressive-web-apps/ [pwa]: https://developers.google.com/web/progressive-web-apps/
[configuration]: ../configuring/index.md [configuration]: ../configuring/index.md
[extensions]: ../extensions/index.md [extensions]: ../extensions/index.md
[core-github]: https://github.com/OHIF/viewers/platform/core [core-github]: https://github.com/OHIF/viewers/platform/core
[ui-github]: https://github.com/OHIF/Viewers/platform/ui [ui-github]: https://github.com/OHIF/Viewers/tree/master/platform/ui
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
+7 -7
View File
@@ -123,7 +123,7 @@ There are two important steps to making sure this setup works:
Most image archives do not provide either of these features "out of the box". Most image archives do not provide either of these features "out of the box".
It's common to use IIS, Nginx, or Apache to route incoming requests and append It's common to use IIS, Nginx, or Apache to route incoming requests and append
appropriate headers. You can find an example of this setup in our appropriate headers. You can find an example of this setup in our
[Nginx + Image Archive Deployment Recipe](deployment/recipes/nginx--image-archive.md). [Nginx + Image Archive Deployment Recipe](./recipes/nginx--image-archive.md).
#### What if my archive doesn't support DicomWeb? #### What if my archive doesn't support DicomWeb?
@@ -166,7 +166,7 @@ window.config = {
``` ```
You can find an example of this setup in our You can find an example of this setup in our
[User Account Control Deployment Recipe](deployment/recipes/user-account-control.md). [User Account Control Deployment Recipe](./recipes/user-account-control.md).
#### Choosing a Flow for the Viewer #### Choosing a Flow for the Viewer
@@ -183,12 +183,12 @@ many possible configurations, so please don't feel limited to these setups.
Please feel free to suggest or contribute your own recipes. Please feel free to suggest or contribute your own recipes.
- Script Include - Script Include
- [Embedding the Viewer](deployment/recipes/embedded-viewer.md) - [Embedding the Viewer](./recipes/embedded-viewer.md)
- Stand-Alone - Stand-Alone
- [Build for Production](deployment/recipes/build-for-production.md) - [Build for Production](./recipes/build-for-production.md)
- [Static](deployment/recipes/static-assets.md) - [Static](./recipes/static-assets.md)
- [Nginx + Image Archive](deployment/recipes/nginx--image-archive.md) - [Nginx + Image Archive](./recipes/nginx--image-archive.md)
- [User Account Control](deployment/recipes/user-account-control.md) - [User Account Control](./recipes/user-account-control.md)
<!-- <!--
Links Links
@@ -50,7 +50,7 @@ window.config = {
``` ```
To learn more about how you can configure the OHIF Viewer, check out our To learn more about how you can configure the OHIF Viewer, check out our
[Configuration Guide](./index.md). [Configuration Guide](../../configuring/index.md).
<ol start="3"><li> <ol start="3"><li>
Render the viewer in the web page's target <code>div</code> Render the viewer in the web page's target <code>div</code>
@@ -49,9 +49,7 @@ We can solve this one of two ways:
1. Have our Image Archive located at the same domain as our Web App 1. Have our Image Archive located at the same domain as our Web App
2. Add appropriate `Access-Control-Allow-*` HTTP headers 2. Add appropriate `Access-Control-Allow-*` HTTP headers
This solution uses the first approach, but you can see an example of the second **This solution uses the first approach.**
in the `docker-compose` bundled with this project for local development:
[HERE](https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/Nginx-Orthanc/docker-compose.yml)
You can read more about CORS in this Medium article: [Understanding You can read more about CORS in this Medium article: [Understanding
CORS][understanding-cors] CORS][understanding-cors]
@@ -121,7 +119,10 @@ likely want to update:
#### OHIF Viewer #### OHIF Viewer
The OHIF Viewer's configuration is imported from a static `.js` file. The configuration we use is set to a specific file when we build the viewer, and determined by the env variable: `APP_CONFIG`. You can see where we set its value in the `dockerfile` for this solution: The OHIF Viewer's configuration is imported from a static `.js` file. The
configuration we use is set to a specific file when we build the viewer, and
determined by the env variable: `APP_CONFIG`. You can see where we set its value
in the `dockerfile` for this solution:
`ENV APP_CONFIG=config/docker_openresty-orthanc.js` `ENV APP_CONFIG=config/docker_openresty-orthanc.js`
@@ -138,11 +139,11 @@ Viewer's configuration, you can run:
All other files are found in: `/docker/OpenResty-Orthanc/` All other files are found in: `/docker/OpenResty-Orthanc/`
| Service | Configuration | Docs | | Service | Configuration | Docs |
| ----------------- | ---------------------------------------------- | ------------------------------------------- | | ----------------- | --------------------------------- | ------------------------------------------- |
| OHIF Viewer | [dockerfile][dockerfile] / [config.js][config] | You're reading them now! | | OHIF Viewer | [dockerfile][dockerfile] | You're reading them now! |
| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] | | OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] |
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] | | Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
## Next Steps ## Next Steps
@@ -218,10 +219,11 @@ following resources helpful:
- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/) - [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/)
- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/) - [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/)
For a different take on this setup, check out the repository one of our For a different take on this setup, check out the repositories our community
community members put together: members put together:
- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker) - [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker)
- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker)
<!-- <!--
Links Links
@@ -234,8 +236,7 @@ community members put together:
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration [orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc [lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
<!-- SRC --> <!-- SRC -->
[config]: # [dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/dockerfile
[dockerfile]: # [config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/nginx.conf
[config-nginx]: # [config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/orthanc.json
[config-orthanc]: #
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
@@ -122,7 +122,10 @@ likely want to update:
#### OHIF Viewer #### OHIF Viewer
The OHIF Viewer's configuration is imported from a static `.js` file. The configuration we use is set to a specific file when we build the viewer, and determined by the env variable: `APP_CONFIG`. You can see where we set its value in the `dockerfile` for this solution: The OHIF Viewer's configuration is imported from a static `.js` file. The
configuration we use is set to a specific file when we build the viewer, and
determined by the env variable: `APP_CONFIG`. You can see where we set its value
in the `dockerfile` for this solution:
`ENV APP_CONFIG=config/docker_openresty-orthanc-keycloak.js` `ENV APP_CONFIG=config/docker_openresty-orthanc-keycloak.js`
@@ -146,8 +149,8 @@ All other files are found in: `/docker/OpenResty-Orthanc-Keycloak/`
| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] | | Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] |
| Keycloak | [`/ohif-keycloak-realm.json`][config-keycloak]\* | | | Keycloak | [`/ohif-keycloak-realm.json`][config-keycloak]\* | |
- \* These are the seed values for Keycloak. They can be manually updated at \* These are the seed values for Keycloak. They can be manually updated at
`http://127.0.0.1/auth/admin` `http://127.0.0.1/auth/admin`
#### Keycloak Themeing #### Keycloak Themeing
@@ -261,12 +264,12 @@ for OAuth:
- [Diagrams of OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660) - [Diagrams of OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660)
- [KeyCloak: OpenID Connect Flows](https://www.keycloak.org/docs/latest/securing_apps/index.html#authorization-code) - [KeyCloak: OpenID Connect Flows](https://www.keycloak.org/docs/latest/securing_apps/index.html#authorization-code)
- [Good description on SSO Protocols](https://www.keycloak.org/docs/2.5/server_admin/topics/sso-protocols/oidc.html)
For a different take on this setup, check out the repository one of our For a different take on this setup, check out the repositories our community
community members put together: members put together:
- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker) - [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker)
- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker)
<!-- <!--
Links Links
@@ -278,8 +281,8 @@ community members put together:
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc [lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
<!-- SRC --> <!-- SRC -->
[config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/src/config.js [config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/src/config.js
[dockerfile]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/.recipes [dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/dockerfile
[config-nginx]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/.recipes [config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/nginx.conf
[config-orthanc]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/.recipes [config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/orthanc.json
[config-keycloak]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/.recipes [config-keycloak]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/ohif-keycloak-realm.json
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
+2 -2
View File
@@ -114,8 +114,8 @@ the tips above will help ensure your changes are reviewed.
## Testing contribution pull requests ## Testing contribution pull requests
OHIF uses [netlify](netlify.com) so that pull requests are autogenerated and OHIF uses [netlify](https://www.netlify.com/) so that pull requests are
available for testing. autogenerated and available for testing.
For example, [this url][example-url] allows you to test [pull request 237, the For example, [this url][example-url] allows you to test [pull request 237, the
request that created this FAQ entry,][pr-237] using data pulled from Amazon S3. request that created this FAQ entry,][pr-237] using data pulled from Amazon S3.
+1 -1
View File
@@ -132,7 +132,7 @@ feature `X` or scenario `Y`? Open an issue and let's discuss.
[flow-org]: https://flow.org/ [flow-org]: https://flow.org/
<!-- Talks --> <!-- Talks -->
[assert-js-talks]: https://www.youtube.com/playlist?list=PLZ66c9_z3umNSrKSb5cmpxdXZcIPNvKGw [assert-js-talks]: https://www.youtube.com/playlist?list=PLZ66c9_z3umNSrKSb5cmpxdXZcIPNvKGw
[kent-talk]: https://www.youtube.com/watch?list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf [kent-talk]: https://www.youtube.com/watch?v=Fha2bVoC8SE
[gleb-talk]: https://www.youtube.com/watch?v=5FnalKRjpZk [gleb-talk]: https://www.youtube.com/watch?v=5FnalKRjpZk
[kent-blog]: https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests [kent-blog]: https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests
<!-- Images --> <!-- Images -->
@@ -31,6 +31,7 @@
</td> </td>
<td>Viewport, Toolbar</td> <td>Viewport, Toolbar</td>
</tr> </tr>
<!-- dicom-html -->
<tr> <tr>
<td> <td>
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-html">DICOM HTML</a> <a href="https://www.npmjs.com/package/@ohif/extension-dicom-html">DICOM HTML</a>
@@ -40,6 +41,7 @@
</td> </td>
<td>Viewport, SopClassHandler</td> <td>Viewport, SopClassHandler</td>
</tr> </tr>
<!-- dicom-pdf -->
<tr> <tr>
<td> <td>
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-pdf">DICOM PDF</a> <a href="https://www.npmjs.com/package/@ohif/extension-dicom-pdf">DICOM PDF</a>
@@ -49,6 +51,7 @@
</td> </td>
<td>Viewport, SopClassHandler</td> <td>Viewport, SopClassHandler</td>
</tr> </tr>
<!-- dicom-microscopy -->
<tr> <tr>
<td> <td>
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-microscopy">DICOM Microscopy</a> <a href="https://www.npmjs.com/package/@ohif/extension-dicom-microscopy">DICOM Microscopy</a>
@@ -58,5 +61,15 @@
</td> </td>
<td>Viewport, SopClassHandler</td> <td>Viewport, SopClassHandler</td>
</tr> </tr>
<!-- dicom-segmentation -->
<tr>
<td>
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-segmentation">DICOM Segmentation</a>
</td>
<td>
Renders segmentation images for a <a href="https://github.com/OHIF/Viewers/blob/master/extensions/dicom-segmentation/src/OHIFDicomSegSopClassHandler.js#L5-L7">specific SopClassUID</a>.
</td>
<td>Panel, Toolbar</td>
</tr>
</tbody> </tbody>
</table> </table>
+11 -9
View File
@@ -74,7 +74,8 @@ export default {
### Registering an Extension ### Registering an Extension
There are two different ways to register and configure extensions: At There are two different ways to register and configure extensions: At
[runtime](#runtime-extensions) and at [build time](#bundled-extensions). [runtime](#registering-at-runtime) and at
[build time](#registering-at-build-time).
You can leverage one or both strategies. Which one(s) you choose depend on your You can leverage one or both strategies. Which one(s) you choose depend on your
application's requirements. Each [module](#modules) defined by the extension application's requirements. Each [module](#modules) defined by the extension
@@ -82,9 +83,9 @@ becomes available to the core application via the `ExtensionManager`.
#### Registering at Runtime #### Registering at Runtime
The `@ohif/viewer` uses a [configuration file](#) at startup. The schema for The `@ohif/viewer` uses a [configuration file](../viewer/configuration.md) at
that file includes an `Extensions` key that supports an array of extensions to startup. The schema for that file includes an `Extensions` key that supports an
register. array of extensions to register.
```js ```js
// prettier-ignore // prettier-ignore
@@ -193,8 +194,8 @@ today, create a GitHub issue!
The `ExtensionManager` is a class made available to us via the `@ohif/core` The `ExtensionManager` is a class made available to us via the `@ohif/core`
project (platform/core). Our application instantiates a single instance of it, project (platform/core). Our application instantiates a single instance of it,
and provides a `ServicesManager` and `CommandsManager` along with the application's and provides a `ServicesManager` and `CommandsManager` along with the
configuration through the appConfig key (optional). application's configuration through the appConfig key (optional).
```js ```js
const commandsManager = new CommandsManager(); const commandsManager = new CommandsManager();
@@ -202,7 +203,7 @@ const servicesManager = new ServicesManager();
const extensionManager = new ExtensionManager({ const extensionManager = new ExtensionManager({
commandsManager, commandsManager,
servicesManager, servicesManager,
appConfig appConfig,
}); });
``` ```
@@ -212,8 +213,9 @@ The `ExtensionManager` only has a few public members:
- `registerExtensions` - Registers an array of extensions - `registerExtensions` - Registers an array of extensions
- `modules` - An object containing registered extensions by `MODULE_TYPE` - `modules` - An object containing registered extensions by `MODULE_TYPE`
During registration, lifecycle hooks and modules have access to the extension's config, During registration, lifecycle hooks and modules have access to the extension's
the application's config and `ExtensionManager`'s `ServicesManager` and `CommandsManager` instances. config, the application's config and `ExtensionManager`'s `ServicesManager` and
`CommandsManager` instances.
Our `@ohif/viewer` uses the `modules` member to access registered extensions at Our `@ohif/viewer` uses the `modules` member to access registered extensions at
appropriate places in our application. appropriate places in our application.
@@ -1,9 +1,9 @@
# Module: SOP Class Handler # Module: SOP Class Handler
An extension can register a SOP Class Handler Module by defining a An extension can register a [SOP Class][sop-class-link] Handler Module by
`getSopClassHandlerModule` method. The SOP Class Handler is a bit different from defining a `getSopClassHandlerModule` method. The [SOP Class][sop-class-link]
the other modules, as it doesn't provide a `1:1` schema for UI or provide it's Handler is a bit different from the other modules, as it doesn't provide a `1:1`
own components. It instead defines: schema for UI or provide it's own components. It instead defines:
- `sopClassUIDs`: an array of string SOP Class UIDs that the - `sopClassUIDs`: an array of string SOP Class UIDs that the
`getDisplaySetFromSeries` method should be applied to. `getDisplaySetFromSeries` method should be applied to.
@@ -76,6 +76,13 @@ getSopClassHandlerModule({ servicesManager, commandsManager }) {
}; };
``` ```
### More examples :
- [Dicom-HTML SOP][dicom-html-sop]
- [Dicom-PDF SOP][dicom-pdf-sop]
- [Dicom-Microscopy SOP][dicom-micro-sop]
- [Dicom-Segmentation SOP][dicom-seg-sop]
## `@ohif/viewer` usage ## `@ohif/viewer` usage
We use the `sopClassHandlerModule`s in three different places: We use the `sopClassHandlerModule`s in three different places:
@@ -88,3 +95,11 @@ Each time, it is used to map study and series data to `displaySets`. It does
this by working alongside the `StudyMetadataManager` in `@ohif/core`. That this by working alongside the `StudyMetadataManager` in `@ohif/core`. That
manager has the method `createDisplaySets` that takes an array of manager has the method `createDisplaySets` that takes an array of
`sopClassHandlerModules`. `sopClassHandlerModules`.
<!-- prettier-ignore-start -->
[sop-class-link]: http://dicom.nema.org/dicom/2013/output/chtml/part04/sect_B.5.html
[dicom-html-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-html/src/OHIFDicomHtmlSopClassHandler.js#L4-L12
[dicom-pdf-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js#L4-L6
[dicom-micro-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js#L5-L7
[dicom-seg-sop]: https://github.com/OHIF/Viewers/blob/master/extensions/dicom-segmentation/src/OHIFDicomSegSopClassHandler.js#L5-L7
<!-- prettier-ignore-end -->
+2 -2
View File
@@ -75,7 +75,7 @@ items to our ["Active Development" GitHub Project Board][gh-board]. Items on
this project board are either in active development by Core Team members, or this project board are either in active development by Core Team members, or
queued up for development as in-progress items are completed. queued up for development as in-progress items are completed.
> :pencil: Want to contribute but not sure where to start? Check out [Up for > 🖋 Want to contribute but not sure where to start? Check out [Up for
> grabs][label-grabs] issues and our [Contributing > grabs][label-grabs] issues and our [Contributing
> documentation][contributing-docs] > documentation][contributing-docs]
@@ -156,5 +156,5 @@ addressed and a Stable version is released.
[ohif-prod]: https://viewer.ohif.org [ohif-prod]: https://viewer.ohif.org
[gh-board]: https://github.com/OHIF/Viewers/projects/4 [gh-board]: https://github.com/OHIF/Viewers/projects/4
[label-grabs]: https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs+%3Araising_hand_woman%3A%22 [label-grabs]: https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs+%3Araising_hand_woman%3A%22
[contributing-docs]: https://docs.ohif.org/contributing/ [contributing-docs]: ./development/contributing.md
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
+1 -1
View File
@@ -62,7 +62,7 @@ able to fail or be removed. Related patterns that may reduce coupling include:
--> -->
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
[commands]: #/ [commands]: ../extensions/modules/commands.md
[core-services]: https://github.com/OHIF/Viewers/tree/master/platform/core/src/services [core-services]: https://github.com/OHIF/Viewers/tree/master/platform/core/src/services
[services-manager]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/ServicesManager.js [services-manager]: https://github.com/OHIF/Viewers/blob/master/platform/core/src/services/ServicesManager.js
[cross-cutting-concerns]: https://en.wikipedia.org/wiki/Cross-cutting_concern [cross-cutting-concerns]: https://en.wikipedia.org/wiki/Cross-cutting_concern
+2 -2
View File
@@ -2,8 +2,8 @@
- [Overview](#overview) - [Overview](#overview)
- [Example](#example) - [Example](#example)
- [Tips & Tricks](#/) - [Tips & Tricks](#tips--tricks)
- [Maintained Services](#/) - [Maintained Services](#maintained-services)
## Overview ## Overview
+4 -4
View File
@@ -135,7 +135,7 @@ console.log(T('$t(Common:Play) my translated text'));
# Main Concepts While Translating # Main Concepts While Translating
## - Namespaces ## Namespaces
Namespaces are being used to organize translations in smaller portions, combined Namespaces are being used to organize translations in smaller portions, combined
semantically or by use. Each `.json` file inside `@ohif/i18n` npm package semantically or by use. Each `.json` file inside `@ohif/i18n` npm package
@@ -157,7 +157,7 @@ NameSpace, like this following example getting data from `Common` NameSpace:
$t(Common:Reset) $t(Common:Reset)
``` ```
## - Extending Languages in @ohif/i18n ## Extending Languages in @ohif/i18n
Sometimes, even using the same language, some nouns or jargons can change Sometimes, even using the same language, some nouns or jargons can change
according to the country, states or even from Hospital to Hospital. according to the country, states or even from Hospital to Hospital.
@@ -212,7 +212,7 @@ object like this:
Please check the `index.js` files inside locales folder for an example of this Please check the `index.js` files inside locales folder for an example of this
exporting structure. exporting structure.
### - Extending languages dynamically ### Extending languages dynamically
You have access to the i18next instance, so you can use the You have access to the i18next instance, so you can use the
[addResourceBundle](https://www.i18next.com/how-to/add-or-load-translations#add-after-init) [addResourceBundle](https://www.i18next.com/how-to/add-or-load-translations#add-after-init)
@@ -314,4 +314,4 @@ REACT_APP_I18N_DEBUG=true yarn run dev
### Contributing with new languages ### Contributing with new languages
Contributions of any kind are welcome! Please check the Contributions of any kind are welcome! Please check the
[instructions](https://docs.ohif.org/contributing.html). [instructions](../development/contributing.md).
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.5.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.5.0...@ohif/extension-cornerstone@2.5.1) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [2.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.4.3...@ohif/extension-cornerstone@2.5.0) (2020-03-13) # [2.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.4.3...@ohif/extension-cornerstone@2.5.0) (2020-03-13)
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-cornerstone", "name": "@ohif/extension-cornerstone",
"version": "2.5.0", "version": "2.5.1",
"description": "OHIF extension for Cornerstone", "description": "OHIF extension for Cornerstone",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -1,37 +0,0 @@
/**
* We use this component to leverage "Code Splitting"
*
* Link: https://serverless-stack.com/chapters/code-splitting-in-create-react-app.html
*/
import React, { Component } from 'react';
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = {
component: null,
};
}
async componentDidMount() {
// Add dynamically loaded component to state
const { default: component } = await importComponent();
this.setState({
component: component,
});
}
render() {
const C = this.state.component;
// Render the loaded component, or null
return C ? <C {...this.props} /> : null;
}
}
return AsyncComponent;
}
+12 -6
View File
@@ -1,14 +1,20 @@
import React from 'react';
import init from './init.js'; import init from './init.js';
import asyncComponent from './asyncComponent.js';
import commandsModule from './commandsModule.js'; import commandsModule from './commandsModule.js';
import toolbarModule from './toolbarModule.js'; import toolbarModule from './toolbarModule.js';
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm'; import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
const OHIFCornerstoneViewport = asyncComponent(() => const Component = React.lazy(() => {
import( return import('./OHIFCornerstoneViewport');
/* webpackChunkName: "OHIFCornerstoneViewport" */ './OHIFCornerstoneViewport.js' });
)
); const OHIFCornerstoneViewport = props => {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Component {...props} />
</React.Suspense>
);
};
/** /**
* *
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.1.1...@ohif/extension-dicom-html@1.1.2) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.1.0...@ohif/extension-dicom-html@1.1.1) (2020-03-09) ## [1.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.1.0...@ohif/extension-dicom-html@1.1.1) (2020-03-09)
**Note:** Version bump only for package @ohif/extension-dicom-html **Note:** Version bump only for package @ohif/extension-dicom-html
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-html", "name": "@ohif/extension-dicom-html",
"version": "1.1.1", "version": "1.1.2",
"description": "OHIF extension for rendering structured reports to HTML", "description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -1,37 +0,0 @@
/**
* We use this component to leverage "Code Splitting"
*
* Link: https://serverless-stack.com/chapters/code-splitting-in-create-react-app.html
*/
import React, { Component } from 'react';
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = {
component: null,
};
}
async componentDidMount() {
// Add dynamically loaded component to state
const { default: component } = await importComponent();
this.setState({
component: component,
});
}
render() {
const C = this.state.component;
// Render the loaded component, or null
return C ? <C {...this.props} /> : null;
}
}
return AsyncComponent;
}
+12 -6
View File
@@ -1,11 +1,17 @@
import asyncComponent from './asyncComponent.js'; import React from 'react';
import OHIFDicomHtmlSopClassHandler from './OHIFDicomHtmlSopClassHandler.js'; import OHIFDicomHtmlSopClassHandler from './OHIFDicomHtmlSopClassHandler.js';
const OHIFDicomHtmlViewport = asyncComponent(() => const Component = React.lazy(() => {
import( return import('./OHIFDicomHtmlViewport');
/* webpackChunkName: "OHIFDicomHtmlViewport" */ './OHIFDicomHtmlViewport.js' });
)
); const OHIFDicomHtmlViewport = props => {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Component {...props} />
</React.Suspense>
);
};
export default { export default {
/** /**
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.50.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.8...@ohif/extension-dicom-microscopy@0.50.9) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
## [0.50.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.7...@ohif/extension-dicom-microscopy@0.50.8) (2020-03-31)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
## [0.50.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.6...@ohif/extension-dicom-microscopy@0.50.7) (2020-03-09) ## [0.50.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.6...@ohif/extension-dicom-microscopy@0.50.7) (2020-03-09)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy **Note:** Version bump only for package @ohif/extension-dicom-microscopy
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-microscopy", "name": "@ohif/extension-dicom-microscopy",
"version": "0.50.7", "version": "0.50.9",
"description": "OHIF extension for Dicom Microscopy", "description": "OHIF extension for Dicom Microscopy",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -34,11 +34,12 @@ class DicomMicroscopyViewport extends Component {
.then(instances => { .then(instances => {
const promises = []; const promises = [];
for (let i = 0; i < instances.length; i++) { for (let i = 0; i < instances.length; i++) {
const sopInstanceUid = instances[i]['00080018']['Value'][0]; const sopInstanceUID = instances[i]['00080018']['Value'][0];
const retrieveInstanceOptions = { const retrieveInstanceOptions = {
studyInstanceUID: displaySet.StudyInstanceUID, studyInstanceUID: displaySet.StudyInstanceUID,
seriesInstanceUID: displaySet.SeriesInstanceUID, seriesInstanceUID: displaySet.SeriesInstanceUID,
sopInstanceUid, sopInstanceUID,
}; };
const promise = dicomWebClient const promise = dicomWebClient
@@ -1,37 +0,0 @@
/**
* We use this component to leverage "Code Splitting"
*
* Link: https://serverless-stack.com/chapters/code-splitting-in-create-react-app.html
*/
import React, { Component } from 'react';
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = {
component: null,
};
}
async componentDidMount() {
// Add dynamically loaded component to state
const { default: component } = await importComponent();
this.setState({
component: component,
});
}
render() {
const C = this.state.component;
// Render the loaded component, or null
return C ? <C {...this.props} /> : null;
}
}
return AsyncComponent;
}
+12 -6
View File
@@ -1,11 +1,17 @@
import asyncComponent from './asyncComponent.js'; import React from 'react';
import DicomMicroscopySopClassHandler from './DicomMicroscopySopClassHandler.js'; import DicomMicroscopySopClassHandler from './DicomMicroscopySopClassHandler.js';
const DicomMicroscopyViewport = asyncComponent(() => const Component = React.lazy(() => {
import( return import('./DicomMicroscopyViewport');
/* webpackChunkName: "DicomMicroscopyViewport" */ './DicomMicroscopyViewport.js' });
)
); const DicomMicroscopyViewport = props => {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Component {...props} />
</React.Suspense>
);
};
export default { export default {
/** /**
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.2...@ohif/extension-dicom-pdf@1.0.3) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-dicom-pdf
## [1.0.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.1...@ohif/extension-dicom-pdf@1.0.2) (2020-03-09) ## [1.0.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.1...@ohif/extension-dicom-pdf@1.0.2) (2020-03-09)
**Note:** Version bump only for package @ohif/extension-dicom-pdf **Note:** Version bump only for package @ohif/extension-dicom-pdf
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-pdf", "name": "@ohif/extension-dicom-pdf",
"version": "1.0.2", "version": "1.0.3",
"description": "OHIF extension for Dicom PDF", "description": "OHIF extension for Dicom PDF",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -1,37 +0,0 @@
/**
* We use this component to leverage "Code Splitting"
*
* Link: https://serverless-stack.com/chapters/code-splitting-in-create-react-app.html
*/
import React, { Component } from 'react';
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = {
component: null,
};
}
async componentDidMount() {
// Add dynamically loaded component to state
const { default: component } = await importComponent();
this.setState({
component: component,
});
}
render() {
const C = this.state.component;
// Render the loaded component, or null
return C ? <C {...this.props} /> : null;
}
}
return AsyncComponent;
}
+12 -6
View File
@@ -1,11 +1,17 @@
import asyncComponent from './asyncComponent.js'; import React from 'react';
import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js'; import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js';
const ConnectedOHIFDicomPDFViewer = asyncComponent(() => const Component = React.lazy(() => {
import( return import('./ConnectedOHIFDicomPDFViewer');
/* webpackChunkName: "ConnectedOHIFDicomPDFViewer" */ './ConnectedOHIFDicomPDFViewer' });
)
); const ConnectedOHIFDicomPDFViewer = props => {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Component {...props} />
</React.Suspense>
);
};
export default { export default {
/** /**
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.1.0...@ohif/extension-dicom-segmentation@0.1.1) (2020-04-01)
### Bug Fixes
* segmentation not loading ([#1566](https://github.com/OHIF/Viewers/issues/1566)) ([4a7ce1c](https://github.com/OHIF/Viewers/commit/4a7ce1c09324d74c61048393e3a2427757e4001a))
# 0.1.0 (2020-03-13) # 0.1.0 (2020-03-13)
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-segmentation", "name": "@ohif/extension-dicom-segmentation",
"version": "0.1.0", "version": "0.1.1",
"description": "OHIF extension for viewing segmentations in the 2D MPR view", "description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -14,7 +14,7 @@ const OHIFDicomSegSopClassHandler = {
id: 'OHIFDicomSegSopClassHandler', id: 'OHIFDicomSegSopClassHandler',
type: MODULE_TYPES.SOP_CLASS_HANDLER, type: MODULE_TYPES.SOP_CLASS_HANDLER,
sopClassUIDs, sopClassUIDs,
getDisplaySetFromSeries: function( getDisplaySetFromSeries: function (
series, series,
study, study,
dicomWebClient, dicomWebClient,
@@ -53,7 +53,7 @@ const OHIFDicomSegSopClassHandler = {
SeriesDescription, SeriesDescription,
}; };
segDisplaySet.load = function(referencedDisplaySet, studies) { segDisplaySet.load = function (referencedDisplaySet, studies) {
return loadSegmentation( return loadSegmentation(
segDisplaySet, segDisplaySet,
referencedDisplaySet, referencedDisplaySet,
@@ -39,50 +39,26 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
* TODO: wrap get/set interactions with the cornerstoneTools * TODO: wrap get/set interactions with the cornerstoneTools
* store with context to make these kind of things less blurry. * store with context to make these kind of things less blurry.
*/ */
const segmentationModule = cornerstoneTools.getModule('segmentation'); const { configuration } = cornerstoneTools.getModule('segmentation');
const { configuration } = segmentationModule;
const DEFAULT_BRUSH_RADIUS = configuration.radius || 10; const DEFAULT_BRUSH_RADIUS = configuration.radius || 10;
const [state, setState] = useState({
const [brushRadius, setBrushRadius] = useState(DEFAULT_BRUSH_RADIUS); brushRadius: DEFAULT_BRUSH_RADIUS,
brushColor: 'rgba(221, 85, 85, 1)', /* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */
/* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */ selectedSegment: null,
const [brushColor, setBrushColor] = useState('rgba(221, 85, 85, 1)'); selectedSegmentation: null,
const [selectedSegment, setSelectedSegment] = useState(); showSegSettings: false,
const [showSegSettings, setShowSegSettings] = useState(false); brushStackState: null,
const [selectedSegmentation, setSelectedSegmentation] = useState(); labelmapList: [],
segmentList: []
const viewport = viewports[activeIndex]; });
const {
StudyInstanceUID,
SeriesInstanceUID,
displaySetInstanceUID,
} = viewport;
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
const firstImageId = studyMetadata.getFirstImageId(displaySetInstanceUID);
/* CornerstoneTools */
const [brushStackState, setBrushStackState] = useState(
segmentationModule.state.series[firstImageId]
);
useEffect(() => { useEffect(() => {
setShowSegSettings(showSegSettings && !isOpen);
}, [isOpen]);
useEffect(() => {
setBrushStackState(segmentationModule.state.series[firstImageId]);
}, [studies, viewports, activeIndex, firstImageId]);
useEffect(() => {
if (brushStackState) {
setSelectedSegmentation(brushStackState.activeLabelmapIndex);
}
const labelmapModifiedHandler = event => { const labelmapModifiedHandler = event => {
log.warn('labelmap modified', event); log.warn('Segmentation Panel: labelmap modified', event);
setBrushStackState(segmentationModule.state.series[firstImageId]); const module = cornerstoneTools.getModule('segmentation');
const activeViewport = viewports[activeIndex];
const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID);
updateState('brushStackState', module.state.series[firstImageId]);
}; };
/* /*
@@ -107,27 +83,43 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
}; };
}); });
if (!brushStackState) { useEffect(() => {
return null; const module = cornerstoneTools.getModule('segmentation');
} const activeViewport = viewports[activeIndex];
const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID);
const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID);
const brushStackState = module.state.series[firstImageId];
const labelmap3D = if (brushStackState) {
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex]; const labelmap3D = brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
const labelmapList = getLabelmapList(brushStackState, firstImageId, activeViewport);
const segmentList = getSegmentList(labelmap3D, firstImageId);
setState(state => ({
...state,
brushStackState,
selectedSegmentation: brushStackState.activeLabelmapIndex,
labelmapList,
segmentList
}));
} else {
setState(state => ({
...state,
labelmapList: [],
segmentList: [],
}));
}
}, [studies, viewports, activeIndex]);
/* /* Handle open/closed panel behaviour */
* 2. UseEffect to update state? or to a least trigger a re-render useEffect(() => {
* 4. Toggle visibility of labelmap? updateState('showSegSettings', state.showSegSettings && !isOpen);
* 5. Toggle visibility of seg? }, [isOpen]);
*
* If the port is cornerstone, just need to call a re-render.
* If the port is vtkjs, its a bit more tricky as we now need to create a new
*/
const getLabelmapList = () => { const getLabelmapList = (brushStackState, firstImageId, activeViewport) => {
/* Get list of SEG labelmaps specific to active viewport (reference series) */ /* Get list of SEG labelmaps specific to active viewport (reference series) */
const referencedSegDisplaysets = _getReferencedSegDisplaysets( const referencedSegDisplaysets = _getReferencedSegDisplaysets(
StudyInstanceUID, activeViewport.StudyInstanceUID,
SeriesInstanceUID activeViewport.SeriesInstanceUID
); );
return referencedSegDisplaysets.map((displaySet, index) => { return referencedSegDisplaysets.map((displaySet, index) => {
@@ -148,23 +140,19 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
description: displayDate, description: displayDate,
onClick: async () => { onClick: async () => {
const activatedLabelmapIndex = await _setActiveLabelmap( const activatedLabelmapIndex = await _setActiveLabelmap(
viewport, activeViewport,
studies, studies,
displaySet, displaySet,
firstImageId, firstImageId,
brushStackState.activeLabelmapIndex brushStackState.activeLabelmapIndex
); );
setSelectedSegmentation(activatedLabelmapIndex); updateState('selectedSegmentation', activatedLabelmapIndex);
}, },
}; };
}); });
}; };
const labelmapList = getLabelmapList(); const getSegmentList = (labelmap3D, firstImageId) => {
const segmentList = [];
if (labelmap3D) {
/* /*
* Newly created segments have no `meta` * Newly created segments have no `meta`
* So we instead build a list of all segment indexes in use * So we instead build a list of all segment indexes in use
@@ -186,10 +174,12 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
}, []) }, [])
.sort((a, b) => a - b); .sort((a, b) => a - b);
const module = cornerstoneTools.getModule('segmentation');
const colorLutTable = const colorLutTable =
segmentationModule.state.colorLutTables[labelmap3D.colorLUTIndex]; module.state.colorLutTables[labelmap3D.colorLUTIndex];
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data; const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
const segmentList = [];
for (let i = 0; i < uniqueSegmentIndexes.length; i++) { for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
const segmentIndex = uniqueSegmentIndexes[i]; const segmentIndex = uniqueSegmentIndexes[i];
@@ -207,14 +197,14 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
} }
} }
const sameSegment = selectedSegment === segmentNumber; const sameSegment = state.selectedSegment === segmentNumber;
const setCurrentSelectedSegment = () => { const setCurrentSelectedSegment = () => {
_setActiveSegment( _setActiveSegment(
firstImageId, firstImageId,
segmentNumber, segmentNumber,
labelmap3D.activeSegmentIndex labelmap3D.activeSegmentIndex
); );
setSelectedSegment(sameSegment ? null : segmentNumber); updateState('selectedSegment', sameSegment ? null : segmentNumber);
}; };
segmentList.push( segmentList.push(
@@ -229,20 +219,27 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
); );
} }
return segmentList;
/* /*
* Let's iterate over segmentIndexes ^ above * Let's iterate over segmentIndexes ^ above
* If meta has a match, use it to show info * If meta has a match, use it to show info
* If now, add "no-meta" class * If now, add "no-meta" class
* Show default name * Show default name
*/ */
} };
const updateState = (field, value) => {
setState(state => ({ ...state, [field]: value }));
};
const updateBrushSize = evt => { const updateBrushSize = evt => {
const updatedRadius = Number(evt.target.value); const updatedRadius = Number(evt.target.value);
if (updatedRadius !== brushRadius) { if (updatedRadius !== brushRadius) {
setBrushRadius(updatedRadius); updateState('brushRadius', updatedRadius);
segmentationModule.setters.radius(updatedRadius); const module = cornerstoneTools.getModule('segmentation');
module.setters.radius(updatedRadius);
} }
}; };
@@ -251,29 +248,30 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
if (labelmap3D.activeSegmentIndex > 1) { if (labelmap3D.activeSegmentIndex > 1) {
labelmap3D.activeSegmentIndex--; labelmap3D.activeSegmentIndex--;
} }
setSelectedSegment(labelmap3D.activeSegmentIndex); updateState('selectedSegment', labelmap3D.activeSegmentIndex);
updateActiveSegmentColor(); updateActiveSegmentColor();
}; };
const incrementSegment = event => { const incrementSegment = event => {
event.preventDefault(); event.preventDefault();
labelmap3D.activeSegmentIndex++; labelmap3D.activeSegmentIndex++;
setSelectedSegment(labelmap3D.activeSegmentIndex); updateState('selectedSegment', labelmap3D.activeSegmentIndex);
updateActiveSegmentColor(); updateActiveSegmentColor();
}; };
const updateActiveSegmentColor = () => { const updateActiveSegmentColor = () => {
const color = getActiveSegmentColor(); const color = getActiveSegmentColor();
setBrushColor(color); updateState('brushColor', color);
}; };
const getActiveSegmentColor = () => { const getActiveSegmentColor = () => {
if (!brushStackState) { if (!state.brushStackState) {
return 'rgba(255, 255, 255, 1)'; return 'rgba(255, 255, 255, 1)';
} }
const module = cornerstoneTools.getModule('segmentation');
const colorLutTable = const colorLutTable =
segmentationModule.state.colorLutTables[labelmap3D.colorLUTIndex]; module.state.colorLutTables[labelmap3D.colorLUTIndex];
const color = colorLutTable[labelmap3D.activeSegmentIndex]; const color = colorLutTable[labelmap3D.activeSegmentIndex];
return `rgba(${color.join(',')})`; return `rgba(${color.join(',')})`;
@@ -293,11 +291,11 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
refreshViewport(); refreshViewport();
}; };
if (showSegSettings) { if (state.showSegSettings) {
return ( return (
<SegmentationSettings <SegmentationSettings
configuration={configuration} configuration={configuration}
onBack={() => setShowSegSettings(false)} onBack={() => updateState('showSegSettings', false)}
onChange={updateConfiguration} onChange={updateConfiguration}
/> />
); );
@@ -309,13 +307,13 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
name="cog" name="cog"
width="25px" width="25px"
height="25px" height="25px"
onClick={() => setShowSegSettings(true)} onClick={() => updateState('showSegSettings', true)}
/> />
{false && ( {false && (
<form className="selector-form"> <form className="selector-form">
<BrushColorSelector <BrushColorSelector
defaultColor={brushColor} defaultColor={brushColor}
index={labelmap3D.activeSegmentIndex} index={state.selectedSegment}
onNext={incrementSegment} onNext={incrementSegment}
onPrev={decrementSegment} onPrev={decrementSegment}
/> />
@@ -331,17 +329,17 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
<div className="segmentations"> <div className="segmentations">
<SegmentationSelect <SegmentationSelect
value={ value={
labelmapList.find(i => i.value === selectedSegmentation) || null state.labelmapList.find(i => i.value === state.selectedSegmentation) || null
} }
formatOptionLabel={SegmentationItem} formatOptionLabel={SegmentationItem}
options={labelmapList} options={state.labelmapList}
/> />
</div> </div>
<ScrollableArea> <ScrollableArea>
<TableList <TableList
customHeader={<SegmentsHeader count={segmentList.length} />} customHeader={<SegmentsHeader count={state.segmentList.length} />}
> >
{segmentList} {state.segmentList}
</TableList> </TableList>
</ScrollableArea> </ScrollableArea>
</div> </div>
@@ -420,7 +418,7 @@ const _setActiveLabelmap = async (
) => { ) => {
if (displaySet.labelmapIndex === activeLabelmapIndex) { if (displaySet.labelmapIndex === activeLabelmapIndex) {
log.warn(`${activeLabelmapIndex} is already the active labelmap`); log.warn(`${activeLabelmapIndex} is already the active labelmap`);
return; return displaySet.labelmapIndex;
} }
if (!displaySet.isLoaded) { if (!displaySet.isLoaded) {
@@ -431,7 +429,6 @@ const _setActiveLabelmap = async (
const { state } = cornerstoneTools.getModule('segmentation'); const { state } = cornerstoneTools.getModule('segmentation');
const brushStackState = state.series[firstImageId]; const brushStackState = state.series[firstImageId];
brushStackState.activeLabelmapIndex = displaySet.labelmapIndex; brushStackState.activeLabelmapIndex = displaySet.labelmapIndex;
refreshViewport(); refreshViewport();
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.6...@ohif/extension-vtk@1.3.7) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.3.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.5...@ohif/extension-vtk@1.3.6) (2020-04-01)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.3.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.4...@ohif/extension-vtk@1.3.5) (2020-03-25) ## [1.3.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.4...@ohif/extension-vtk@1.3.5) (2020-03-25)
**Note:** Version bump only for package @ohif/extension-vtk **Note:** Version bump only for package @ohif/extension-vtk
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-vtk", "name": "@ohif/extension-vtk",
"version": "1.3.5", "version": "1.3.7",
"description": "OHIF extension for VTK.js", "description": "OHIF extension for VTK.js",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -53,7 +53,7 @@
"react-vtkjs-viewport": "^0.8.3" "react-vtkjs-viewport": "^0.8.3"
}, },
"devDependencies": { "devDependencies": {
"@ohif/core": "^2.6.4", "@ohif/core": "^2.6.6",
"@ohif/ui": "^1.4.1", "@ohif/ui": "^1.4.1",
"cornerstone-tools": "4.12.5", "cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0", "cornerstone-wado-image-loader": "^3.0.0",
+22 -14
View File
@@ -1,5 +1,5 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { getImageData, loadImageData } from 'react-vtkjs-viewport'; import { getReactVtkjsViewport } from './utils/getReactVtkjsViewport';
import ConnectedVTKViewport from './ConnectedVTKViewport'; import ConnectedVTKViewport from './ConnectedVTKViewport';
import LoadingIndicator from './LoadingIndicator.js'; import LoadingIndicator from './LoadingIndicator.js';
import OHIF from '@ohif/core'; import OHIF from '@ohif/core';
@@ -69,7 +69,7 @@ class OHIFVTKViewport extends Component {
}; };
static defaultProps = { static defaultProps = {
onScroll: () => {}, onScroll: () => { },
}; };
static id = 'OHIFVTKViewport'; static id = 'OHIFVTKViewport';
@@ -133,7 +133,8 @@ class OHIFVTKViewport extends Component {
displaySetInstanceUID, displaySetInstanceUID,
SOPClassUID, SOPClassUID,
SOPInstanceUID, SOPInstanceUID,
frameIndex frameIndex,
reactVtkjsViewport
) => { ) => {
const stack = OHIFVTKViewport.getCornerstoneStack( const stack = OHIFVTKViewport.getCornerstoneStack(
studies, studies,
@@ -144,7 +145,10 @@ class OHIFVTKViewport extends Component {
frameIndex frameIndex
); );
const imageDataObject = getImageData(stack.imageIds, displaySetInstanceUID); const imageDataObject = reactVtkjsViewport.getImageData(
stack.imageIds,
displaySetInstanceUID
);
let labelmapDataObject; let labelmapDataObject;
let labelmapColorLUT; let labelmapColorLUT;
@@ -257,7 +261,9 @@ class OHIFVTKViewport extends Component {
return volumeActor; return volumeActor;
} }
setStateFromProps() { async setStateFromProps() {
const reactVtkjsViewport = await getReactVtkjsViewport();
const { studies, displaySet } = this.props.viewportData; const { studies, displaySet } = this.props.viewportData;
const { const {
StudyInstanceUID, StudyInstanceUID,
@@ -295,8 +301,10 @@ class OHIFVTKViewport extends Component {
studies, studies,
StudyInstanceUID, StudyInstanceUID,
displaySetInstanceUID, displaySetInstanceUID,
sopClassUIDs[0],
SOPInstanceUID, SOPInstanceUID,
frameIndex frameIndex,
reactVtkjsViewport
); );
this.imageDataObject = imageDataObject; this.imageDataObject = imageDataObject;
@@ -317,7 +325,7 @@ class OHIFVTKViewport extends Component {
dataDetails, dataDetails,
}, },
() => { () => {
this.loadProgressively(imageDataObject); this.loadProgressively(imageDataObject, reactVtkjsViewport);
// TODO: There must be a better way to do this. // TODO: There must be a better way to do this.
// We do this so that if all the data is available the react-vtkjs-viewport // We do this so that if all the data is available the react-vtkjs-viewport
@@ -335,26 +343,26 @@ class OHIFVTKViewport extends Component {
); );
} }
componentDidMount() { async componentDidMount() {
this.setStateFromProps(); await this.setStateFromProps();
} }
componentDidUpdate(prevProps) { async componentDidUpdate(prevProps) {
const { displaySet } = this.props.viewportData; const { displaySet } = this.props.viewportData;
const prevDisplaySet = prevProps.viewportData.displaySet; const prevDisplaySet = prevProps.viewportData.displaySet;
if ( if (
displaySet.displaySetInstanceUID !== displaySet.displaySetInstanceUID !==
prevDisplaySet.displaySetInstanceUID || prevDisplaySet.displaySetInstanceUID ||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID || displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
displaySet.frameIndex !== prevDisplaySet.frameIndex displaySet.frameIndex !== prevDisplaySet.frameIndex
) { ) {
this.setStateFromProps(); await this.setStateFromProps();
} }
} }
loadProgressively(imageDataObject) { loadProgressively(imageDataObject, reactVtkjsViewport) {
loadImageData(imageDataObject); reactVtkjsViewport.loadImageData(imageDataObject);
const { isLoading, insertPixelDataPromises } = imageDataObject; const { isLoading, insertPixelDataPromises } = imageDataObject;
+9 -2
View File
@@ -1,9 +1,16 @@
import React, { useEffect, useCallback } from 'react'; import React, { useEffect, useCallback } from 'react';
import { View2D } from 'react-vtkjs-viewport'; // import { View2D } from 'react-vtkjs-viewport';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import asyncComponent from './asyncComponent.js';
import { getReactVtkjsViewport } from './utils/getReactVtkjsViewport';
import './VTKViewport.css'; import './VTKViewport.css';
const View2D = asyncComponent(async () => {
const reactVtkjsViewport = await getReactVtkjsViewport();
return { default: reactVtkjsViewport.View2D };
});
const VTKViewport = props => { const VTKViewport = props => {
const style = { width: '100%', height: '100%', position: 'relative' }; const style = { width: '100%', height: '100%', position: 'relative' };
@@ -52,7 +59,7 @@ VTKViewport.propTypes = {
}; };
VTKViewport.defaultProps = { VTKViewport.defaultProps = {
onScroll: () => {}, onScroll: () => { },
}; };
export default VTKViewport; export default VTKViewport;
+13 -14
View File
@@ -1,10 +1,5 @@
import throttle from 'lodash.throttle'; import throttle from 'lodash.throttle';
import { import { getReactVtkjsViewport } from './utils/getReactVtkjsViewport';
vtkInteractorStyleMPRCrosshairs,
vtkInteractorStyleMPRWindowLevel,
vtkInteractorStyleMPRRotate,
vtkSVGCrosshairsWidget,
} from 'react-vtkjs-viewport';
import setMPRLayout from './utils/setMPRLayout.js'; import setMPRLayout from './utils/setMPRLayout.js';
import setViewportToVTK from './utils/setViewportToVTK.js'; import setViewportToVTK from './utils/setViewportToVTK.js';
@@ -124,16 +119,18 @@ const commandsModule = ({ commandsManager }) => {
_setView(api, [0, 1, 0], [0, 0, 1]); _setView(api, [0, 1, 0], [0, 0, 1]);
}, },
enableRotateTool: () => { enableRotateTool: async () => {
const reactVtkjsViewport = await getReactVtkjsViewport();
apis.forEach(api => { apis.forEach(api => {
const istyle = vtkInteractorStyleMPRRotate.newInstance(); const istyle = reactVtkjsViewport.vtkInteractorStyleMPRRotate.newInstance();
api.setInteractorStyle({ istyle }); api.setInteractorStyle({ istyle });
}); });
}, },
enableCrosshairsTool: () => { enableCrosshairsTool: async () => {
const reactVtkjsViewport = await getReactVtkjsViewport();
apis.forEach((api, apiIndex) => { apis.forEach((api, apiIndex) => {
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance(); const istyle = reactVtkjsViewport.vtkInteractorStyleMPRCrosshairs.newInstance();
api.setInteractorStyle({ api.setInteractorStyle({
istyle, istyle,
@@ -141,7 +138,7 @@ const commandsModule = ({ commandsManager }) => {
}); });
}); });
}, },
enableLevelTool: () => { enableLevelTool: async () => {
function updateVOI(apis, windowWidth, windowCenter) { function updateVOI(apis, windowWidth, windowCenter) {
apis.forEach(api => { apis.forEach(api => {
api.updateVOI(windowWidth, windowCenter); api.updateVOI(windowWidth, windowCenter);
@@ -162,8 +159,9 @@ const commandsModule = ({ commandsManager }) => {
}, },
}; };
const reactVtkjsViewport = await getReactVtkjsViewport();
apis.forEach(api => { apis.forEach(api => {
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance(); const istyle = reactVtkjsViewport.vtkInteractorStyleMPRWindowLevel.newInstance();
api.setInteractorStyle({ istyle, callbacks }); api.setInteractorStyle({ istyle, callbacks });
}); });
@@ -260,14 +258,15 @@ const commandsModule = ({ commandsManager }) => {
} }
// Add widgets and set default interactorStyle of each viewport. // Add widgets and set default interactorStyle of each viewport.
const reactVtkjsViewport = await getReactVtkjsViewport();
apis.forEach((api, apiIndex) => { apis.forEach((api, apiIndex) => {
api.addSVGWidget( api.addSVGWidget(
vtkSVGCrosshairsWidget.newInstance(), reactVtkjsViewport.vtkSVGCrosshairsWidget.newInstance(),
'crosshairsWidget' 'crosshairsWidget'
); );
const uid = api.uid; const uid = api.uid;
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance(); const istyle = reactVtkjsViewport.vtkInteractorStyleMPRCrosshairs.newInstance();
api.setInteractorStyle({ api.setInteractorStyle({
istyle, istyle,
@@ -0,0 +1,5 @@
export function getReactVtkjsViewport() {
return import(
/* webpackChunkName: "ReactVtkjsViewport" */ 'react-vtkjs-viewport'
);
}
+19
View File
@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.6.6](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.5...@ohif/core@2.6.6) (2020-04-02)
**Note:** Version bump only for package @ohif/core
## [2.6.5](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.4...@ohif/core@2.6.5) (2020-04-01)
### Bug Fixes
* segmentation not loading ([#1566](https://github.com/OHIF/Viewers/issues/1566)) ([4a7ce1c](https://github.com/OHIF/Viewers/commit/4a7ce1c09324d74c61048393e3a2427757e4001a))
## [2.6.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.3...@ohif/core@2.6.4) (2020-03-25) ## [2.6.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.3...@ohif/core@2.6.4) (2020-03-25)
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/core", "name": "@ohif/core",
"version": "2.6.4", "version": "2.6.6",
"description": "Generic business logic for web-based medical imaging applications", "description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team", "author": "OHIF Core Team",
"license": "MIT", "license": "MIT",
@@ -1,10 +1,10 @@
import cornerstone from 'cornerstone-core'; import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import { import {
clearStudyLoadingProgress, clearStudyLoadingProgress,
setStudyLoadingProgress, setStudyLoadingProgress,
} from '../redux/actions'; } from '../redux/actions';
import StackManager from '../utils/StackManager'; import StackManager from '../utils/StackManager';
import { getCornerstoneWADOImageLoader } from '../utils/cornerstoneWADOImageLoader';
class BaseLoadingListener { class BaseLoadingListener {
constructor(stack, options = {}) { constructor(stack, options = {}) {
@@ -103,7 +103,8 @@ class DICOMFileLoadingListener extends BaseLoadingListener {
this._checkCachedData(); this._checkCachedData();
} }
_checkCachedData() { async _checkCachedData() {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get( const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(
this._dataSetUrl this._dataSetUrl
); );
@@ -78,7 +78,7 @@ export class StudyMetadata extends Metadata {
Object.defineProperty(this, 'studyInstanceUID', { Object.defineProperty(this, 'studyInstanceUID', {
configurable: false, configurable: false,
enumerable: false, enumerable: false,
get: function() { get: function () {
return this.getStudyInstanceUID(); return this.getStudyInstanceUID();
}, },
}); });
@@ -208,20 +208,21 @@ export class StudyMetadata extends Metadata {
/** /**
* Returns a list of derived datasets in the study, filtered by the given filter. * Returns a list of derived datasets in the study, filtered by the given filter.
* @param {object} filter An object containing search filters * @param {object} filter An object containing search filters
* @param {object} filter.modality * @param {object} filter.Modality
* @param {object} filter.referencedSeriesInstanceUID * @param {object} filter.referencedSeriesInstanceUID
* @param {object} filter.referencedFrameOfReferenceUID * @param {object} filter.referencedFrameOfReferenceUID
* @return {Array} filtered derived display sets * @return {Array} filtered derived display sets
*/ */
getDerivedDatasets(filter) { getDerivedDatasets(filter) {
const { const {
modality, Modality,
referencedSeriesInstanceUID, referencedSeriesInstanceUID,
referencedFrameOfReferenceUID, referencedFrameOfReferenceUID,
} = filter; } = filter;
let filteredDerivedDisplaySets = this._derivedDisplaySets; let filteredDerivedDisplaySets = this._derivedDisplaySets;
if (modality) { if (Modality) {
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter( filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
displaySet => displaySet.Modality === Modality displaySet => displaySet.Modality === Modality
); );
@@ -1,8 +1,8 @@
import DICOMWeb from '../../../DICOMWeb'; import DICOMWeb from '../../../DICOMWeb';
import metadataProvider from '../../../classes/MetadataProvider'; import metadataProvider from '../../../classes/MetadataProvider';
import getWADORSImageId from '../../../utils/getWADORSImageId'; import getWADORSImageId from '../../../utils/getWADORSImageId';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import getReferencedSeriesSequence from './getReferencedSeriesSequence'; import getReferencedSeriesSequence from './getReferencedSeriesSequence';
import { getCornerstoneWADOImageLoader } from '../../../utils/cornerstoneWADOImageLoader';
/** /**
* Create a plain JS object that describes a study (a study descriptor object) * Create a plain JS object that describes a study (a study descriptor object)
@@ -160,6 +160,7 @@ async function makeSOPInstance(server, study, instance) {
const wadoRSMetadata = Object.assign(instance); const wadoRSMetadata = Object.assign(instance);
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
if (sopInstance.NumberOfFrames) { if (sopInstance.NumberOfFrames) {
for (let i = 0; i < sopInstance.NumberOfFrames; i++) { for (let i = 0; i < sopInstance.NumberOfFrames; i++) {
const wadorsImageId = getWADORSImageId(sopInstance, i); const wadorsImageId = getWADORSImageId(sopInstance, i);
@@ -0,0 +1,5 @@
export function getCornerstoneWADOImageLoader() {
return import(
/* webpackChunkName: "CornerstoneWADOImageLoader" */ 'cornerstone-wado-image-loader'
);
}
@@ -1,7 +1,7 @@
import cornerstone from 'cornerstone-core'; import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import { api } from 'dicomweb-client'; import { api } from 'dicomweb-client';
import DICOMWeb from '../DICOMWeb'; import DICOMWeb from '../DICOMWeb';
import { getCornerstoneWADOImageLoader } from './cornerstoneWADOImageLoader';
const getImageId = imageObj => { const getImageId = imageObj => {
if (!imageObj) { if (!imageObj) {
@@ -86,7 +86,7 @@ const getImageLoaderType = imageId => {
}; };
class DicomLoaderService { class DicomLoaderService {
getLocalData(dataset, studies) { async getLocalData(dataset, studies) {
if (dataset && dataset.localFile) { if (dataset && dataset.localFile) {
// Use referenced imageInstance // Use referenced imageInstance
const imageInstance = getImageInstance(dataset); const imageInstance = getImageInstance(dataset);
@@ -98,6 +98,7 @@ class DicomLoaderService {
} }
if (!someInvalidStrings(imageId)) { if (!someInvalidStrings(imageId)) {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId); return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
} }
} }
@@ -176,16 +177,16 @@ class DicomLoaderService {
} }
} }
*getLoaderIterator(dataset, studies) { async *getLoaderIterator(dataset, studies) {
yield this.getLocalData(dataset, studies); yield await this.getLocalData(dataset, studies);
yield this.getDataByImageType(dataset); yield this.getDataByImageType(dataset);
yield this.getDataByDatasetType(dataset); yield this.getDataByDatasetType(dataset);
} }
findDicomDataPromise(dataset, studies) { async findDicomDataPromise(dataset, studies) {
const loaderIterator = this.getLoaderIterator(dataset, studies); const loaderIterator = this.getLoaderIterator(dataset, studies);
// it returns first valid retriever method. // it returns first valid retriever method.
for (const loader of loaderIterator) { for await (const loader of loaderIterator) {
if (loader) { if (loader) {
return loader; return loader;
} }
+35
View File
@@ -3,6 +3,41 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.8.15](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.14...@ohif/viewer@3.8.15) (2020-04-02)
**Note:** Version bump only for package @ohif/viewer
## [3.8.14](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.13...@ohif/viewer@3.8.14) (2020-04-02)
**Note:** Version bump only for package @ohif/viewer
## [3.8.13](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.12...@ohif/viewer@3.8.13) (2020-04-01)
### Bug Fixes
* segmentation not loading ([#1566](https://github.com/OHIF/Viewers/issues/1566)) ([4a7ce1c](https://github.com/OHIF/Viewers/commit/4a7ce1c09324d74c61048393e3a2427757e4001a))
## [3.8.12](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.11...@ohif/viewer@3.8.12) (2020-03-31)
**Note:** Version bump only for package @ohif/viewer
## [3.8.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.10...@ohif/viewer@3.8.11) (2020-03-26) ## [3.8.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.10...@ohif/viewer@3.8.11) (2020-03-26)
+8 -8
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/viewer", "name": "@ohif/viewer",
"version": "3.8.11", "version": "3.8.15",
"description": "OHIF Viewer", "description": "OHIF Viewer",
"author": "OHIF Contributors", "author": "OHIF Contributors",
"license": "MIT", "license": "MIT",
@@ -48,14 +48,14 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"@ohif/core": "^2.6.4", "@ohif/core": "^2.6.6",
"@ohif/extension-cornerstone": "^2.5.0", "@ohif/extension-cornerstone": "^2.5.1",
"@ohif/extension-dicom-html": "^1.1.1", "@ohif/extension-dicom-html": "^1.1.2",
"@ohif/extension-dicom-microscopy": "^0.50.7", "@ohif/extension-dicom-microscopy": "^0.50.9",
"@ohif/extension-dicom-pdf": "^1.0.2", "@ohif/extension-dicom-pdf": "^1.0.3",
"@ohif/extension-dicom-segmentation": "^0.1.0", "@ohif/extension-dicom-segmentation": "^0.1.1",
"@ohif/extension-lesion-tracker": "^0.2.0", "@ohif/extension-lesion-tracker": "^0.2.0",
"@ohif/extension-vtk": "^1.3.5", "@ohif/extension-vtk": "^1.3.7",
"@ohif/i18n": "^0.52.7", "@ohif/i18n": "^0.52.7",
"@ohif/ui": "^1.4.1", "@ohif/ui": "^1.4.1",
"@tanem/react-nprogress": "^1.1.25", "@tanem/react-nprogress": "^1.1.25",
+1 -5
View File
@@ -34,10 +34,7 @@ import i18n from '@ohif/i18n';
import './config'; import './config';
/** Utils */ /** Utils */
import { import { getUserManagerForOpenIdConnectClient } from './utils/index.js';
getUserManagerForOpenIdConnectClient,
initWebWorkers,
} from './utils/index.js';
/** Extensions */ /** Extensions */
import { GenericViewerCommands, MeasurementsPanel } from './appExtensions'; import { GenericViewerCommands, MeasurementsPanel } from './appExtensions';
@@ -140,7 +137,6 @@ class App extends Component {
*/ */
_initHotkeys(appConfigHotkeys); _initHotkeys(appConfigHotkeys);
_initServers(servers); _initServers(servers);
initWebWorkers();
} }
render() { render() {
@@ -11,7 +11,7 @@ import EmptyViewport from './EmptyViewport.js';
const { loadAndCacheDerivedDisplaySets } = utils; const { loadAndCacheDerivedDisplaySets } = utils;
const ViewportGrid = function(props) { const ViewportGrid = function (props) {
const { const {
activeViewportIndex, activeViewportIndex,
availablePlugins, availablePlugins,
@@ -37,7 +37,7 @@ const ViewportGrid = function(props) {
viewportData.forEach(displaySet => { viewportData.forEach(displaySet => {
loadAndCacheDerivedDisplaySets(displaySet, studies); loadAndCacheDerivedDisplaySets(displaySet, studies);
}); });
}, [viewportData]); }, [studies, viewportData]);
const getViewportPanes = () => const getViewportPanes = () =>
layout.viewports.map((layout, viewportIndex) => { layout.viewports.map((layout, viewportIndex) => {
-30
View File
@@ -1,7 +1,4 @@
import OHIF from '@ohif/core';
import cornerstone from 'cornerstone-core'; import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import dicomParser from 'dicom-parser';
import version from './version.js'; import version from './version.js';
let homepage; let homepage;
@@ -15,31 +12,4 @@ window.info = {
homepage, homepage,
}; };
// For debugging
//if (process.env.node_env === 'development') {
window.cornerstone = cornerstone; window.cornerstone = cornerstone;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
//}
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return;
}
return state.oidc.user.access_token;
};
cornerstoneWADOImageLoader.configure({
beforeSend: function(xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization);
}
},
});
+1 -1
View File
@@ -3,7 +3,7 @@ import FileLoaderService from './localFileLoaders/fileLoaderService';
const processFile = async file => { const processFile = async file => {
try { try {
const fileLoaderService = new FileLoaderService(file); const fileLoaderService = new FileLoaderService(file);
const imageId = fileLoaderService.addFile(file); const imageId = await fileLoaderService.addFile(file);
const image = await fileLoaderService.loadFile(file, imageId); const image = await fileLoaderService.loadFile(file, imageId);
const dataset = await fileLoaderService.getDataset(image, imageId); const dataset = await fileLoaderService.getDataset(image, imageId);
const studies = await fileLoaderService.getStudies(dataset, imageId); const studies = await fileLoaderService.getStudies(dataset, imageId);
@@ -1,13 +1,14 @@
import * as dcmjs from 'dcmjs'; import * as dcmjs from 'dcmjs';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader'; import FileLoader from './fileLoader';
import OHIF from '@ohif/core'; import OHIF from '@ohif/core';
import { getCornerstoneWADOImageLoader } from '../../utils/cornerstoneWADOImageLoader';
const metadataProvider = OHIF.cornerstone.metadataProvider; const metadataProvider = OHIF.cornerstone.metadataProvider;
const DICOMFileLoader = new (class extends FileLoader { const DICOMFileLoader = new (class extends FileLoader {
fileType = 'application/dicom'; fileType = 'application/dicom';
loadFile(file, imageId) { async loadFile(file, imageId) {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId); return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
} }
@@ -1,7 +1,7 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader'; import FileLoader from './fileLoader';
import PDFFileLoader from './pdfFileLoader'; import PDFFileLoader from './pdfFileLoader';
import DICOMFileLoader from './dicomFileLoader'; import DICOMFileLoader from './dicomFileLoader';
import { getCornerstoneWADOImageLoader } from '../../utils/cornerstoneWADOImageLoader';
class FileLoaderService extends FileLoader { class FileLoaderService extends FileLoader {
fileType; fileType;
@@ -53,7 +53,8 @@ class FileLoaderService extends FileLoader {
return result; return result;
} }
addFile(file) { async addFile(file) {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
return cornerstoneWADOImageLoader.wadouri.fileManager.add(file); return cornerstoneWADOImageLoader.wadouri.fileManager.add(file);
} }
@@ -1,9 +1,10 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader'; import FileLoader from './fileLoader';
import { getCornerstoneWADOImageLoader } from '../../utils/cornerstoneWADOImageLoader';
const PDFFileLoader = new (class extends FileLoader { const PDFFileLoader = new (class extends FileLoader {
fileType = 'application/pdf'; fileType = 'application/pdf';
loadFile(file, imageId) { async loadFile(file, imageId) {
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId); return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
} }
+16 -7
View File
@@ -1,4 +1,5 @@
import asyncComponent from '../components/AsyncComponent.js'; import asyncComponent from '../components/AsyncComponent.js';
import { withCornerstone } from '../utils/cornerstoneWADOImageLoader';
import OHIF from '@ohif/core'; import OHIF from '@ohif/core';
const { urlUtil: UrlUtil } = OHIF.utils; const { urlUtil: UrlUtil } = OHIF.utils;
@@ -9,8 +10,10 @@ const IHEInvokeImageDisplay = asyncComponent(() =>
/* webpackChunkName: "IHEInvokeImageDisplay" */ './IHEInvokeImageDisplay.js' /* webpackChunkName: "IHEInvokeImageDisplay" */ './IHEInvokeImageDisplay.js'
) )
); );
const ViewerRouting = asyncComponent(() => const ViewerRouting = withCornerstone(
import(/* webpackChunkName: "ViewerRouting" */ './ViewerRouting.js') asyncComponent(() =>
import(/* webpackChunkName: "ViewerRouting" */ './ViewerRouting.js')
)
); );
const StudyListRouting = asyncComponent(() => const StudyListRouting = asyncComponent(() =>
@@ -18,12 +21,18 @@ const StudyListRouting = asyncComponent(() =>
/* webpackChunkName: "StudyListRouting" */ '../studylist/StudyListRouting.js' /* webpackChunkName: "StudyListRouting" */ '../studylist/StudyListRouting.js'
) )
); );
const StandaloneRouting = asyncComponent(() => const StandaloneRouting = withCornerstone(
import(/* webpackChunkName: "ConnectedStandaloneRouting" */ '../connectedComponents/ConnectedStandaloneRouting.js') asyncComponent(() =>
import(
/* webpackChunkName: "ConnectedStandaloneRouting" */ '../connectedComponents/ConnectedStandaloneRouting.js'
)
)
); );
const ViewerLocalFileData = asyncComponent(() => const ViewerLocalFileData = withCornerstone(
import( asyncComponent(() =>
/* webpackChunkName: "ViewerLocalFileData" */ '../connectedComponents/ViewerLocalFileData.js' import(
/* webpackChunkName: "ViewerLocalFileData" */ '../connectedComponents/ViewerLocalFileData.js'
)
) )
); );
@@ -0,0 +1,79 @@
import OHIF from '@ohif/core';
import cornerstone from 'cornerstone-core';
import dicomParser from 'dicom-parser';
import React from 'react';
export function getCornerstoneWADOImageLoader() {
return import(
/* webpackChunkName: "CornerstoneWADOImageLoader" */ 'cornerstone-wado-image-loader'
);
}
let initialized = false;
export async function initCornerstoneWADOImageLoader() {
if (initialized) return;
const cornerstoneWADOImageLoader = await getCornerstoneWADOImageLoader();
// For debugging
//if (process.env.node_env === 'development') {
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
//}
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return;
}
return state.oidc.user.accesstoken;
};
cornerstoneWADOImageLoader.configure({
beforeSend: function (xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization);
}
},
});
const config = {
maxWebWorkers: Math.max(navigator.hardwareConcurrency - 1, 1),
startWebWorkersOnDemand: true,
taskConfiguration: {
decodeTask: {
initializeCodecsOnStartup: false,
usePDFJS: false,
strict: false,
},
},
};
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
initialized = true;
}
export function withCornerstone(Component) {
// eslint-disable-next-line react/display-name
return props => {
const [
cornerstoneWADOImageLoaderInitialized,
setCornerstoneWADOImageLoaderInitialized,
] = React.useState(false);
React.useEffect(() => {
initCornerstoneWADOImageLoader().then(() =>
setCornerstoneWADOImageLoaderInitialized(true)
);
}, []);
if (!cornerstoneWADOImageLoaderInitialized) {
return null;
}
return <Component {...props} />;
};
}
+1 -2
View File
@@ -1,4 +1,3 @@
import getUserManagerForOpenIdConnectClient from './getUserManagerForOpenIdConnectClient.js'; import getUserManagerForOpenIdConnectClient from './getUserManagerForOpenIdConnectClient.js';
import initWebWorkers from './initWebWorkers.js';
export { getUserManagerForOpenIdConnectClient, initWebWorkers }; export { getUserManagerForOpenIdConnectClient };
+1 -1
View File
@@ -5,7 +5,7 @@ describe('utils', () => {
const utilExports = Object.keys(utils).sort(); const utilExports = Object.keys(utils).sort();
expect(utilExports).toEqual( expect(utilExports).toEqual(
['getUserManagerForOpenIdConnectClient', 'initWebWorkers'].sort() ['getUserManagerForOpenIdConnectClient'].sort()
); );
}); });
}); });
@@ -1,22 +0,0 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
let initialized = false;
export default function initWebWorkers() {
const config = {
maxWebWorkers: Math.max(navigator.hardwareConcurrency - 1, 1),
startWebWorkersOnDemand: true,
taskConfiguration: {
decodeTask: {
initializeCodecsOnStartup: false,
usePDFJS: false,
strict: false,
},
},
};
if (!initialized) {
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
initialized = true;
}
}
@@ -1,23 +0,0 @@
import initWebWorkers from './initWebWorkers.js';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
describe('initWebWorkers', () => {
it("initializes cornerstoneWADOImageLoader's web workers", () => {
initWebWorkers();
expect(
cornerstoneWADOImageLoader.webWorkerManager.initialize
).toHaveBeenCalled();
});
});
describe('initWebWorkers', () => {
it("initializes cornerstoneWADOImageLoader's web workers only once", () => {
initWebWorkers();
initWebWorkers();
expect(
cornerstoneWADOImageLoader.webWorkerManager.initialize
).toHaveBeenCalledTimes(1);
});
});