feat: cornerstone3D stack and volume viewports (#2787)
* feat: cs3d working stack viewport and tools (#19) * Squashed everything * fix weird react issue * fix eslint / prettier stuff * thumbnails work now with cpu rendering * feat: use new loadImageToCanvas in cs3d * make jump to measurement work * fix active thumbnail * fix measurement delete * fix window level presets * remove segmentation and sync groups for now * fix the dicom pdf and dicom video * fix cornerstone window assignment for cypress * apply review comments Co-authored-by: Erik <erik.sweed@gmail.com> * feat: cs3d tools and toolGroups (#20) * add more tools to work with cs3d mode * fix hotkeys * add stack manager usage for stach viewports * add image scrollbar * wip viewport overlay * fix toAnnotation schema for tools * fix the unnecessary size change that triggered resize * hanging protocol improvement to allow unmatched errors * study description matching for hanging protocol * fix the displaysetOptions to work * fix handle the active tool when a new viewport is added * fix separate toolGroups for mode * apply review comments * apply review comments * yarn lock * feat: overlay component (#21) * fix default displayset options * add viewport overlay * apply review comments * feat: loading and orientation indicators (#22) * add loading indicator * add orientation marker initial work * apply review comments * fix: orientation markers (#23) * finished the orientation markers * fix various broken cypress tests * apply review comments * update yarn lock * feat: re-working measurement tracking mode with cornerstone3d (#2805) * feat: cs3d working stack viewport and tools (#19) * Squashed everything * fix weird react issue * fix eslint / prettier stuff * thumbnails work now with cpu rendering * feat: use new loadImageToCanvas in cs3d * make jump to measurement work * fix active thumbnail * fix measurement delete * fix window level presets * remove segmentation and sync groups for now * fix the dicom pdf and dicom video * fix cornerstone window assignment for cypress * apply review comments Co-authored-by: Erik <erik.sweed@gmail.com> * feat: Add Measurement tracking mode with cs3D (#2789) * feat: first render for cornerstone3d tracked viewport * make tool active work * wip for SR extension * renamed dicom sr to cornerstone dicom sr * remove cornerstone from dicom pdf and video * move dicom sr logic to sr extension * feat: Add hydration for the length tool * fix SR display tool for length using cs3d * fix default config * fix: various bugs with sr viewport and tracking * fix promptying to continue tracking for when SR is created * feat: add keep trackign of unique identifiers * fix hydration for same imageIds * feat: Add SR toolGroup creation on modeEnter * feat: remove the need for separate mapper for SR hydration * add SR display for ellipse * handle hydration of elliptical ROI tool * remove cornerstone extension * add arrow mapping * feat: Add ArrowAnnotate SR display and hydration * apply review comments * apply review comments * move viewport labels to the viewportData * apply review comments * fix: integration cypress tests with cornerstone3D and add CINE tool (#2795) * fix: integration cypress tests with cornerstone3D * revert to addOrUpdate as it makes more sense * fix local drag and drop * fix tests * move dicomLoaderService to cornerstone extension * fix various import bugs * fix bug for local PT series * fix various unit tests * bump cs3d versions * add angle and magnify tool * bump dependencies to avoid broken peerDeps * feat: add initial work for capture using cs3d * feat: show annotations on the image capture * feat: add svg layer export * feat: Add CINE Tool * feat: remove unnecessary viewport rendering for cine state changes Co-authored-by: Erik Ziegler <erik.sweed@gmail.com> * docs: modify and improve documentation (#2800) * cleanup docs versionings * feat: Add all version explanations * version docs for 3.0 * wip for changing docs * wip for updated docs * add utility module documentation * fix demo with nohoisting of history * add slides and video to resources * apply review comments * fix: drag and drop SR into SR viewport (#2803) * update yarn lock Co-authored-by: Erik <erik.sweed@gmail.com> * update pathnames to match v3-stable * update the e2e pathname * fix: various bugs with regard to tracking workflow (#2811) * fix: various issues with measurement panel * fix: update default tool style for annotations * fix: annotatoin label getting removed * feat: Add backward compatibility for SR hydration with legacy cornerstone * fix: cursors and ellipse ROI max style * fix: ArrowAnnotate SRDisplay * apply review comments * bump package versions * fix: bugin rehydration of SR * fix: e2e tests * fix active viewport thickness and arrowTool ui * add readme for measurement tracking * use uploaded image for readme * add back images * try to fix e2e test * fix: window level presets hotkeys * Update README.md * update yarn lock * feat: volume api and TMTV mode (#2817) * feat: volumeAPI and TMTV mode * feat: use cs3d cache service to obtain viewportData * wip for volume api * wip: fusion viewport * feat: add blend mode option * wip for image scrollbar * fix drag and drop thumbnail * wip for image scrollbar for voluems * fix: element mismatch bug for scroll * feat: Add image scrollbar to volumes * feat: add syncGroups to volume api * feat: add tmtv mode initial setup * feat: add initial image options for the stack viewports * feat: Add custom load strategy for volume viewports via HP * apply review comments fix: Jump presets cs3d (#2812) * feat: Add JumpPreset to OHIF for Cornerstone3D * fix: accessing viewport service from servicesManager feat: volume API and TMTV mode (#2814) * fix: do not display overlays on mip viewports * feat: add optional disableCommands for toggle buttons * fix: toggleCrossharis * feat: config the crosshairs * feat: add PetSUV Panel for changing metadata * feat: initial work for the rectangleROIThreshold panel * wip: measurement service * roi threshold working * feat: Add displayText to segmentations * feat: add remove segmentation * feat: add csv export * feat: add RT export for annotations in tmtv mode * fix: fusion to use pt in tmtv mode and measuremet mappings * fix: various bugs * apply review comments * apply review comments * feat: add fusion color maps * add readme to tmtv mode * Update README.md * fix: try to fix build * fix unit tests * Update README.md * feat: add about to the panel * fix: changing strategy in roi panel * apply review comments * update package versions * wip for stackPrefetch * fix: cornerstone3d hydration and renaming (#2818) * update readme * renamed cornerstone extension * wip for renaming cornerstone3D variables * wip for renaming cornerstone3D variables * wip for fixing bugs for SR viewport * fix: jumpToMeasurement and initial label after hydration * fix: fileName capitalization * feat: use the new prefetch stack in the cs3d (#2820) * feat: use the new prefetch stack in the cs3d * use viewport scroll api for stack viewport * fix cine stop when scrollbar changes * feat: use new prefetch events * fix loading state to not show repeatedly * fix: various bugs for tmtv mode thresholding and new icons (#2823) * feat: make tmtv mode available in worklist * feat: add new icons for tmtv mode * feat: add fusion color icon * fix: parallel scale calculation * fix: bump Cornerstone to get large image support working * fix: Fix issues with CPU viewport flipping, including config files * fix: bump cornerstone to fix magnify tool * fix: Bump Cornerstone version to fix resetCamera issue in StackViewport * ci: Add _headers file to enable CORS headers for Netlify Drag/Drop deploys * fix: WADO-URI was not working. PET Metadata was coming from the wrong place. Fixed some minor React errors * feat(OHIF):Allow modes and extensions to be added after commpile time. (#2838) Also works with the compile time add that the existing cli uses, so that both build types work. Eric and I agreed this doesn't change existing functionality, but is almost entirely build issues/fixes. * bump: dependency versions to fix hydration bugs (#2848) * bump: dcmjs version to fix hydration bugs * try to fix tests * bump dependency versions Co-authored-by: Alireza <ar.sedghi@gmail.com> Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
This commit is contained in:
668 files changed
+41669
-13410
No files matched your search
@@ -12,7 +12,7 @@ Key features:
|
||||
|
||||
- Designed to load large radiology studies as quickly as possible. Retrieves
|
||||
metadata ahead of time and streams in imaging pixel data as needed.
|
||||
- Leverages [Cornerstone.js](https://cornerstonejs.org/) for decoding,
|
||||
- Leverages [Cornerstone3D](https://github.com/cornerstonejs/cornerstone3D-beta) for decoding,
|
||||
rendering, and annotating medical images.
|
||||
- Works out-of-the-box with Image Archives that support [DICOMWeb][dicom-web].
|
||||
Offers a Data Source API for communicating with archives over proprietary API
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
sidebar_label: Configuration
|
||||
---
|
||||
|
||||
# Viewer: Configuration
|
||||
|
||||
The OHIF Viewer Platform strives to be highly configurable and extensible. This
|
||||
makes it easier for our community members to keep their "secret sauce" private,
|
||||
and incentives contributions back to the platform. The `@ohif/viewer` project of
|
||||
the platform is the lynchpin that combines everything to create our application.
|
||||
|
||||
There are two configuration mechanisms, one for run-time configuration, allowing
|
||||
for changes to be decided based on including different configuration files as
|
||||
specified by the 'theme' URL parameters. This mechanism is intended for
|
||||
modifications of data exposed as configurable items by the existing code. See
|
||||
sections below on configuring this type of value.
|
||||
|
||||
The other mechanism is the code-configuration mechanism that specifies load
|
||||
time configuration. This is intended to load things that require code level
|
||||
changes to OHIF such as adding a new viewer configuration. This is also used
|
||||
for base definitions that are shared site-wide such as the data sources. This
|
||||
was the original configuration mechanism provided, and some of the configurations
|
||||
specified there are better suited to the run time loading, but are currently
|
||||
left alone as there hasn't been time to move them.
|
||||
|
||||
We maintain a number of common viewer application configurations at
|
||||
[`<root>/platform/viewer/public/configs`][config-dir].
|
||||
|
||||
You can take a look at how to use different configs in the
|
||||
[Environment Variables](../platform/environment-variables)
|
||||
|
||||
```js title="<root>/platform/viewer/public/configs"
|
||||
window.config = {
|
||||
routerBasename: '/',
|
||||
/**
|
||||
* "White Labeling" is used to change the branding, look, and feel of the OHIF
|
||||
* Viewer. These settings, and the color variables that are used by our components,
|
||||
* are the easiest way to rebrand the application.
|
||||
*
|
||||
* More extensive changes are made possible through swapping out the UI library,
|
||||
* Viewer project, or extensions.
|
||||
*/
|
||||
whiteLabeling: {
|
||||
/** ... **/
|
||||
},
|
||||
httpErrorHandler: {
|
||||
/** coming soon **/
|
||||
},
|
||||
extensions: [],
|
||||
showStudyList: true,
|
||||
filterQueryParam: false,
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
qidoSupportsIncludeField: true,
|
||||
supportsReject: true,
|
||||
imageRendering: 'wadors',
|
||||
thumbnailRendering: 'wadors',
|
||||
enableStudyLazyLoad: true,
|
||||
supportsFuzzyMatching: true,
|
||||
supportsWildcard: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
<!--
|
||||
LINKS
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[config-dir]: https://github.com/OHIF/Viewers/tree/master/platform/viewer/public/config
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -24,15 +24,16 @@ you'll see the following:
|
||||
```bash
|
||||
│
|
||||
├── extensions
|
||||
│ ├── _example # Skeleton of example extension
|
||||
│ ├── default # default functionalities
|
||||
│ ├── cornerstone # 2D images w/ Cornerstone.js
|
||||
│ ├── measurement-tracking # measurement tracking
|
||||
│ ├── dicom-sr # Structured reports
|
||||
│ └── dicom-pdf # View DICOM wrapped PDFs in viewport
|
||||
│ ├── _example # Skeleton of example extension
|
||||
│ ├── default # default functionalities
|
||||
│ ├── cornerstone # 2D/3D images w/ Cornerstonejs
|
||||
│ ├── cornerstone-dicom-sr # Structured reports
|
||||
│ ├── measurement-tracking # measurement tracking
|
||||
│ └── dicom-pdf # View DICOM wrapped PDFs in viewport
|
||||
│
|
||||
├── modes
|
||||
│ └── longitudinal # longitudinal measurement tracking mode
|
||||
| └── basic-dev-mode # basic viewer with Cornerstone (a developer focused mode)
|
||||
│
|
||||
├── platform
|
||||
│ ├── core # Business Logic
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
sidebar_label: Continous Integration
|
||||
sidebar_label: Continuos Integration
|
||||
---
|
||||
|
||||
# Continous Integration (CI)
|
||||
# Continuos Integration (CI)
|
||||
|
||||
This repository uses `CircleCI` and `Netlify` for continous integration.
|
||||
This repository uses `CircleCI` and `Netlify` for Continuos integration.
|
||||
|
||||
## Deploy Previews
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@ up-to-date with the upstream (original) repository. This is called a "Triangular
|
||||
Workflow" and is common for Open Source projects. The GitHub blog has a [good
|
||||
graphic that illustrates this setup][triangular-workflow].
|
||||
|
||||
### `v3-stable` branch
|
||||
Currently the stable branch for OHIF-v3 is `v3-stable`. Once the v3-stable branch has
|
||||
feature parity with the master branch, `v3-stable` will be pushed to the master branch.
|
||||
You can read more about the roadmap timeline [here](https://ohif.org/roadmap).
|
||||
|
||||
### Private
|
||||
|
||||
Alternatively, if you intend to use the OHIF Viewer as a starting point, and you
|
||||
@@ -106,6 +111,6 @@ yarn run build
|
||||
[add-remote-repo]: https://help.github.com/en/articles/fork-a-repo#step-3-configure-git-to-sync-your-fork-with-the-original-spoon-knife-repository
|
||||
[sync-changes]: https://help.github.com/en/articles/syncing-a-fork
|
||||
[triangular-workflow]: https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/#improved-support-for-triangular-workflows
|
||||
[ohif-viewers-repo]: https://github.com/OHIF/Viewers
|
||||
[ohif-viewers-repo]: https://github.com/OHIF/Viewers/tree/v3-stable
|
||||
[ohif-viewers]: https://github.com/OHIF/Viewers
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -65,6 +65,7 @@ export default {
|
||||
getContextModule() { /* */ },
|
||||
getToolbarModule() { /* */ },
|
||||
getHangingProtocolModule() { /* */ },
|
||||
getUtilityModule() { /* */ },
|
||||
}
|
||||
```
|
||||
|
||||
@@ -86,7 +87,7 @@ the top level [`extensions/`][ext-source] directory.
|
||||
<tr>
|
||||
<td>
|
||||
<a href="">
|
||||
Default
|
||||
default
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
@@ -97,18 +98,18 @@ the top level [`extensions/`][ext-source] directory.
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-cornerstone">
|
||||
Cornerstone
|
||||
<a href="">
|
||||
cornerstone
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
Provides rendering functionalities for 2D images.
|
||||
Provides 2d and 3d rendering functionalities
|
||||
</td>
|
||||
<td>ViewportModule, CommandsModule</td>
|
||||
<td>ViewportModule, CommandsModule, UtilityModule</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.npmjs.com/package/@ohif/extension-dicom-pdf">DICOM PDF</a>
|
||||
<a href="">dicom-pdf</a>
|
||||
</td>
|
||||
<td>
|
||||
Renders PDFs for a <a href="https://github.com/OHIF/Viewers/blob/master/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js#L4-L6">specific SopClassUID</a>.
|
||||
@@ -117,7 +118,16 @@ the top level [`extensions/`][ext-source] directory.
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="">DICOM SR</a>
|
||||
<a href="">dicom-video</a>
|
||||
</td>
|
||||
<td>
|
||||
Renders DICOM Video files.
|
||||
</td>
|
||||
<td>Viewport, SopClassHandler</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="">cornerstone-dicom-sr</a>
|
||||
</td>
|
||||
<td>
|
||||
Maintained extensions for cornerstone and visualization of DICOM Structured Reports
|
||||
@@ -126,7 +136,7 @@ the top level [`extensions/`][ext-source] directory.
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="">Measurement tracking</a>
|
||||
<a href="">measurement-tracking</a>
|
||||
</td>
|
||||
<td>
|
||||
Tracking measurements in the measurement panel
|
||||
@@ -290,6 +300,14 @@ differently.
|
||||
</td>
|
||||
<td align="left">Adds hanging protocol rules</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<a href="./modules/hpModule">
|
||||
Utility
|
||||
</a>
|
||||
</td>
|
||||
<td align="left">Expose utility functions to the outside of extensions</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ Extensions can implement specific lifecycle methods.
|
||||
## preRegistration
|
||||
|
||||
If an extension defines the `preRegistration` lifecycle hook, it is called
|
||||
before any modules are registered in the `ExtensionManager`. This hook can be
|
||||
used to:
|
||||
before any modules are registered in the `ExtensionManager`. This hook is an
|
||||
`async` function that can be used to perform:
|
||||
|
||||
- initialize 3rd party libraries
|
||||
- register event listeners
|
||||
@@ -61,7 +61,7 @@ export default {
|
||||
* @param {CommandsManager} params.commandsManager
|
||||
* @returns void
|
||||
*/
|
||||
preRegistration({ servicesManager, commandsManager, configuration }) {
|
||||
async preRegistration({ servicesManager, commandsManager, configuration }) {
|
||||
console.log('Wiring up important stuff.');
|
||||
|
||||
window.importantStuff = () => {
|
||||
@@ -90,7 +90,7 @@ _Example `onModeEnter` hook implementation_
|
||||
|
||||
```js
|
||||
export default {
|
||||
id: '@ohif/extension-dicom-sr',
|
||||
id: '@ohif/extension-cornerstone-dicom-sr',
|
||||
|
||||
onModeEnter({ servicesManager }) {
|
||||
const { DisplaySetService } = servicesManager.services;
|
||||
|
||||
@@ -22,26 +22,40 @@ const deafultProtocol = {
|
||||
availableTo: {},
|
||||
editableBy: {},
|
||||
protocolMatchingRules: [],
|
||||
toolGroupIds: ['default'],
|
||||
stages: [
|
||||
{
|
||||
id: 'nwzau7jDkEkL8djfr',
|
||||
name: 'oneByOne',
|
||||
id: 'hYbmMy3b7pz7GLiaT',
|
||||
name: 'default',
|
||||
viewportStructure: {
|
||||
type: 'grid',
|
||||
properties: {
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
columns: 2,
|
||||
},
|
||||
},
|
||||
viewports: [
|
||||
displaySets: [
|
||||
{
|
||||
viewportSettings: [],
|
||||
id: 'displaySet',
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
],
|
||||
createdDate: '2021-02-23T19:22:08.894Z',
|
||||
viewports: [
|
||||
{
|
||||
viewportOptions: {
|
||||
toolGroupId: 'default',
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
options: [],
|
||||
id: 'displaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
createdDate: '2021-02-23T18:32:42.850Z',
|
||||
},
|
||||
],
|
||||
numberOfPriorsReferenced: -1,
|
||||
@@ -51,8 +65,385 @@ function getHangingProtocolModule() {
|
||||
return [
|
||||
{
|
||||
name: hangingProtocolName,
|
||||
protocols: [deafultProtocol],
|
||||
protocols: [defaultProtocol],
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Skeleton of A Hanging Protocol
|
||||
|
||||
The skeleton of a hanging protocol is as follows:
|
||||
|
||||
|
||||
```js
|
||||
const testProtocol = {
|
||||
id: 'test',
|
||||
locked: true,
|
||||
hasUpdatedPriorsInformation: false,
|
||||
name: 'Default',
|
||||
createdDate: '2021-02-23T19:22:08.894Z',
|
||||
modifiedDate: '2021-02-23T19:22:08.894Z',
|
||||
availableTo: {},
|
||||
editableBy: {},
|
||||
toolGroupIds: ['default'],
|
||||
protocolMatchingRules: [
|
||||
{
|
||||
id: 'wauZK2QNEfDPwcAQo',
|
||||
weight: 1,
|
||||
attribute: 'StudyDescription',
|
||||
constraint: {
|
||||
contains: {
|
||||
value: 'PETCT',
|
||||
},
|
||||
},
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'wauZK2QNEfDPwcAQo',
|
||||
weight: 1,
|
||||
attribute: 'StudyDescription',
|
||||
constraint: {
|
||||
contains: {
|
||||
value: 'PET/CT',
|
||||
},
|
||||
},
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
stages: [
|
||||
{
|
||||
id: 'hYbmMy3b7pz7GLiaT',
|
||||
name: 'default',
|
||||
viewportStructure: {
|
||||
type: 'grid',
|
||||
properties: {
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
viewportOptions: [],
|
||||
},
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'ctDisplaySet',
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
id: 'GPEYqFLv2dwzCM322',
|
||||
weight: 1,
|
||||
attribute: 'Modality',
|
||||
constraint: {
|
||||
equals: {
|
||||
value: 'CT',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
{
|
||||
id: 'ptACDisplaySet',
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
id: 'GPEYqFLv2dwzCM322',
|
||||
weight: 1,
|
||||
attribute: 'Modality',
|
||||
constraint: {
|
||||
equals: {
|
||||
value: 'PT',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
{
|
||||
id: 'ptNACDisplaySet',
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
id: 'GPEYqFLv2dwzCM322',
|
||||
weight: 1,
|
||||
attribute: 'SeriesDescription',
|
||||
constraint: {
|
||||
contains: {
|
||||
value: 'Uncorrected',
|
||||
},
|
||||
},
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
],
|
||||
viewports: [
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportId: 'ctAxial',
|
||||
viewportType: 'stack',
|
||||
background: [0, 0, 0],
|
||||
orientation: 'AXIAL',
|
||||
toolGroupId: 'default',
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'ctDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportId: 'ptAxial',
|
||||
viewportType: 'stack',
|
||||
background: [1, 1, 1],
|
||||
orientation: 'AXIAL',
|
||||
toolGroupId: 'default',
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
options: {
|
||||
voi: {
|
||||
windowWidth: 5,
|
||||
windowCenter: 2.5,
|
||||
},
|
||||
voiInverted: true,
|
||||
},
|
||||
id: 'ptACDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
createdDate: '2021-02-23T18:32:42.850Z',
|
||||
},
|
||||
],
|
||||
numberOfPriorsReferenced: -1,
|
||||
};
|
||||
```
|
||||
|
||||
Let's discuss each property in depth.
|
||||
|
||||
### Id
|
||||
unique identifier for the protocol
|
||||
|
||||
### protocolMatchingRules
|
||||
A list of criteria for the protocol along with the provided points for ranking.
|
||||
|
||||
- `weight`: weight for the matching rule. Eventually, all the registered
|
||||
protocols get sorted based on the weights, and the winning protocol gets
|
||||
applied to the viewer.
|
||||
- `attribute`: tag that needs to be matched against. This can be either
|
||||
Study-level metadata or a custom attribute.
|
||||
[Learn more about custom attribute matching](#custom-attribute)
|
||||
|
||||
- `constraint`: the constraint that needs to be satisfied for the attribute. It accepts a `validator` which can be
|
||||
[`equals`, `doesNotEqual`, `contains`, `doesNotContain`, `startsWith`, `endsWidth`]
|
||||
|
||||
A sample of the matching rule is above which matches against the study description to be PETCT
|
||||
|
||||
```js
|
||||
{
|
||||
id: 'wauZK2QNEfDPwcAQo',
|
||||
weight: 1,
|
||||
attribute: 'StudyDescription',
|
||||
constraint: {
|
||||
contains: {
|
||||
value: 'PETCT',
|
||||
},
|
||||
},
|
||||
required: false,
|
||||
},
|
||||
```
|
||||
|
||||
### stages
|
||||
Each protocol can define one or more stages. Each stage defines a certain layout and viewport rules.
|
||||
Therefore, the `stages` property is array of objects, each object being one stage.
|
||||
|
||||
### viewportStructure
|
||||
Defines the layout of the viewer. You can define the number of `rows` and `columns`. There should be `rows * columns` number of
|
||||
viewport configuration in the `viewports` property. Note that order of viewports are rows first then columns.
|
||||
|
||||
```js
|
||||
viewportStructure: {
|
||||
type: 'grid',
|
||||
properties: {
|
||||
rows: 1,
|
||||
columns: 2,
|
||||
viewportOptions: [],
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
In addition to the equal viewport sizes, you can define viewports to span multiple rows or columns.
|
||||
|
||||
```js
|
||||
viewportStructure: {
|
||||
type: 'grid',
|
||||
properties: {
|
||||
rows: 1,
|
||||
columns: 2,
|
||||
viewportOptions: [
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1 / 4,
|
||||
height: 1,
|
||||
},
|
||||
{
|
||||
x: 1 / 4,
|
||||
y: 0,
|
||||
width: 3 / 4,
|
||||
height: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### displaySets
|
||||
Defines the display sets that are available for the stage. Each
|
||||
include an `id` and some `seriesMatchingRules`
|
||||
|
||||
```js
|
||||
displaySets: [
|
||||
{
|
||||
id: 'ctDisplaySet',
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
id: 'GPEYqFLv2dwzCM322',
|
||||
weight: 1,
|
||||
attribute: 'Modality',
|
||||
constraint: {
|
||||
equals: {
|
||||
value: 'CT',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
{
|
||||
id: 'ptACDisplaySet',
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
id: 'GPEYqFLv2dwzCM322',
|
||||
weight: 1,
|
||||
attribute: 'Modality',
|
||||
constraint: {
|
||||
equals: {
|
||||
value: 'PT',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
{
|
||||
id: 'ptNACDisplaySet',
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
id: 'GPEYqFLv2dwzCM322',
|
||||
weight: 1,
|
||||
attribute: 'Modality',
|
||||
constraint: {
|
||||
equals: {
|
||||
value: 'PT',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'GPEYqFLv2dwzCM322',
|
||||
weight: 1,
|
||||
attribute: 'SeriesDescription',
|
||||
constraint: {
|
||||
contains: {
|
||||
value: 'Uncorrected',
|
||||
},
|
||||
},
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
],
|
||||
```
|
||||
|
||||
As you see in the above `displaySets` entry in the example above, there are
|
||||
three defined displaySets that __will__ become available later on in the `viewports`
|
||||
section of the protocol.
|
||||
|
||||
|
||||
1. `ctDisplaySet` which is matched against the CT modality
|
||||
2. `ptACDisplaySet` which is matched against the PT modality
|
||||
3. `ptNACDisplaySet` which is matched against the PT modality and also contains the `Uncorrected` series description
|
||||
|
||||
### viewports
|
||||
This field includes the viewports that will get hung on the viewer.
|
||||
|
||||
```js
|
||||
viewports: [
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportId: 'ctAxial',
|
||||
viewportType: 'stack',
|
||||
background: [0, 0, 0],
|
||||
orientation: 'AXIAL',
|
||||
toolGroupId: 'default',
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'ctDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportId: 'ptAxial',
|
||||
viewportType: 'stack',
|
||||
background: [1, 1, 1],
|
||||
orientation: 'AXIAL',
|
||||
toolGroupId: 'default',
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
options: {
|
||||
voi: {
|
||||
windowWidth: 5,
|
||||
windowCenter: 2.5,
|
||||
},
|
||||
voiInverted: true,
|
||||
},
|
||||
id: 'ptACDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
```
|
||||
|
||||
As you can see there are two viewports defined. The first one is a CT viewport and the second one is a PT viewport.
|
||||
|
||||
Each viewport has two properties:
|
||||
|
||||
1. `viewportOptions`: defines the viewport properties such as
|
||||
- `viewportId`: unique identifier for the viewport (optional)
|
||||
- `viewportType`: type of the viewport (required - currently only stack)
|
||||
- `background`: background color of the viewport (optional)
|
||||
- `orientation`: orientation of the viewport (optional)
|
||||
- `toolGroupId`: tool group that will be used for the viewport (optional)
|
||||
|
||||
|
||||
2. `displaySets`: defines the display sets that are available for the viewport. It is an array of objects, each object being one display set.
|
||||
- `id`: id of the display set (required)
|
||||
- `options` (optional): options for the display set
|
||||
- voi: windowing options for the display set
|
||||
- voiInverted: whether the VOI is inverted or not (optional)
|
||||
@@ -72,7 +72,7 @@ const extensionDependencies = {
|
||||
'@ohif/extension-default': '^3.0.0',
|
||||
'@ohif/extension-cornerstone': '^3.0.0',
|
||||
'@ohif/extension-measurement-tracking': '^3.0.0',
|
||||
'@ohif/extension-dicom-sr': '^3.0.0',
|
||||
'@ohif/extension-cornerstone-dicom-sr': '^3.0.0',
|
||||
};
|
||||
|
||||
const id = 'viewer'
|
||||
|
||||
@@ -89,25 +89,32 @@ The simplest toolbarButtons definition has the following properties:
|
||||
|
||||
```js
|
||||
{
|
||||
id: 'Zoom',
|
||||
type: 'ohif.radioGroup',
|
||||
props: {
|
||||
type: 'tool',
|
||||
icon: 'tool-zoom',
|
||||
label: 'Zoom',
|
||||
commandOptions: { toolName: 'Zoom' },
|
||||
},
|
||||
},
|
||||
"id": "Zoom",
|
||||
"type": "ohif.radioGroup",
|
||||
"props": {
|
||||
"type": "tool",
|
||||
"icon": "tool-zoom",
|
||||
"label": "Zoom",
|
||||
"commands": [
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "Zoom"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| property | description | values |
|
||||
| ---------------- | ----------------------------------------------------------------- | ------------------------------------------- |
|
||||
| `id` | Unique string identifier for the definition | \* |
|
||||
| `label` | User/display friendly to show in UI | \* |
|
||||
| `icon` | A string name for an icon supported by the consuming application. | \* |
|
||||
| `type` | Used to determine the button's behaviour | "tool", "toggle", "action" |
|
||||
| `commandName` | (optional) The command to run when the button is used. | Any command registered by a `CommandModule` |
|
||||
| `commandOptions` | (optional) Options to pass the target `commandName` | \* |
|
||||
| `icon` | A string name for an icon supported by the consuming application. | \* |
|
||||
| `label` | User/display friendly to show in UI | \* |
|
||||
| `commands` | (optional) The commands to run when the button is used. It include a commandName, commandOptions, and/or a context | Any command registered by a `CommandModule` |
|
||||
|
||||
There are three main types of toolbar buttons:
|
||||
|
||||
@@ -133,59 +140,92 @@ to create `MeasurementTools` nested button
|
||||
|
||||
```js title="modes/longitudinal/src/toolbarButtons.js"
|
||||
{
|
||||
id: 'MeasurementTools',
|
||||
type: 'ohif.splitButton',
|
||||
props: {
|
||||
groupId: 'MeasurementTools',
|
||||
isRadio: true,
|
||||
primary: {
|
||||
id: 'Length',
|
||||
icon: 'tool-length',
|
||||
label: 'Length',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'Length',
|
||||
}
|
||||
"id": "MeasurementTools",
|
||||
"type": "ohif.splitButton",
|
||||
"props": {
|
||||
"groupId": "MeasurementTools",
|
||||
"isRadio": true,
|
||||
"primary": {
|
||||
"id": "Length",
|
||||
"icon": "tool-length",
|
||||
"label": "Length",
|
||||
"type": "tool",
|
||||
"commands": [
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "Length"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
},
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "SRLength",
|
||||
"toolGroupId": "SRToolGroup"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
}
|
||||
],
|
||||
"tooltip": "Length"
|
||||
},
|
||||
secondary: {
|
||||
icon: 'chevron-down',
|
||||
label: '',
|
||||
isActive: true,
|
||||
tooltip: 'More Measure Tools',
|
||||
"secondary": {
|
||||
"icon": "chevron-down",
|
||||
"label": "",
|
||||
"isActive": true,
|
||||
"tooltip": "More Measure Tools"
|
||||
},
|
||||
items: [
|
||||
// Length tool
|
||||
"items": [
|
||||
{
|
||||
id: 'Length',
|
||||
icon: 'tool-length',
|
||||
label: 'Length',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'Length',
|
||||
}
|
||||
"id": "Bidirectional",
|
||||
"icon": "tool-bidirectional",
|
||||
"label": "Bidirectional",
|
||||
"type": "tool",
|
||||
"commands": [
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "Bidirectional"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
},
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "SRBidirectional",
|
||||
"toolGroupId": "SRToolGroup"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
}
|
||||
],
|
||||
"tooltip": "Bidirectional Tool"
|
||||
},
|
||||
// Bidirectional tool
|
||||
{
|
||||
id: 'Bidirectional',
|
||||
icon: 'tool-bidirectional',
|
||||
label: 'Length',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'Bidirectional',
|
||||
}
|
||||
"id": "ArrowAnnotate",
|
||||
"icon": "tool-annotate",
|
||||
"label": "Annotation",
|
||||
"type": "tool",
|
||||
"commands": [
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "ArrowAnnotate"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
},
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "SRArrowAnnotate",
|
||||
"toolGroupId": "SRToolGroup"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
}
|
||||
],
|
||||
"tooltip": "Arrow Annotate"
|
||||
},
|
||||
// Ellipse tool
|
||||
{
|
||||
id: 'EllipticalRoi',
|
||||
icon: 'tool-elipse',
|
||||
label: 'Ellipse',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'EllipticalRoi',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
sidebar_label: Utility
|
||||
---
|
||||
|
||||
# Module: Utility
|
||||
|
||||
## Overview
|
||||
|
||||
Often, an extension will need to expose some useful functionality to the other
|
||||
extensions, or modes that consume the extension. For example, the `cornerstone`
|
||||
extension, uses its `utility` module to expose methods via
|
||||
|
||||
```js
|
||||
getUtilityModule({ servicesManager }) {
|
||||
return [
|
||||
{
|
||||
name: 'common',
|
||||
exports: {
|
||||
getCornerstoneLibraries: () => {
|
||||
return { cornerstone, cornerstoneTools };
|
||||
},
|
||||
getEnabledElement,
|
||||
CornerstoneViewportService,
|
||||
dicomLoaderService,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'core',
|
||||
exports: {
|
||||
Enums: cs3DEnums,
|
||||
CONSTANTS,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'tools',
|
||||
exports: {
|
||||
toolNames,
|
||||
Enums: cs3DToolsEnums,
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Then a consuming extension can use `getModuleEntry` to access the methods
|
||||
Below, which is a code from `TrackedCornerstoneViewport` use the `getUtilityModule` method to get the internal `CornerstoneViewportService` which handles the `Cornerstone` viewport.
|
||||
|
||||
```js title="extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.tsx"
|
||||
const utilityModule = extensionManager.getModuleEntry(
|
||||
'@ohif/extension-cornerstone.utilityModule.common'
|
||||
);
|
||||
|
||||
const { CornerstoneViewportService } = utilityModule.exports;
|
||||
const viewportId = CornerstoneViewportService.getViewportId(viewportIndex);
|
||||
```
|
||||
@@ -14,10 +14,7 @@ add support for:
|
||||
|
||||
- 2D Medical Image Viewing (cornerstone ext.)
|
||||
- Structured Reports as SR (DICOM SR ext.)
|
||||
- Structured Reports as HTML (DICOM html ext.)
|
||||
- Encapsulated PDFs as PDFs (DICOM pdf ext.)
|
||||
- Whole Slide Microscopy Viewing (whole slide ext.)
|
||||
- etc.
|
||||
|
||||
The general pattern is that a mode can define which `Viewport` to use for which
|
||||
specific `SOPClassHandlerUID`, so if you want to fork just a single Viewport
|
||||
@@ -43,44 +40,53 @@ const getViewportModule = () => {
|
||||
|
||||
## Example Viewport Component
|
||||
|
||||
A simplified version of the tracked CornerstoneViewport is shown below, which
|
||||
creates a cornerstone viewport and action bar on top of it.
|
||||
A simplified version of the tracked `OHIFCornerstoneViewport` is shown below, which
|
||||
creates a cornerstone viewport:
|
||||
|
||||
:::note Tip
|
||||
|
||||
Not in OHIF version 3.1 we use `displaySets` in the props which is new compared to
|
||||
the previous version (3.0) which uses `displaySet`. This is due to the fact that
|
||||
we are moving to a new data model that can render fused images in a single viewport.
|
||||
|
||||
:::
|
||||
|
||||
```jsx
|
||||
function TrackedCornerstoneViewport({
|
||||
children,
|
||||
dataSource,
|
||||
displaySet,
|
||||
displaySets,
|
||||
viewportIndex,
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
commandsManager,
|
||||
}) {
|
||||
const renderViewport = () => {
|
||||
const { component: Component } = extensionManager.getModuleEntry(
|
||||
'@ohif/extension-cornerstone.viewportModule.cornerstone'
|
||||
);
|
||||
return (
|
||||
<Component
|
||||
onElementEnabled={onElementEnabled}
|
||||
element={element}
|
||||
{...props}
|
||||
></Component>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewportActionBar onDoubleClick onSeriesChange />
|
||||
<div className="relative flex flex-row w-full h-full overflow-hidden">
|
||||
{renderViewport()}
|
||||
</div>
|
||||
</>
|
||||
<div className="viewport-wrapper">
|
||||
/** Resize Detector */
|
||||
<ReactResizeDetector
|
||||
handleWidth
|
||||
handleHeight
|
||||
skipOnMount={true} // Todo: make these configurable
|
||||
refreshMode={'debounce'}
|
||||
refreshRate={100}
|
||||
onResize={onResize}
|
||||
targetRef={elementRef.current}
|
||||
/>
|
||||
/** Div For displaying image */
|
||||
<div
|
||||
className="cornerstone-viewport-element"
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
onContextMenu={e => e.preventDefault()}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
ref={elementRef}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
### `@ohif/viewer`
|
||||
|
||||
|
||||
@@ -2,24 +2,30 @@
|
||||
sidebar_position: 3
|
||||
sidebar_label: Service Manager
|
||||
---
|
||||
|
||||
# Services Manager
|
||||
|
||||
## Overview
|
||||
Services manager is the single point of service registration. Each service needs to implement a `create` method which gets called inside `ServicesManager` to instantiate the service. In the app, you can get access to a registered service via the `services` property of the `ServicesManager`.
|
||||
|
||||
Services manager is the single point of service registration. Each service needs
|
||||
to implement a `create` method which gets called inside `ServicesManager` to
|
||||
instantiate the service. In the app, you can get access to a registered service
|
||||
via the `services` property of the `ServicesManager`.
|
||||
|
||||
## Skeleton
|
||||
*Simplified* skeleton of `ServicesManager` is shown below. There are two public methods:
|
||||
|
||||
_Simplified_ skeleton of `ServicesManager` is shown below. There are two public
|
||||
methods:
|
||||
|
||||
- `registerService`: registering a new service with/without a configuration
|
||||
- `registerServices`: registering batch of services
|
||||
|
||||
|
||||
```js
|
||||
export default class ServicesManager {
|
||||
constructor(commandsManager) {
|
||||
this._commandsManager = commandsManager
|
||||
this.services = {}
|
||||
this.registeredServiceNames = []
|
||||
this._commandsManager = commandsManager;
|
||||
this.services = {};
|
||||
this.registeredServiceNames = [];
|
||||
}
|
||||
|
||||
registerService(service, configuration = {}) {
|
||||
@@ -27,10 +33,10 @@ export default class ServicesManager {
|
||||
this.services[service.name] = service.create({
|
||||
configuration,
|
||||
commandsManager: this._commandsManager,
|
||||
})
|
||||
});
|
||||
|
||||
/* Track service registration */
|
||||
this.registeredServiceNames.push(service.name)
|
||||
this.registeredServiceNames.push(service.name);
|
||||
}
|
||||
|
||||
registerServices(services) {
|
||||
@@ -39,32 +45,32 @@ export default class ServicesManager {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Default Registered Services
|
||||
|
||||
By default, `OHIF-v3` registers the following services in the `appInit`.
|
||||
|
||||
```js title="platform/viewer/src/appInit.js"
|
||||
servicesManager.registerServices([
|
||||
UINotificationService,
|
||||
UIModalService,
|
||||
UIDialogService,
|
||||
UIViewportDialogService,
|
||||
MeasurementService,
|
||||
DisplaySetService,
|
||||
ToolBarService,
|
||||
ViewportGridService,
|
||||
HangingProtocolService,
|
||||
CineService,
|
||||
]);
|
||||
UINotificationService,
|
||||
UIModalService,
|
||||
UIDialogService,
|
||||
UIViewportDialogService,
|
||||
MeasurementService,
|
||||
DisplaySetService,
|
||||
ToolBarService,
|
||||
ViewportGridService,
|
||||
HangingProtocolService,
|
||||
CineService,
|
||||
]);
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Service Architecture
|
||||
If you take a look at the folder of each service implementation above, you will
|
||||
find out that services need to be exported as an object with `name` and `create` method.
|
||||
|
||||
For instance, `ToolbarService` is exported as:
|
||||
If you take a look at the folder of each service implementation above, you will
|
||||
find out that services need to be exported as an object with `name` and `create`
|
||||
method.
|
||||
|
||||
For instance, `ToolBarService` is exported as:
|
||||
|
||||
```js title="platform/core/src/services/ToolBarService/index.js"
|
||||
import ToolBarService from './ToolBarService';
|
||||
@@ -77,43 +83,45 @@ export default {
|
||||
};
|
||||
```
|
||||
|
||||
and the implementation of `ToolbarService` lies in the same folder at `./ToolbarSerivce.js`.
|
||||
and the implementation of `ToolBarService` lies in the same folder at
|
||||
`./ToolbarSerivce.js`.
|
||||
|
||||
> Note, the create method is critical for any custom service that you write and
|
||||
> want to add to the list of services
|
||||
|
||||
|
||||
|
||||
## Accessing Services
|
||||
Throughout the app you can use `services` property of the service manager to access
|
||||
the desired service.
|
||||
|
||||
For instance in the `PanelMeasurementTableTracking` which is the right panel in the
|
||||
`longitudinal` mode, we have the *simplified code below* for downloading the drawn measurements.
|
||||
Throughout the app you can use `services` property of the service manager to
|
||||
access the desired service.
|
||||
|
||||
For instance in the `PanelMeasurementTableTracking` which is the right panel in
|
||||
the `longitudinal` mode, we have the _simplified code below_ for downloading the
|
||||
drawn measurements.
|
||||
|
||||
```js
|
||||
function PanelMeasurementTableTracking({ servicesManager }) {
|
||||
const { MeasurementService } = servicesManager.services
|
||||
const { MeasurementService } = servicesManager.services;
|
||||
/** ... **/
|
||||
|
||||
async function exportReport() {
|
||||
const measurements = MeasurementService.getMeasurements()
|
||||
const measurements = MeasurementService.getMeasurements();
|
||||
/** ... **/
|
||||
downloadCSVReport(measurements, MeasurementService)
|
||||
downloadCSVReport(measurements, MeasurementService);
|
||||
}
|
||||
|
||||
/** ... **/
|
||||
return <> /** ... **/ </>
|
||||
return <> /** ... **/ </>;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Registering Custom Services
|
||||
You might need to write you own custom service in an extension. `preRegistration` hook inside your extension is the place for registering your custom service.
|
||||
|
||||
You might need to write you own custom service in an extension.
|
||||
`preRegistration` hook inside your extension is the place for registering your
|
||||
custom service.
|
||||
|
||||
```js title="extensions/customExtension/src/index.js"
|
||||
import WrappedBackEndService from './services/backEndService'
|
||||
import WrappedBackEndService from './services/backEndService';
|
||||
|
||||
export default {
|
||||
// ID of the extension
|
||||
@@ -121,12 +129,11 @@ export default {
|
||||
preRegistration({ servicesManager }) {
|
||||
servicesManager.registerService(WrappedBackEndService(servicesManager));
|
||||
},
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
and the logic for your service shall be
|
||||
|
||||
|
||||
```js title="extensions/customExtension/src/services/backEndService/index.js"
|
||||
import backEndService from './backEndService';
|
||||
|
||||
|
||||
@@ -22,6 +22,15 @@ For instance, in `longitudinal` mode we are using this hook to initialize the
|
||||
`ToolBarService` and set the window level/width tool to be active and add
|
||||
buttons to the toolbar.
|
||||
|
||||
:::note Tip
|
||||
|
||||
In OHIF Version 3.1, there is a new service `ToolGroupService` that is used to
|
||||
define and manage tools for the group of viewports. This is a new concept
|
||||
borrowed from the Cornerstone ToolGroup, and you can read more
|
||||
[here](https://www.cornerstonejs.org/docs/concepts/cornerstone-tools/toolgroups/)
|
||||
|
||||
:::
|
||||
|
||||
```js
|
||||
function modeFactory() {
|
||||
return {
|
||||
@@ -29,16 +38,10 @@ function modeFactory() {
|
||||
version: '',
|
||||
displayName: '',
|
||||
onModeEnter: ({ servicesManager, extensionManager }) => {
|
||||
const { ToolBarService } = servicesManager.services;
|
||||
const { ToolBarService, ToolGroupService } = servicesManager.services;
|
||||
|
||||
const interaction = {
|
||||
groupId: 'primary',
|
||||
itemId: 'Wwwc',
|
||||
interactionType: 'tool',
|
||||
commandOptions: undefined,
|
||||
};
|
||||
|
||||
ToolBarService.recordInteraction(interaction);
|
||||
// Init Default and SR ToolGroups
|
||||
initToolGroups(extensionManager, ToolGroupService);
|
||||
|
||||
ToolBarService.init(extensionManager);
|
||||
ToolBarService.addButtons(toolbarButtons);
|
||||
@@ -65,7 +68,7 @@ This hook is called when the viewer navigate away from the route in the url.
|
||||
This is the place for cleaning up data, and services by unsubscribing to the
|
||||
events.
|
||||
|
||||
For instance, it can be used to reset the `ToolbarService` which reset the
|
||||
For instance, it can be used to reset the `ToolBarService` which reset the
|
||||
toggled buttons.
|
||||
|
||||
```js
|
||||
|
||||
@@ -58,9 +58,9 @@ function modeFactory() {
|
||||
],
|
||||
},
|
||||
{
|
||||
namespace: '@ohif/extension-dicom-sr.viewportModule.dicom-sr',
|
||||
namespace: '@ohif/extension-cornerstone-dicom-sr.viewportModule.dicom-sr',
|
||||
displaySetsToDisplay: [
|
||||
'@ohif/extension-dicom-sr.sopClassHandlerModule.dicom-sr',
|
||||
'@ohif/extension-cornerstone-dicom-sr.sopClassHandlerModule.dicom-sr',
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -204,9 +204,6 @@ init: async ({
|
||||
const instance = displaySet.images.find(
|
||||
image => image.SOPInstanceUID === tool.SOPInstanceUID
|
||||
);
|
||||
|
||||
const { SOPInstanceUID, url } = instance;
|
||||
displaySet.initialImageIdIndex = displaySet.images.indexOf(instance);
|
||||
});
|
||||
|
||||
MeasurementService.addMeasurement(/**...**/);
|
||||
|
||||
@@ -2,205 +2,54 @@
|
||||
sidebar_position: 4
|
||||
sidebar_label: Hanging Protocol Service
|
||||
---
|
||||
|
||||
# Hanging Protocol Service
|
||||
|
||||
## Overview
|
||||
`HangingProtocolService` is a migration of the `OHIF-v1` hanging protocol engine.
|
||||
This service handles the arrangement of the images in the viewport.
|
||||
In short, the registered protocols will get matched with the Series that are available
|
||||
for the series. Each protocol gets a point, and they are ranked. The winning protocol gets
|
||||
applied and its settings run for the viewports.
|
||||
|
||||
You can read more about hanging protocols [here](http://dicom.nema.org/dicom/Conf-2005/Day-2_Selected_Papers/B305_Morgan_HangProto_v1.pdf). In short
|
||||
with `OHIF-v3` hanging protocols you can:
|
||||
`HangingProtocolService` is a migration of the `OHIF-v1` hanging protocol
|
||||
engine. This service handles the arrangement of the images in the viewport. In
|
||||
short, the registered protocols will get matched with the Series that are
|
||||
available for the series. Each protocol gets a point, and they are ranked. The
|
||||
winning protocol gets applied and its settings run for the viewports.
|
||||
|
||||
You can read more about hanging protocols
|
||||
[here](http://dicom.nema.org/dicom/Conf-2005/Day-2_Selected_Papers/B305_Morgan_HangProto_v1.pdf).
|
||||
In short with `OHIF-v3` hanging protocols you can:
|
||||
|
||||
- Define what layout of the viewport should the viewer starts with (2x2 layout)
|
||||
- Define which series gets displayed in which position of the layout
|
||||
- Apply certain initial viewport settings; e.g., inverting the contrast
|
||||
- Enable certain tools based on what series are displayed: link prostate T2 and ADC MRI.
|
||||
|
||||
|
||||
|
||||
|
||||
## Skeleton of A Hanging Protocol
|
||||
You can find the skeleton of the hanging protocols here:
|
||||
|
||||
```js
|
||||
const deafultProtocol = {
|
||||
id: 'defaultProtocol',
|
||||
locked: true,
|
||||
hasUpdatedPriorsInformation: false,
|
||||
name: 'Default',
|
||||
createdDate: '2021-02-23T19:22:08.894Z',
|
||||
modifiedDate: '2021-02-23T19:22:08.894Z',
|
||||
availableTo: {},
|
||||
editableBy: {},
|
||||
protocolMatchingRules: [],
|
||||
stages: [
|
||||
{
|
||||
id: 'nwzau7jDkEkL8djfr',
|
||||
name: 'oneByOne',
|
||||
viewportStructure: {
|
||||
type: 'grid',
|
||||
properties: {
|
||||
rows: 1,
|
||||
columns: 1,
|
||||
},
|
||||
},
|
||||
viewports: [
|
||||
{
|
||||
viewportSettings: [],
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
],
|
||||
createdDate: '2021-02-23T19:22:08.894Z',
|
||||
},
|
||||
],
|
||||
numberOfPriorsReferenced: -1,
|
||||
};
|
||||
```
|
||||
|
||||
Let's discuss each property in depth.
|
||||
|
||||
|
||||
|
||||
|
||||
- `id`: unique identifier for the protocol
|
||||
|
||||
- `protocolMatchingRules`: A list of criteria for the protocol along with the provided points for ranking.
|
||||
|
||||
- `weight`: weight for the matching rule. Eventually, all the registered protocols get sorted based on the weights, and the winning
|
||||
protocol gets applied to the viewer.
|
||||
- `attriubte`: tag that needs to be matched against. This can be either Study-level metadata or a custom attribute. [Learn more about custom attribute matching](#custom-attribute)
|
||||
|
||||
- `constraint`: the constraint that needs to be satisfied for the attribute. It accepts a `validator` which can be
|
||||
[`equals`, `doesNotEqual`, `contains`, `doesNotContain`, `startsWith`, `endsWidth`]
|
||||
|
||||
A sample of the matching rule is:
|
||||
|
||||
```js
|
||||
{
|
||||
id: 'wauZK2QNEfDPwcAQo',
|
||||
weight: 1,
|
||||
attribute: 'StudyInstanceUID',
|
||||
constraint: {
|
||||
equals: {
|
||||
value: '1.3.6.1.4.1.25403.345050719074.3824.20170125112931.11',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
}
|
||||
```
|
||||
|
||||
- `stages`: Each protocol can define one or more stages. Each stage defines a certain layout and viewport rules.
|
||||
Therefore, the `stages` property is array of objects, each object being one stage.
|
||||
|
||||
- `viewportStructure`: Defines the layout of the viewer. You can define the number of `rows` and `columns`.
|
||||
There should be `rows * columns` number of viewport configuration in the `viewports` property. Note that order of viewports are rows first then columns.
|
||||
|
||||
- `viewportSettings`: custom settings to be applied to the viewport. This can be a `voi` being applied
|
||||
to the viewer or a tool to get enabled. We will discuss viewport-specific settings [below](#viewport-settings)
|
||||
|
||||
- `imageMatchingRules (comming soon)`: setting the image slice for the viewport.
|
||||
|
||||
- `seriesMatchingRules`: the most important rule that matches series in the viewport. For instance, the following stage
|
||||
configuration will create a one-by-two layout and put the series whose description contains `t2` on the left, and a series with
|
||||
description that contains `adc` on the right. (order of viewports are rows, first then columns)
|
||||
|
||||
```js
|
||||
stages: [
|
||||
{
|
||||
id: 'hYbmMy3b7pz7GLiaT',
|
||||
name: 'oneByThree',
|
||||
viewportStructure: {
|
||||
type: 'grid',
|
||||
properties: {
|
||||
rows: 1,
|
||||
columns: 2,
|
||||
},
|
||||
},
|
||||
viewports: [
|
||||
// viewport 1
|
||||
{
|
||||
viewportSettings: [],
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
id: 'vSjk7NCYjtdS3XZAw',
|
||||
weight: 1,
|
||||
attribute: 'SeriesDescription',
|
||||
constraint: {
|
||||
contains: {
|
||||
value: 't2',
|
||||
},
|
||||
},
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
// viewport 2
|
||||
{
|
||||
viewportSettings: [],
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
id: 'vSjk7NCYjtdS3XZAw',
|
||||
weight: 1,
|
||||
attribute: 'SeriesDescription',
|
||||
constraint: {
|
||||
contains: {
|
||||
value: 'ADC',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- Enable certain tools based on what series are displayed: link prostate T2 and
|
||||
ADC MRI.
|
||||
|
||||
## Events
|
||||
There are two events that get publish in `HangingProtocolService`:
|
||||
|
||||
There are two events that get publish in `HangingProtocolService`:
|
||||
|
||||
| Event | Description |
|
||||
| ------------ | -------------------------------------------------------------------- |
|
||||
| NEW_LAYOUT | Fires when a new layout is requested by the `HangingProtocolService` |
|
||||
| STAGE_CHANGE | Fires when the the stage is changed in the hanging protocols |
|
||||
|
||||
|
||||
## API
|
||||
|
||||
- `getState`: returns an array: `[matchDetails, hpAlreadyApplied]`:
|
||||
|
||||
- `matchDetails`: matching details for the series
|
||||
- `hpAlreadyApplied`: An array which tracks whether HPServices has been applied on each viewport.
|
||||
- `hpAlreadyApplied`: An array which tracks whether HPServices has been
|
||||
applied on each viewport.
|
||||
|
||||
- `addProtocols`: adds provided protocols to the list of registered protocols for matching
|
||||
- `addProtocols`: adds provided protocols to the list of registered protocols
|
||||
for matching
|
||||
|
||||
- `run(studyMetaData, protocol)`: runs the HPService with the provided studyMetaData and optional protocol.
|
||||
If protocol is not given, HP Matching engine will search all the registered protocols for the best matching one based on the constraints.
|
||||
- `run(studyMetaData, protocol)`: runs the HPService with the provided
|
||||
studyMetaData and optional protocol. If protocol is not given, HP Matching
|
||||
engine will search all the registered protocols for the best matching one
|
||||
based on the constraints.
|
||||
|
||||
- `addCustomAttribute`: adding a custom attribute for matching. (see below)
|
||||
|
||||
- `addCustomViewportSetting`: adding a custom setting to a viewport (initial `voi`). Below, we explain
|
||||
in detail how to add custom viewport settings via protocol definitions. `addCustomViewportSetting` is another way to set
|
||||
these settings which is exposed by API
|
||||
|
||||
-
|
||||
|
||||
Default initialization of the modes handles running the `HangingProtocolService`
|
||||
|
||||
## Custom Attribute
|
||||
@@ -208,9 +57,13 @@ In some situations, you might want to match based on a custom attribute and not
|
||||
if you have assigned a `timepointId` to each study, and you want to match based on it.
|
||||
Good news is that, in `OHIF-v3` you can define you custom attribute and use it for matching.
|
||||
|
||||
There are various ways that you can let `HangingProtocolService` know of you custom attribute.
|
||||
We will show how to add it inside the mode configuration.
|
||||
In some situations, you might want to match based on a custom attribute and not
|
||||
the DICOM tags. For instance, if you have assigned a `timepointId` to each study
|
||||
and you want to match based on it. Good news is that, in `OHIF-v3` you can
|
||||
define you custom attribute and use it for matching.
|
||||
|
||||
There are various ways that you can let `HangingProtocolService` know of you
|
||||
custom attribute. We will show how to add it inside the mode configuration.
|
||||
|
||||
```js
|
||||
const deafultProtocol = {
|
||||
@@ -233,13 +86,13 @@ const deafultProtocol = {
|
||||
/** ... **/
|
||||
],
|
||||
numberOfPriorsReferenced: -1,
|
||||
}
|
||||
};
|
||||
|
||||
// Custom function for custom attribute
|
||||
const getTimePointUID = (metaData) => {
|
||||
const getTimePointUID = metaData => {
|
||||
// requesting the timePoint Id
|
||||
return myBackEndAPI(metaData)
|
||||
}
|
||||
return myBackEndAPI(metaData);
|
||||
};
|
||||
|
||||
function modeFactory() {
|
||||
return {
|
||||
@@ -249,49 +102,51 @@ function modeFactory() {
|
||||
{
|
||||
path: 'myModeRoute',
|
||||
init: async ({}) => {
|
||||
const { DicomMetadataStore, HangingProtocolService } =
|
||||
servicesManager.services
|
||||
const {
|
||||
DicomMetadataStore,
|
||||
HangingProtocolService,
|
||||
} = servicesManager.services;
|
||||
|
||||
const onSeriesAdded = ({
|
||||
StudyInstanceUID,
|
||||
madeInClient = false,
|
||||
}) => {
|
||||
const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID)
|
||||
const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID);
|
||||
|
||||
// Adding custom attribute to the hangingprotocol
|
||||
HangingProtocolService.addCustomAttribute(
|
||||
'timepoint',
|
||||
'timepoint',
|
||||
(metaData) => getFirstMeasurementSeriesInstanceUID(metaData)
|
||||
)
|
||||
metaData => getFirstMeasurementSeriesInstanceUID(metaData)
|
||||
);
|
||||
|
||||
HangingProtocolService.run(studyMetadata)
|
||||
}
|
||||
HangingProtocolService.run(studyMetadata);
|
||||
};
|
||||
|
||||
DicomMetadataStore.subscribe(
|
||||
DicomMetadataStore.EVENTS.SERIES_ADDED,
|
||||
onSeriesAdded
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
/** ... **/
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Viewport Settings
|
||||
You can define custom settings to be applied to each viewport. There
|
||||
are two types of settings:
|
||||
|
||||
You can define custom settings to be applied to each viewport. There are two
|
||||
types of settings:
|
||||
|
||||
- `viewport settings`: Currently we support two viewport settings
|
||||
|
||||
- `voi`: applying an initial `voi` by setting the windowWidth and windowCenter
|
||||
- `inverted`: inverting the viewport color (e.g., for PET images)
|
||||
|
||||
- `props settings`: Running commands after the first render; e.g., enabling the link tool
|
||||
|
||||
- `props settings`: Running commands after the first render; e.g., enabling the
|
||||
link tool
|
||||
|
||||
Examples of each settings are :
|
||||
|
||||
@@ -307,7 +162,7 @@ viewportSettings: [
|
||||
commandName: 'setToolActive',
|
||||
type: 'props',
|
||||
},
|
||||
]
|
||||
];
|
||||
```
|
||||
|
||||
and
|
||||
@@ -324,5 +179,5 @@ viewportSettings: [
|
||||
commandName: '',
|
||||
type: 'viewport',
|
||||
},
|
||||
]
|
||||
];
|
||||
```
|
||||
@@ -7,7 +7,7 @@ sidebar_label: Toolbar Service
|
||||
|
||||
## Overview
|
||||
|
||||
`ToolbarService` handles the toolbar section buttons, and what happens when a
|
||||
`ToolBarService` handles the toolbar section buttons, and what happens when a
|
||||
button is clicked by the user.
|
||||
|
||||
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
|
||||
@@ -19,12 +19,12 @@ button is clicked by the user.
|
||||
| Event | Description |
|
||||
| ----------------------- | ---------------------------------------------------------------------- |
|
||||
| TOOL_BAR_MODIFIED | Fires when a button is added/removed to the toolbar |
|
||||
| TOOL_BAR_STATE_MODIFIED | Fires when an interaction happens and ToolbarService state is modified |
|
||||
| TOOL_BAR_STATE_MODIFIED | Fires when an interaction happens and ToolBarService state is modified |
|
||||
|
||||
## API
|
||||
|
||||
- `recordInteraction(interaction)`: executes the provided interaction which is
|
||||
an object providing the following properties to the ToolbarService:
|
||||
an object providing the following properties to the ToolBarService:
|
||||
|
||||
- `interactionType`: can be `tool`, `toggle` and `action`. We will discuss
|
||||
more each type below.
|
||||
@@ -47,7 +47,7 @@ button is clicked by the user.
|
||||
|
||||
## State
|
||||
|
||||
ToolbarService has an internal state that gets updated per tool interaction and
|
||||
ToolBarService has an internal state that gets updated per tool interaction and
|
||||
tracks the active toolId, state of the buttons that have toggled state, and the
|
||||
group buttons and which tool in each group is active.
|
||||
|
||||
@@ -70,10 +70,10 @@ interaction object.
|
||||
|
||||
- `tool`: setting a tool to be active; e.g., measurement tools
|
||||
- `toggle`: toggling state of a tool; e.g., viewport link (sync)
|
||||
- `action`: performs a registered action outside of the ToolbarService; e.g.,
|
||||
- `action`: performs a registered action outside of the ToolBarService; e.g.,
|
||||
capture
|
||||
|
||||
A _simplified_ implementation of the ToolbarService is:
|
||||
A _simplified_ implementation of the ToolBarService is:
|
||||
|
||||
```js
|
||||
export default class ToolBarService {
|
||||
@@ -115,25 +115,32 @@ The simplest toolbarButtons definition has the following properties:
|
||||
|
||||
```js
|
||||
{
|
||||
id: 'Zoom',
|
||||
type: 'ohif.radioGroup',
|
||||
props: {
|
||||
type: 'tool',
|
||||
icon: 'tool-zoom',
|
||||
label: 'Zoom',
|
||||
commandOptions: { toolName: 'Zoom' },
|
||||
},
|
||||
},
|
||||
"id": "Zoom",
|
||||
"type": "ohif.radioGroup",
|
||||
"props": {
|
||||
"type": "tool",
|
||||
"icon": "tool-zoom",
|
||||
"label": "Zoom",
|
||||
"commands": [
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "Zoom"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| property | description | values |
|
||||
| ---------------- | ----------------------------------------------------------------- | ------------------------------------------- |
|
||||
| `id` | Unique string identifier for the definition | \* |
|
||||
| `label` | User/display friendly to show in UI | \* |
|
||||
| `icon` | A string name for an icon supported by the consuming application. | \* |
|
||||
| `type` | Used to determine the button's behaviour | "tool", "toggle", "action" |
|
||||
| `commandName` | (optional) The command to run when the button is used. | Any command registered by a `CommandModule` |
|
||||
| `commandOptions` | (optional) Options to pass the target `commandName` | \* |
|
||||
| `icon` | A string name for an icon supported by the consuming application. | \* |
|
||||
| `label` | User/display friendly to show in UI | \* |
|
||||
| `commands` | (optional) The commands to run when the button is used. It include a commandName, commandOptions, and/or a context | Any command registered by a `CommandModule` |
|
||||
|
||||
There are three main types of toolbar buttons:
|
||||
|
||||
@@ -159,58 +166,91 @@ to create `MeasurementTools` nested button
|
||||
|
||||
```js title="modes/longitudinal/src/toolbarButtons.js"
|
||||
{
|
||||
id: 'MeasurementTools',
|
||||
type: 'ohif.splitButton',
|
||||
props: {
|
||||
groupId: 'MeasurementTools',
|
||||
isRadio: true,
|
||||
primary: {
|
||||
id: 'Length',
|
||||
icon: 'tool-length',
|
||||
label: 'Length',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'Length',
|
||||
}
|
||||
"id": "MeasurementTools",
|
||||
"type": "ohif.splitButton",
|
||||
"props": {
|
||||
"groupId": "MeasurementTools",
|
||||
"isRadio": true,
|
||||
"primary": {
|
||||
"id": "Length",
|
||||
"icon": "tool-length",
|
||||
"label": "Length",
|
||||
"type": "tool",
|
||||
"commands": [
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "Length"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
},
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "SRLength",
|
||||
"toolGroupId": "SRToolGroup"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
}
|
||||
],
|
||||
"tooltip": "Length"
|
||||
},
|
||||
secondary: {
|
||||
icon: 'chevron-down',
|
||||
label: '',
|
||||
isActive: true,
|
||||
tooltip: 'More Measure Tools',
|
||||
"secondary": {
|
||||
"icon": "chevron-down",
|
||||
"label": "",
|
||||
"isActive": true,
|
||||
"tooltip": "More Measure Tools"
|
||||
},
|
||||
items: [
|
||||
// Length tool
|
||||
"items": [
|
||||
{
|
||||
id: 'Length',
|
||||
icon: 'tool-length',
|
||||
label: 'Length',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'Length',
|
||||
}
|
||||
"id": "Bidirectional",
|
||||
"icon": "tool-bidirectional",
|
||||
"label": "Bidirectional",
|
||||
"type": "tool",
|
||||
"commands": [
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "Bidirectional"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
},
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "SRBidirectional",
|
||||
"toolGroupId": "SRToolGroup"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
}
|
||||
],
|
||||
"tooltip": "Bidirectional Tool"
|
||||
},
|
||||
// Bidirectional tool
|
||||
{
|
||||
id: 'Bidirectional',
|
||||
icon: 'tool-bidirectional',
|
||||
label: 'Length',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'Bidirectional',
|
||||
}
|
||||
"id": "ArrowAnnotate",
|
||||
"icon": "tool-annotate",
|
||||
"label": "Annotation",
|
||||
"type": "tool",
|
||||
"commands": [
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "ArrowAnnotate"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
},
|
||||
{
|
||||
"commandName": "setToolActive",
|
||||
"commandOptions": {
|
||||
"toolName": "SRArrowAnnotate",
|
||||
"toolGroupId": "SRToolGroup"
|
||||
},
|
||||
"context": "CORNERSTONE"
|
||||
}
|
||||
],
|
||||
"tooltip": "Arrow Annotate"
|
||||
},
|
||||
// Ellipse tool
|
||||
{
|
||||
id: 'EllipticalRoi',
|
||||
icon: 'tool-elipse',
|
||||
label: 'Ellipse',
|
||||
type: 'tool',
|
||||
commandOptions: {
|
||||
toolName: 'EllipticalRoi',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -17,7 +17,7 @@ We maintain the following non-ui Services:
|
||||
- [DicomMetadata Store](./../data/DicomMetadataStore.md)
|
||||
- [DisplaySet Service](./../data/DisplaySetService.md)
|
||||
- [Hanging Protocol Service](../data/HangingProtocolService.md)
|
||||
- [Toolbar Service](../data/ToolbarService.md)
|
||||
- [Toolbar Service](../data/ToolBarService.md)
|
||||
- [Measurement Service](../data/MeasurementService.md)
|
||||
|
||||
## Service Architecture
|
||||
|
||||
@@ -83,7 +83,7 @@ The following services is available in the `OHIF-v3`.
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="./data/ToolbarService">
|
||||
<a href="./data/ToolBarService">
|
||||
ToolBarService (NEW)
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@@ -2,27 +2,25 @@
|
||||
sidebar_position: 6
|
||||
sidebar_label: Viewport Grid Service
|
||||
---
|
||||
|
||||
# Viewport Grid Service
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
This is a new UI service, that handles the grid layout of the viewer.
|
||||
|
||||
|
||||
|
||||
|
||||
## Interface
|
||||
|
||||
For a more detailed look on the options and return values each of these methods
|
||||
is expected to support, [check out it's interface in `@ohif/core`][interface]
|
||||
|
||||
| API Member | Description |
|
||||
| -------------------------------------------------------------------- | --------------------------------------------------- |
|
||||
| `setActiveViewportIndex(index)` | Sets the active viewport index in the app |
|
||||
| `getState()` | Gets the states of the viewport (see below) |
|
||||
| `setDisplaysetForViewport({ viewportIndex, displaySetInstanceUID })` | Sets displaySet for viewport based on displaySet Id |
|
||||
| `setLayout({numCols, numRows})` | Sets rows and columns |
|
||||
| `reset()` | Resets the default states |
|
||||
| API Member | Description |
|
||||
| --------------------------------------------------------------------- | --------------------------------------------------- |
|
||||
| `setActiveViewportIndex(index)` | Sets the active viewport index in the app |
|
||||
| `getState()` | Gets the states of the viewport (see below) |
|
||||
| `setDisplaySetsForViewport({ viewportIndex, displaySetInstanceUID })` | Sets displaySet for viewport based on displaySet Id |
|
||||
| `setLayout({numCols, numRows})` | Sets rows and columns |
|
||||
| `reset()` | Resets the default states |
|
||||
|
||||
## Implementations
|
||||
|
||||
@@ -32,7 +30,6 @@ is expected to support, [check out it's interface in `@ohif/core`][interface]
|
||||
|
||||
`*` - Denotes maintained by OHIF
|
||||
|
||||
|
||||
## State
|
||||
|
||||
```js
|
||||
@@ -49,5 +46,5 @@ const DEFAULT_STATE = {
|
||||
*/
|
||||
],
|
||||
activeViewportIndex: 0,
|
||||
}
|
||||
};
|
||||
```
|
||||
@@ -25,11 +25,12 @@ of our medical image web viewers from the start. The summary of changes includes
|
||||
- Our vision is that technical people focus of developing extensions which
|
||||
provides core functionalities, and experts to build modes by picking the
|
||||
appropriate functionalities from each extension.
|
||||
- Migrated all the `cornerstone-core` and `cornerstone-tools` usage to the newly released `Cornerstone3D`.
|
||||
|
||||
* UI has been completely redesigned with modularity and workflow modes in mind.
|
||||
* New UI components have been built with Tailwind CSS
|
||||
* Redux store has been removed from the viewer in favour of services backed by
|
||||
React's Context API
|
||||
React's Context **API**
|
||||
|
||||
Below, you can find the gap analysis between the `OHIF-v2` and `OHIF-v3`:
|
||||
|
||||
@@ -86,6 +87,10 @@ Below, you can find the gap analysis between the `OHIF-v2` and `OHIF-v3`:
|
||||
<td align="left">White-labelling: Easily replace the OHIF Logo with your logo</td>
|
||||
<td align="center">✅</td>
|
||||
<td align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">DICOM PDF support</td>
|
||||
<td align="center">✅</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">DICOM Whole-slide imaging viewport</td>
|
||||
@@ -97,11 +102,6 @@ Below, you can find the gap analysis between the `OHIF-v2` and `OHIF-v3`:
|
||||
<td align="center">🔜</td>
|
||||
<td align="center">Not Started</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">DICOM PDF support</td>
|
||||
<td align="center">🔜</td>
|
||||
<td align="center">Not Started</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">Displaying non-renderable DICOM as HTML</td>
|
||||
<td align="center">🔜</td>
|
||||
@@ -129,8 +129,8 @@ Below, you can find the gap analysis between the `OHIF-v2` and `OHIF-v3`:
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">VTK Extension + MIP / MPR layout</td>
|
||||
<td align="center">❌</td>
|
||||
<td align="center">Other plans that involves amazing news soon!</td>
|
||||
<td align="center">🔜</td>
|
||||
<td align="center">3D rendering and 3D annotation tools via Cornerstone3D</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">UMD Build (Embedded Viewer). </td>
|
||||
|
||||
@@ -11,6 +11,15 @@ and other resources that we have provided to the community in the past:
|
||||
|
||||
## 2022
|
||||
|
||||
### The Imaging Network Ontario - Remote
|
||||
|
||||
The Imaging Network Ontario (ImNO) is an annual symposium that brings together
|
||||
medical imaging researchers and scientists from across Canada to share
|
||||
knowledge, ideas, and experiences.
|
||||
[[Slides]](https://docs.google.com/presentation/d/18XZDon4-Sitc2a70V5sFyhyUVZI_mIgfXHGtdxhZMjE/edit?usp=sharing)
|
||||
[[Video]](https://vimeo.com/691134870/ad7d308a44)
|
||||
|
||||
|
||||
### [NA-MIC Project Week 36th 2022 - Remote](https://github.com/NA-MIC/ProjectWeek/blob/master/PW36_2022_Virtual/README.md)
|
||||
|
||||
The Project Week is a week-long hackathon of hands-on activity in which medical
|
||||
|
||||
Reference in new issue
Block a user