OHIF-285/OHIF-274: Fix default hanging of display sets into the ViewportGrid (#1917)

* OHIF-285/OHIF-274: Fix default hanging of display sets into the ViewportGrid

Co-authored-by: James A. Petts <jamesapetts@gmail.com>
This commit is contained in:
Erik ZieglerandJames A. Petts authored and GitHub committed 2020-07-24 12:51:57 +02:00
1 parent 9df57e7f7f
commit 55cf9f2306
33 files changed
+548 -208

No files matched your search

+13 -9
View File
@@ -12,6 +12,7 @@ import {
ThemeWrapper,
ViewportDialogProvider,
ViewportGridProvider,
HangingProtocolProvider,
} from '@ohif/ui';
// Viewer Project
// TODO: Should this influence study list?
@@ -58,6 +59,7 @@ function App({ config, defaultExtensions }) {
UINotificationService,
UIViewportDialogService,
ViewportGridService, // TODO: Should this be a "UI" Service?
HangingProtocolService,
} = servicesManager.services;
return (
@@ -66,15 +68,17 @@ function App({ config, defaultExtensions }) {
<Router basename={routerBasename}>
<ThemeWrapper>
<ViewportGridProvider service={ViewportGridService}>
<ViewportDialogProvider service={UIViewportDialogService}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider modal={Modal} service={UIModalService}>
{appRoutes}
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</ViewportDialogProvider>
<HangingProtocolProvider service={HangingProtocolService}>
<ViewportDialogProvider service={UIViewportDialogService}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider modal={Modal} service={UIModalService}>
{appRoutes}
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</ViewportDialogProvider>
</HangingProtocolProvider>
</ViewportGridProvider>
</ThemeWrapper>
</Router>
+2
View File
@@ -11,6 +11,7 @@ import {
DisplaySetService,
ToolBarSerivce,
ViewportGridService,
HangingProtocolService
// utils,
// redux as reduxOHIF,
} from '@ohif/core';
@@ -54,6 +55,7 @@ function appInit(appConfigOrFunc, defaultExtensions) {
DisplaySetService,
ToolBarSerivce,
ViewportGridService,
HangingProtocolService
]);
/**
+50 -64
View File
@@ -1,7 +1,7 @@
/**
* CSS Grid Reference: http://grid.malven.co/
*/
import React, { useEffect, useCallback } from 'react';
import React, { useEffect, useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui';
import EmptyViewport from './EmptyViewport';
@@ -22,21 +22,53 @@ function ViewerViewportGrid(props) {
} = viewportGrid;
// TODO -> Need some way of selecting which displaySets hit the viewports.
const { DisplaySetService, MeasurementService } = servicesManager.services;
const {
DisplaySetService,
MeasurementService,
HangingProtocolService,
} = servicesManager.services;
// This is a placeholder for applying hanging protocols
// It probably shouldn't be done here
// For now it just hangs the first display set in the study in 1x1
// as sorted by SeriesNumber
useEffect(() => {
const { unsubscribe } = DisplaySetService.subscribe(
DisplaySetService.EVENTS.DISPLAY_SETS_CHANGED,
displaySets => {
displaySets.sort((a, b) => {
const isImageSet = x => x instanceof ImageSet;
return isImageSet(a) === isImageSet(b) ? 0 : isImageSet(a) ? -1 : 1;
});
DisplaySetService.EVENTS.DISPLAY_SETS_ADDED,
eventData => {
const { displaySetsAdded } = eventData;
viewportGridService.setDisplaysetForViewport({
viewportIndex: 0,
displaySetInstanceUID: displaySets[0].displaySetInstanceUID,
});
const data = HangingProtocolService.getState();
// TODO: Sometimes this is undefined?
const { hpAlreadyApplied } = data;
// Match each viewport individually
const numViewports = numRows * numCols;
for (let i = 0; i < numViewports; i++) {
if (hpAlreadyApplied[i] === true) {
return;
}
// Temporary until matching is ported back over from the Meteor version.
const reqSeriesInstanceUID =
data.hangingProtocol.stages[0].viewports[0].seriesMatchingRules[0]
.constraint.equals.value;
const matchingDisplaySet = displaySetsAdded.find(ds => {
return ds.SeriesInstanceUID === reqSeriesInstanceUID;
});
if (!matchingDisplaySet) {
return;
}
viewportGridService.setDisplaysetForViewport({
viewportIndex: i,
displaySetInstanceUID: matchingDisplaySet.displaySetInstanceUID,
});
HangingProtocolService.setHangingProtocolAppliedForViewport(i);
}
}
);
@@ -70,32 +102,6 @@ function ViewerViewportGrid(props) {
};
}, [viewports]);
// TODO -> Make a HangingProtocolService
// Commented out whilst not in use to avoid pointlessly regenerating this function.
// const HangingProtocolService = displaySets => {
// let displaySetInstanceUID;
// // Fallback
// if (!displaySets || !displaySets.length) {
// const displaySet = DisplaySetService.activeDisplaySets[0];
// displaySetInstanceUID = displaySet.displaySetInstanceUID;
// } else {
// const displaySet = displaySets[0];
// displaySetInstanceUID = displaySet.displaySetInstanceUID;
// }
// return {
// numRows: 1,
// numCols: 1,
// activeViewportIndex: 0,
// viewports: [
// {
// displaySetInstanceUID,
// },
// ],
// };
// };
const onDoubleClick = viewportIndex => {
// TODO -> Disabled for now.
// onNewImage on a cornerstone viewport is firing setDisplaySetForViewport.
@@ -138,30 +144,6 @@ function ViewerViewportGrid(props) {
});
};
// TODO:
// Hmm... Should a "displaySet" being added update the viewport based on HP?
// I guess it might.
// This is where you would likely "fill" emptyViewports if none had content
// Or to recheck best placement/priority based on all activeDisplaySets
// useEffect(() => {
// const { unsubscribe } = DisplaySetService.subscribe(
// DisplaySetService.EVENTS.DISPLAY_SETS_ADDED,
// displaySets => {
// displaySets.forEach(ds => console.log(`VPG:ADD::${ds.StudyInstanceUID}`));
// const hp = HangingProtocolService(displaySets);
// viewportGrid.setViewportGrid(hp);
// },
// );
// return unsubscribe;
// }, []);
// const droppedDisplaySet = DisplaySetService.getDisplaySetByUID(
// displaySetInstanceUID
// );
// const updatedViewportGridState = HangingProtocolService([
// droppedDisplaySet,
// ]);
const onDropHandler = (viewportIndex, { displaySetInstanceUID }) => {
console.warn(`DROPPED: ${displaySetInstanceUID}`);
viewportGridService.setDisplaysetForViewport({
@@ -188,7 +170,7 @@ function ViewerViewportGrid(props) {
viewportComponents
);
const onInterationHandler = (event) => {
const onInterationHandler = event => {
if (isActive) return;
if (event) {
@@ -211,7 +193,11 @@ function ViewerViewportGrid(props) {
onInteraction={onInterationHandler}
isActive={isActive}
>
<div className={classNames('h-full w-full flex flex-col align-center', { 'pointer-events-none': !isActive })}>
<div
className={classNames('h-full w-full flex flex-col align-center', {
'pointer-events-none': !isActive,
})}
>
<ViewportComponent
displaySet={displaySet}
viewportIndex={viewportIndex}
+95 -1
View File
@@ -2,13 +2,15 @@ import React, { useEffect } from 'react';
import { useParams } from 'react-router';
import PropTypes from 'prop-types';
// TODO: DicomMetadataStore should be injected?
import { DicomMetadataStore } from '@ohif/core';
import { DicomMetadataStore, utils } from '@ohif/core';
import { DragAndDropProvider, ImageViewerProvider } from '@ohif/ui';
//
import { useQuery } from '@hooks';
import ViewportGrid from '@components/ViewportGrid';
import Compose from './Compose';
const { isLowPriorityModality } = utils;
export default function ModeRoute({
location,
mode,
@@ -46,6 +48,7 @@ export default function ModeRoute({
DisplaySetService,
MeasurementService,
ViewportGridService,
HangingProtocolService,
} = servicesManager.services;
const layoutTemplateData = route.layoutTemplate({ location });
@@ -98,9 +101,13 @@ export default function ModeRoute({
useEffect(() => {
// Core
// TODO: For some reason this is running before the Providers
// are calling setServiceImplementation
// TOOD -> iterate through services.
MeasurementService.clearMeasurements();
ViewportGridService.reset();
HangingProtocolService.reset();
// Extension
extensionManager.onModeEnter();
// Mode
@@ -150,6 +157,93 @@ export default function ModeRoute({
dataSource,
]);
useEffect(() => {
const { unsubscribe } = DicomMetadataStore.subscribe(
DicomMetadataStore.EVENTS.SERIES_ADDED,
({ StudyInstanceUID }) => {
const studyMetadata = DicomMetadataStore.getStudy(
StudyInstanceUID,
);
const sortedSeries = studyMetadata.series.sort((a, b) => {
const aLowPriority = isLowPriorityModality(a.Modality);
const bLowPriority = isLowPriorityModality(b.Modality);
if (!aLowPriority && bLowPriority) {
return -1;
}
if (aLowPriority && !bLowPriority) {
return 1;
}
return a.SeriesNumber - b.SeriesNumber;
})
const { SeriesInstanceUID } = sortedSeries[0]
HangingProtocolService.setHangingProtocol(
{
/*protocolMatchingRules: [
{
id: '7tmuq7KzDMCWFeapc',
weight: 2,
required: false,
attribute: 'x00081030',
constraint: {
contains: {
value: 'DFCI CT CHEST',
},
},
},
],*/
stages: [
{
/*id: 'v5PfGt9F6mffZPif5',
viewportStructure: {
type: 'grid',
properties: {
Rows: 1,
Columns: 1,
},
layoutTemplateName: 'gridLayout',
},*/
viewports: [
{
viewportSettings: {},
imageMatchingRules: [],
seriesMatchingRules: [
{
id: 'mXnsCcNzZL56z7mTZ',
weight: 1,
required: true,
attribute: 'SeriesInstanceUID',
constraint: {
equals: {
value: SeriesInstanceUID,
},
},
},
],
studyMatchingRules: [],
},
],
}
]
}
)
}
);
return unsubscribe;
}, [
mode,
dataSourceName,
location,
DisplaySetService,
extensionManager,
sopClassHandlers,
StudyInstanceUIDsAsArray,
dataSource,
]);
const reducer = (state, action) => {
console.log(state, action);
};