feat: Add White Labelling, Hanging Protocols, and tons of Extensions / Mode fixes

* modified

* new files in existing extensions/libraries/modes

* fixed bugs and tests

* Clean up

* fix: viewport display fixed

* Fixed configs for deployment

* Removed unnecessary files and functions

* Fixed default HP module

* Added white labelling

* Fixed hash routing

* Removed unnecessary routers

Co-authored-by: Alireza Sedghi <ar.sedghi@gmail.com>
This commit is contained in:
Erik ZieglerandAlireza Sedghi authored and GitHub committed 2021-05-28 14:32:48 -04:00
1 parent 0db81b30f3
commit 99b8dc4759
93 files changed
+3825 -1293

No files matched your search

+1 -1
View File
@@ -34,7 +34,7 @@
"dcmjs": "0.16.1",
"prop-types": "^15.6.2",
"react": "^16.13.1",
"react-cornerstone-viewport": "^4.0.4",
"react-cornerstone-viewport": "^4.0.5",
"react-dom": "^16.13.1",
"webpack": "^4.0.0",
"webpack-merge": "^5.7.3"
@@ -12,7 +12,6 @@ import promptTrackNewSeries from './promptTrackNewSeries';
import promptTrackNewStudy from './promptTrackNewStudy';
import promptSaveReport from './promptSaveReport';
import promptHydrateStructuredReport from './promptHydrateStructuredReport';
import hydrateStructuredReport from './_hydrateStructuredReport.js';
const TrackedMeasurementsContext = React.createContext();
TrackedMeasurementsContext.displayName = 'TrackedMeasurementsContext';
@@ -99,6 +98,7 @@ function TrackedMeasurementsContextProvider(
}),
promptHydrateStructuredReport: promptHydrateStructuredReport.bind(null, {
servicesManager,
extensionManager
}),
});
@@ -1,7 +1,7 @@
import cornerstoneTools from 'cornerstone-tools';
import OHIF, { DicomMetadataStore } from '@ohif/core';
import getLabelFromDCMJSImportedToolData from './utils/getLabelFromDCMJSImportedToolData';
import getToolStateToCornerstoneMeasurementSchema from './getToolStateToCornerstoneMeasurementSchema';
import getCornerstoneToolStateToMeasurementSchema from './getCornerstoneToolStateToMeasurementSchema';
import { adapters } from 'dcmjs';
const { guid } = OHIF.utils;
@@ -15,6 +15,7 @@ export default function _hydrateStructuredReport(
{ servicesManager, extensionManager },
displaySetInstanceUID
) {
const dataSource = extensionManager.getActiveDataSource()[0];
const { MeasurementService, DisplaySetService } = servicesManager.services;
const displaySet = DisplaySetService.getDisplaySetByUID(
@@ -114,14 +115,23 @@ export default function _hydrateStructuredReport(
data.id = guid();
_addToolDataToCornerstoneTools(data, toolType, imageId);
const instance = cornerstone.metaData.get('instance', imageId);
const {
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = instance;
// Let the measurement service know we added to toolState
const toMeasurementSchema = getToolStateToCornerstoneMeasurementSchema(
const toMeasurementSchema = getCornerstoneToolStateToMeasurementSchema(
toolType,
MeasurementService,
DisplaySetService,
imageId
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID
);
const source = MeasurementService.getSource('CornerstoneTools', '4');
@@ -132,7 +142,8 @@ export default function _hydrateStructuredReport(
source,
toolType,
data,
toMeasurementSchema
toMeasurementSchema,
dataSource
);
if (!imageIds.includes(imageId)) {
@@ -148,24 +159,3 @@ export default function _hydrateStructuredReport(
SeriesInstanceUIDs,
};
}
function _addToolDataToCornerstoneTools(data, toolType, imageId) {
const toolState = globalImageIdSpecificToolStateManager.saveToolState();
if (toolState[imageId] === undefined) {
toolState[imageId] = {};
}
const imageIdToolState = toolState[imageId];
// If we don't have tool state for this type of tool, add an empty object
if (imageIdToolState[toolType] === undefined) {
imageIdToolState[toolType] = {
data: [],
};
}
const toolData = imageIdToolState[toolType];
toolData.data.push(data);
}
@@ -1,8 +1,11 @@
export default function getToolStateToCornerstoneMeasurementSchema(
export default function getCornerstoneToolStateToMeasurementSchema(
toolType,
MeasurementService,
DisplaySetService,
imageId
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID
) {
const _getValueTypeFromToolType = toolType => {
const {
@@ -31,7 +34,10 @@ export default function getToolStateToCornerstoneMeasurementSchema(
return measurementData =>
Length(
measurementData,
imageId,
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
DisplaySetService,
_getValueTypeFromToolType
);
@@ -39,7 +45,10 @@ export default function getToolStateToCornerstoneMeasurementSchema(
return measurementData =>
Bidirectional(
measurementData,
imageId,
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
DisplaySetService,
_getValueTypeFromToolType
);
@@ -47,7 +56,10 @@ export default function getToolStateToCornerstoneMeasurementSchema(
return measurementData =>
EllipticalRoi(
measurementData,
imageId,
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
DisplaySetService,
_getValueTypeFromToolType
);
@@ -55,7 +67,10 @@ export default function getToolStateToCornerstoneMeasurementSchema(
return measurementData =>
ArrowAnnotate(
measurementData,
imageId,
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
DisplaySetService,
_getValueTypeFromToolType
);
@@ -64,18 +79,14 @@ export default function getToolStateToCornerstoneMeasurementSchema(
function Length(
measurementData,
imageId,
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
DisplaySetService,
_getValueTypeFromToolType
) {
const tool = measurementData.toolType || measurementData.toolName;
const instance = cornerstone.metaData.get('instance', imageId);
const {
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = instance;
const displaySetInstanceUID = _getDisplaySetInstanceUID(
DisplaySetService,
@@ -113,18 +124,15 @@ function Length(
function Bidirectional(
measurementData,
imageId,
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
DisplaySetService,
_getValueTypeFromToolType
) {
const tool = measurementData.toolType || measurementData.toolName;
const instance = cornerstone.metaData.get('instance', imageId);
const {
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = instance;
const displaySetInstanceUID = _getDisplaySetInstanceUID(
DisplaySetService,
SeriesInstanceUID,
@@ -155,18 +163,14 @@ function Bidirectional(
function EllipticalRoi(
measurementData,
imageId,
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
DisplaySetService,
_getValueTypeFromToolType
) {
const tool = measurementData.toolType || measurementData.toolName;
const instance = cornerstone.metaData.get('instance', imageId);
const {
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = instance;
const displaySetInstanceUID = _getDisplaySetInstanceUID(
DisplaySetService,
@@ -222,18 +226,14 @@ function EllipticalRoi(
function ArrowAnnotate(
measurementData,
imageId,
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
DisplaySetService,
_getValueTypeFromToolType
) {
const tool = measurementData.toolType || measurementData.toolName;
const instance = cornerstone.metaData.get('instance', imageId);
const {
SOPInstanceUID,
FrameOfReferenceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = instance;
const displaySetInstanceUID = _getDisplaySetInstanceUID(
DisplaySetService,
@@ -6,7 +6,7 @@ const RESPONSE = {
SET_STUDY_AND_SERIES: 3,
};
function promptUser({ servicesManager }, ctx, evt) {
function promptUser({ servicesManager, extensionManager }, ctx, evt) {
const { UIViewportDialogService } = servicesManager.services;
const { viewportIndex, StudyInstanceUID, SeriesInstanceUID } = evt;
@@ -12,12 +12,13 @@ const OHIFCornerstoneViewport = props => {
);
};
function getViewportModule({ servicesManager, commandsManager }) {
function getViewportModule({ servicesManager, commandsManager, extensionManager }) {
const ExtendedOHIFCornerstoneTrackingViewport = props => {
return (
<OHIFCornerstoneViewport
servicesManager={servicesManager}
commandsManager={commandsManager}
extensionManager={extensionManager}
{...props}
/>
);
@@ -114,15 +114,16 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
// Are we exposing the right API for measurementService?
// This watches for ALL MeasurementService changes. It updates a timestamp,
// which is debounced. After a brief period of inactivity, this triggers
// a re-render where we grab up-to-date measurements.
// a re-render where we grab up-to-date measurements
useEffect(() => {
const added = MeasurementService.EVENTS.MEASUREMENT_ADDED;
const addedRaw = MeasurementService.EVENTS.RAW_MEASUREMENT_ADDED;
const updated = MeasurementService.EVENTS.MEASUREMENT_UPDATED;
const removed = MeasurementService.EVENTS.MEASUREMENT_REMOVED;
const cleared = MeasurementService.EVENTS.MEASUREMENTS_CLEARED;
const subscriptions = [];
[added, updated, removed, cleared].forEach(evt => {
[added, addedRaw, updated, removed, cleared].forEach(evt => {
subscriptions.push(
MeasurementService.subscribe(evt, () => {
setMeasurementsUpdated(Date.now().toString());
@@ -28,7 +28,7 @@ function PanelStudyBrowserTracking({
// Normally you nest the components so the tree isn't so deep, and the data
// doesn't have to have such an intense shape. This works well enough for now.
// Tabs --> Studies --> DisplaySets --> Thumbnails
const [{ StudyInstanceUIDs }, dispatchImageViewer] = useImageViewer();
const { StudyInstanceUIDs } = useImageViewer();
const [
{ activeViewportIndex, viewports, numCols, numRows },
viewportGridService,
@@ -58,27 +58,34 @@ function PanelStudyBrowserTracking({
const isSingleViewport = numCols === 1 && numRows === 1;
// TODO: Should this be somewhere else? Feels more like a mode "lifecycle" setup/destroy?
useEffect(() => {
const { unsubscribe } = MeasurementService.subscribe(
MeasurementService.EVENTS.MEASUREMENT_ADDED,
({ source, measurement }) => {
const {
referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID,
} = measurement;
const added = MeasurementService.EVENTS.MEASUREMENT_ADDED;
const addedRaw = MeasurementService.EVENTS.RAW_MEASUREMENT_ADDED;
const subscriptions = [];
sendTrackedMeasurementsEvent('SET_DIRTY', { SeriesInstanceUID });
sendTrackedMeasurementsEvent('TRACK_SERIES', {
viewportIndex: activeViewportIndex,
StudyInstanceUID,
SeriesInstanceUID,
});
}
);
[added, addedRaw].forEach(evt => {
subscriptions.push(
MeasurementService.subscribe(evt, ({ source, measurement }) => {
const {
referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID,
} = measurement;
return unsubscribe;
// eslint-disable-next-line react-hooks/exhaustive-deps
sendTrackedMeasurementsEvent('SET_DIRTY', { SeriesInstanceUID });
sendTrackedMeasurementsEvent('TRACK_SERIES', {
viewportIndex: activeViewportIndex,
StudyInstanceUID,
SeriesInstanceUID,
});
}).unsubscribe
);
});
return () => {
subscriptions.forEach(unsub => {
unsub();
});
};
}, [MeasurementService, activeViewportIndex, sendTrackedMeasurementsEvent]);
const { trackedStudy, trackedSeries } = trackedMeasurements.context;
@@ -89,7 +96,6 @@ function PanelStudyBrowserTracking({
async function fetchStudiesForPatient(StudyInstanceUID) {
const qidoStudiesForPatient =
(await getStudiesForPatientByStudyInstanceUID(StudyInstanceUID)) || [];
// TODO: This should be "naturalized DICOM JSON" studies
const mappedStudies = _mapDataSourceStudies(qidoStudiesForPatient);
const actuallyMappedStudies = mappedStudies.map(qidoStudy => {
@@ -182,6 +188,7 @@ function PanelStudyBrowserTracking({
const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
const imageId = imageIds[Math.floor(imageIds.length / 2)];
// TODO: Is it okay that imageIds are not returned here for SR displaysets?
if (imageId) {
// When the image arrives, render it and store the result in the thumbnailImgSrcMap
@@ -2,6 +2,7 @@ async function getStudiesForPatientByStudyInstanceUID(
dataSource,
StudyInstanceUID
) {
if (StudyInstanceUID === undefined) return;
// TODO: The `DicomMetadataStore` should short-circuit both of these requests
// Data _could_ be here from route query, or if using JSON data source
// We could also force this to "await" these values being available in the DICOMStore?
@@ -1,8 +1,7 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import CornerstoneViewport from 'react-cornerstone-viewport';
import OHIF, { utils } from '@ohif/core';
import {
Notification,
@@ -12,14 +11,10 @@ import {
useViewportDialog,
} from '@ohif/ui';
import { useTrackedMeasurements } from './../getContextModule';
import ViewportOverlay from './ViewportOverlay';
import ViewportLoadingIndicator from './ViewportLoadingIndicator';
import setCornerstoneMeasurementActive from '../_shared/setCornerstoneMeasurementActive';
import setActiveAndPassiveToolsForElement from '../_shared/setActiveAndPassiveToolsForElement';
import getTools from '../_shared/getTools';
const scrollToIndex = cornerstoneTools.importInternal('util/scrollToIndex');
const { formatDate } = utils;
// TODO -> Get this list from the list of tracked measurements.
@@ -40,19 +35,19 @@ const BaseAnnotationTool = cornerstoneTools.importInternal(
const { StackManager } = OHIF.utils;
function TrackedCornerstoneViewport({
children,
dataSource,
displaySet,
viewportIndex,
servicesManager,
commandsManager,
}) {
function TrackedCornerstoneViewport(props) {
const {
ToolBarService,
DisplaySetService,
MeasurementService,
} = servicesManager.services;
children,
dataSource,
displaySet,
viewportIndex,
servicesManager,
extensionManager,
commandsManager,
} = props;
const { ToolBarService } = servicesManager.services;
const [trackedMeasurements] = useTrackedMeasurements();
const [
{ activeViewportIndex, viewports },
@@ -60,73 +55,9 @@ function TrackedCornerstoneViewport({
] = useViewportGrid();
const [{ isCineEnabled, cines }, cineService] = useCine();
const [viewportDialogState, viewportDialogApi] = useViewportDialog();
const [viewportData, setViewportData] = useState(null);
const [element, setElement] = useState(null);
const [isTracked, setIsTracked] = useState(false);
const [trackedMeasurementId, setTrackedMeasurementId] = useState(null);
// TODO: Still needed? Better way than import `OHIF` and destructure?
// Why is this managed by `core`?
useEffect(() => {
return () => {
StackManager.clearStacks();
};
}, []);
useEffect(() => {
cineService.setCine({ id: viewportIndex });
}, [viewportIndex]);
useEffect(() => {
const unsubscribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents(
MeasurementService,
DisplaySetService,
element,
viewportIndex,
displaySet.displaySetInstanceUID
);
_checkForCachedJumpToMeasurementEvents(
MeasurementService,
DisplaySetService,
element,
viewportIndex,
displaySet.displaySetInstanceUID
);
return () => {
unsubscribeFromJumpToMeasurementEvents();
};
}, [element, displaySet]);
useEffect(() => {
if (!element) {
return;
}
const allTools = cornerstoneTools.store.state.tools;
const toolsForElement = allTools.filter(tool => tool.element === element);
toolsForElement.forEach(tool => {
if (
tool instanceof ArrowAnnotateTool ||
tool instanceof BidirectionalTool ||
tool instanceof EllipticalRoiTool ||
tool instanceof LengthTool
) {
const configuration = tool.configuration;
configuration.renderDashed = !isTracked;
tool.configuration = configuration;
}
});
const enabledElement = cornerstone.getEnabledElement(element);
if (enabledElement.image) {
cornerstone.updateImage(element);
}
}, [isTracked]);
const [element, setElement] = useState(null);
const onElementEnabled = evt => {
const eventData = evt.detail;
@@ -191,51 +122,33 @@ function TrackedCornerstoneViewport({
};
useEffect(() => {
const {
StudyInstanceUID,
displaySetInstanceUID,
sopClassUids,
} = displaySet;
if (!StudyInstanceUID || !displaySetInstanceUID) {
if (!element) {
return;
}
const allTools = cornerstoneTools.store.state.tools;
const toolsForElement = allTools.filter(tool => tool.element === element);
if (sopClassUids && sopClassUids.length > 1) {
console.warn(
'More than one SOPClassUID in the same series is not yet supported.'
);
}
toolsForElement.forEach(tool => {
if (
tool instanceof ArrowAnnotateTool ||
tool instanceof BidirectionalTool ||
tool instanceof EllipticalRoiTool ||
tool instanceof LengthTool
) {
const configuration = tool.configuration;
_getViewportData(dataSource, displaySet).then(setViewportData);
}, [dataSource, displaySet, viewports, viewportIndex]);
configuration.renderDashed = !isTracked;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
let childrenWithProps = null;
if (!viewportData) {
return null;
}
const {
imageIds,
currentImageIdIndex,
// If this comes from the instance, would be a better default
// `FrameTime` in the instance
// frameRate = 0,
} = viewportData.stack;
if (children && children.length) {
childrenWithProps = children.map((child, index) => {
return (
child &&
React.cloneElement(child, {
viewportIndex,
key: index,
})
);
tool.configuration = configuration;
}
});
}
const enabledElement = cornerstone.getEnabledElement(element);
if (enabledElement.image) {
cornerstone.updateImage(element);
}
}, [isTracked]);
// We have...
// StudyInstanceUid, DisplaySetInstanceUid
@@ -313,6 +226,19 @@ function TrackedCornerstoneViewport({
);
}
const renderViewport = () => {
const { component: Component } = extensionManager.getModuleEntry(
'org.ohif.cornerstone.viewportModule.cornerstone'
);
return (
<Component
onElementEnabled={onElementEnabled}
element={element}
{...props}
></Component>
);
};
const cine = cines[viewportIndex];
const isPlaying = (cine && cine.isPlaying) || false;
const frameRate = (cine && cine.frameRate) || 24;
@@ -341,10 +267,12 @@ function TrackedCornerstoneViewport({
patientSex: PatientSex || '',
patientAge: PatientAge || '',
MRN: PatientID || '',
thickness: SliceThickness ? `${SliceThickness.toFixed(2)}mm` : '',
thickness: SliceThickness
? `${parseFloat(SliceThickness).toFixed(2)}mm`
: '',
spacing:
SpacingBetweenSlices !== undefined
? `${SpacingBetweenSlices.toFixed(2)}mm`
? `${parseFloat(SpacingBetweenSlices).toFixed(2)}mm`
: '',
scanner: ManufacturerModelName || '',
},
@@ -362,29 +290,7 @@ function TrackedCornerstoneViewport({
/>
{/* TODO: Viewport interface to accept stack or layers of content like this? */}
<div className="relative flex flex-row w-full h-full overflow-hidden">
<CornerstoneViewport
onElementEnabled={onElementEnabled}
viewportIndex={viewportIndex}
imageIds={imageIds}
imageIdIndex={currentImageIdIndex}
// Sync resize throttle w/ sidepanel animation duration to prevent
// seizure inducing strobe blinking effect
resizeRefreshRateMs={150}
isActive={true} // todo
isStackPrefetchEnabled={true} // todo
isPlaying={isPlaying}
frameRate={frameRate}
isOverlayVisible={true}
loadingIndicatorComponent={ViewportLoadingIndicator}
viewportOverlayComponent={props => {
return (
<ViewportOverlay
{...props}
activeTools={ToolBarService.getActiveTools()}
/>
);
}}
/>
{renderViewport()}
<div className="absolute w-full">
{viewportDialogState.viewportIndex === viewportIndex && (
<Notification
@@ -397,7 +303,6 @@ function TrackedCornerstoneViewport({
/>
)}
</div>
{childrenWithProps}
</div>
</>
);
@@ -417,54 +322,6 @@ TrackedCornerstoneViewport.defaultProps = {
const _viewportLabels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];
/**
* Obtain the CornerstoneTools Stack for the specified display set.
*
* @param {Object} displaySet
* @param {Object} dataSource
* @return {Object} CornerstoneTools Stack
*/
function _getCornerstoneStack(displaySet, dataSource) {
// Get stack from Stack Manager
const storedStack = StackManager.findOrCreateStack(displaySet, dataSource);
// Clone the stack here so we don't mutate it
const stack = Object.assign({}, storedStack);
return stack;
}
// TODO -> disabled double click for now: onDoubleClick={_onDoubleClick}
function _onDoubleClick() {
const cancelActiveManipulatorsForElement = cornerstoneTools.getModule(
'manipulatorState'
).setters.cancelActiveManipulatorsForElement;
const enabledElements = cornerstoneTools.store.state.enabledElements;
enabledElements.forEach(element => {
cancelActiveManipulatorsForElement(element);
});
}
/**
* Builds the viewport data from a datasource and a displayset.
*
* @param {Object} dataSource
* @param {Object} displaySet
* @return {Object} viewport data
*/
async function _getViewportData(dataSource, displaySet) {
const stack = _getCornerstoneStack(displaySet, dataSource);
const viewportData = {
StudyInstanceUID: displaySet.StudyInstanceUID,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
stack,
};
return viewportData;
}
function _getNextMeasurementId(
direction,
servicesManager,
@@ -518,112 +375,4 @@ function _getNextMeasurementId(
return newTrackedMeasurementId;
}
function _subscribeToJumpToMeasurementEvents(
MeasurementService,
DisplaySetService,
element,
viewportIndex,
displaySetInstanceUID
) {
const { unsubscribe } = MeasurementService.subscribe(
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT,
({ viewportIndex: jumpToMeasurementViewportIndex, measurement }) => {
// check if the correct viewport index.
if (viewportIndex !== jumpToMeasurementViewportIndex) {
// Event for a different viewport.
return;
}
if (measurement.displaySetInstanceUID !== displaySetInstanceUID) {
// Not for this displaySet.
return;
}
_jumpToMeasurement(
measurement,
element,
viewportIndex,
MeasurementService,
DisplaySetService
);
}
);
return unsubscribe;
}
function _checkForCachedJumpToMeasurementEvents(
MeasurementService,
DisplaySetService,
element,
viewportIndex,
displaySetInstanceUID
) {
// Check if there is a queued jumpToMeasurement event
const measurementIdToJumpTo = MeasurementService.getJumpToMeasurement(
viewportIndex
);
if (measurementIdToJumpTo && element) {
// Jump to measurement if the measurement exists
const measurement = MeasurementService.getMeasurement(
measurementIdToJumpTo
);
if (measurement.displaySetInstanceUID === displaySetInstanceUID) {
_jumpToMeasurement(
measurement,
element,
viewportIndex,
MeasurementService,
DisplaySetService
);
}
}
}
function _jumpToMeasurement(
measurement,
targetElement,
viewportIndex,
MeasurementService,
DisplaySetService
) {
const { displaySetInstanceUID, SOPInstanceUID } = measurement;
const referencedDisplaySet = DisplaySetService.getDisplaySetByUID(
displaySetInstanceUID
);
const imageIndex = referencedDisplaySet.images.findIndex(
i => i.SOPInstanceUID === SOPInstanceUID
);
setCornerstoneMeasurementActive(measurement);
if (targetElement !== null) {
const enabledElement = cornerstone.getEnabledElement(targetElement);
// Wait for the image to update or we get a race condition when the element has only just been enabled.
const scrollToHandler = evt => {
scrollToIndex(targetElement, imageIndex);
targetElement.removeEventListener(
cornerstone.EVENTS.IMAGE_RENDERED,
scrollToHandler
);
};
targetElement.addEventListener(
cornerstone.EVENTS.IMAGE_RENDERED,
scrollToHandler
);
if (enabledElement.image) {
cornerstone.updateImage(targetElement);
}
// Jump to measurement consumed, remove.
MeasurementService.removeJumpToMeasurement(viewportIndex);
}
}
export default TrackedCornerstoneViewport;