feat: explicit layout apply via HangingProtocolService (#2936)

* feat: add more types to HP service

* change hp to have apply protocol

* feat: mpr initial work

* apply protocol by id

* feat: mode should be able to apply protocol directly

* fix: wrong inheritance for viewports drag and drop

* apply review comments
This commit is contained in:
Alireza authored and GitHub committed 2022-09-20 23:40:26 -04:00
1 parent fb9744a817
commit 5f480ff746
28 files changed
+705 -226

No files matched your search

@@ -28,7 +28,7 @@ const deafultProtocol = {
id: 'hYbmMy3b7pz7GLiaT',
name: 'default',
viewportStructure: {
type: 'grid',
layoutType: 'grid',
properties: {
rows: 1,
columns: 2,
@@ -64,8 +64,8 @@ const deafultProtocol = {
function getHangingProtocolModule() {
return [
{
name: hangingProtocolName,
protocols: [defaultProtocol],
id: 'default',
protocol: deafultProtocol,
},
];
}
+2 -2
View File
@@ -149,8 +149,8 @@ async function defaultRouteInit({
});
DicomMetadataStore.subscribe('seriesAdded', ({ StudyInstanceUID }) => {
const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID);
HangingProtocolService.run(studyMetadata);
const studyMetadata = // get study metadata and displaySets
HangingProtocolService.run({studies, displaySets, activeStudy});
});
return unsubscriptions;
@@ -8,20 +8,32 @@ sidebar_label: 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
engine along with various improvements and fixes.
This service handles the arrangement of the images in the viewport. In
short, the registered protocols will get matched with the DisplaySets that are
available. Each protocol gets a score, 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:
In `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.
- Specify the type of the viewport and its orientation
- Define which displaySets gets displayed in which viewport of the layout
- Apply certain initial viewport settings; e.g., inverting the contrast, jumping to a specific slice, etc.
- Add specific synchronization rules for the viewports
## Protocols
A protocol can be an object or a function that returns an object (protocol generator).
Each protocol can get added to the `HangingProtocolService` by using the `addProtocol` method given
an `id` and the protocol itself. As an example, the `default` protocol is an object, while
the 'mpr' protocol is a function that returns the protocol (the reason for this is that the
`mpr` protocol needs to be generated based on the active viewport).
All protocols are stored in the `HangingProtocolService` using their `id` as the key, and
the protocol itself as the value.
## Events
@@ -31,20 +43,21 @@ There are two events that get publish in `HangingProtocolService`:
| ------------ | -------------------------------------------------------------------- |
| 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 |
| PROTOCOL_CHANGED | Fires when the the protocol is changed in the hanging protocols |
## API
- `getState`: returns an array: `[matchDetails, hpAlreadyApplied]`:
- `getMatchDetails`: returns an object which contains the details of the
matching for the viewports, displaysets and whether the protocol is
applied to the viewport or not
- `matchDetails`: matching details for the series
- `hpAlreadyApplied`: An array which tracks whether HPServices has been
applied on each viewport.
- `addProtocols`: adds provided protocols to the list of registered protocols
- `addProtocol`: adds provided protocol 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
- `run({studies, activeStudy, displaySets }, protocolId)`: runs the HPService with the provided
studyMetaData and optional protocolId. If protocol is not given, HP Matching
engine will search all the registered protocols for the best matching one
based on the constraints.
@@ -52,6 +65,8 @@ There are two events that get publish in `HangingProtocolService`:
Default initialization of the modes handles running the `HangingProtocolService`
## Custom Attribute
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.
@@ -129,50 +144,3 @@ function modeFactory() {
};
}
```
## Viewport 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
Examples of each settings are :
```js
viewportSettings: [
{
options: {
itemId: 'SyncScroll',
interactionType: 'toggle',
commandName: 'toggleSynchronizer',
commandOptions: { toggledState: true },
},
commandName: 'setToolActive',
type: 'props',
},
];
```
and
```js
viewportSettings: [
{
options: {
voi: {
windowWidth: 400,
windowCenter: 40,
},
},
commandName: '',
type: 'viewport',
},
];
```
@@ -58,8 +58,7 @@ async function defaultRouteInit({
const { unsubscribe: seriesAddedUnsubscribe } = DicomMetadataStore.subscribe(
DicomMetadataStore.EVENTS.SERIES_ADDED,
({ StudyInstanceUID }) => {
const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID);
HangingProtocolService.run(studyMetadata);
HangingProtocolService.run({studies, displaySets, activeStudy});
}
);
unsubscriptions.push(seriesAddedUnsubscribe);
@@ -89,11 +88,6 @@ export default function ModeRoute(/**..**/) {
extensionManager.onModeEnter();
mode?.onModeEnter({ servicesManager, extensionManager });
hangingProtocols.forEach(extentionProtocols => {
const { protocols } = extensionManager.getModuleEntry(extentionProtocols);
HangingProtocolService.addProtocols(protocols);
});
const setupRouteInit = async () => {
if (route.init) {
return await route.init(/**...**/);