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:
1 parent
0db81b30f3
commit
99b8dc4759
93 files changed
+3825
-1293
No files matched your search
+1
-1
@@ -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
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
+16
-26
@@ -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);
|
||||
}
|
||||
+38
-38
@@ -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,
|
||||
+1
-1
@@ -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());
|
||||
|
||||
+27
-20
@@ -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
|
||||
|
||||
+1
@@ -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;
|
||||
Reference in new issue
Block a user