diff --git a/extensions/cornerstone-3d/package.json b/extensions/cornerstone-3d/package.json
index 803466ad6d7..8cd8b4bfe6c 100644
--- a/extensions/cornerstone-3d/package.json
+++ b/extensions/cornerstone-3d/package.json
@@ -30,7 +30,7 @@
"@ohif/core": "^3.0.0",
"@ohif/ui": "^2.0.0",
"cornerstone-wado-image-loader": "^4.1.2",
- "dcmjs": "0.22.0",
+ "dcmjs": "^0.22.1",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
@@ -45,9 +45,9 @@
"@babel/runtime": "7.17.9",
"lodash.merge": "^4.6.2",
"lodash.debounce": "4.0.8",
- "@cornerstonejs/core": "^0.8.1",
- "@cornerstonejs/tools": "^0.15.0",
- "@cornerstonejs/streaming-image-volume-loader": "^0.2.26",
+ "@cornerstonejs/core": "^0.10.2",
+ "@cornerstonejs/tools": "^0.17.2",
+ "@cornerstonejs/streaming-image-volume-loader": "^0.3.2",
"shader-loader": "^1.3.1",
"@kitware/vtk.js": "^24.10.0",
"dom-to-image": "^2.6.0",
diff --git a/extensions/cornerstone-3d/src/OHIFCornerstone3DViewport.tsx b/extensions/cornerstone-3d/src/OHIFCornerstone3DViewport.tsx
deleted file mode 100644
index 6f7c1ef8826..00000000000
--- a/extensions/cornerstone-3d/src/OHIFCornerstone3DViewport.tsx
+++ /dev/null
@@ -1,272 +0,0 @@
-import React, { useEffect, useRef, useCallback } from 'react';
-import { utilities } from '@cornerstonejs/tools';
-import ReactResizeDetector from 'react-resize-detector';
-import { useViewportGrid } from '@ohif/ui';
-
-function areEqual(prevProps, nextProps) {
- if (nextProps.needsRerendering) {
- return false;
- }
-
- if (prevProps.displaySets.length !== nextProps.displaySets.length) {
- return false;
- }
-
- // Todo: handle fusion
- // Todo: handle orientation
- const prevDisplaySets = prevProps.displaySets[0];
- const nextDisplaySets = nextProps.displaySets[0];
-
- if (prevDisplaySets && nextDisplaySets) {
- return (
- prevDisplaySets.displaySetInstanceUID ===
- nextDisplaySets.displaySetInstanceUID &&
- prevDisplaySets.images.length === nextDisplaySets.images.length &&
- prevDisplaySets.images.every(
- (prevImage, index) =>
- prevImage.imageId === nextDisplaySets.images[index].imageId
- )
- );
- }
- return false;
-}
-
-// Todo: This should be done with expose of internal API similar to react-vtkjs-viewport
-// Then we don't need to worry about the re-renders if the props change.
-const OHIFCornerstoneViewport = React.memo(props => {
- const {
- displaySets,
- viewportIndex,
- dataSource,
- viewportOptions,
- displaySetOptions,
- servicesManager,
- } = props;
-
- const [_, viewportGridService] = useViewportGrid();
-
- const elementRef = useRef();
- const {
- ViewportService,
- MeasurementService,
- DisplaySetService,
- } = servicesManager.services;
-
- // useCallback for onResize
- const onResize = useCallback(
- props => {
- if (elementRef.current) {
- const element = elementRef.current;
- ViewportService.resize();
- }
- },
- [elementRef]
- );
-
- // disable the element upon unmounting
- useEffect(() => {
- // setElementRef(targetRef.current);
- ViewportService.enableElement(viewportIndex, elementRef.current);
- return () => {
- ViewportService.disableElement(viewportIndex);
- };
- }, []);
-
- // Todo: Use stackManager to handle the stack creation and imageId get, problem
- // would be what to do with the volumes which needs different schema for loading: streaming-wadors
- // Stack manager shouldn't care about the type of volume. Maybe add a postImageId creation callback?
- // Maybe we should need a volumeManager later on.
- useEffect(() => {
- ViewportService.setViewportDisplaySets(
- viewportIndex,
- displaySets,
- viewportOptions,
- displaySetOptions,
- dataSource
- );
- }, [
- viewportIndex,
- viewportOptions,
- displaySetOptions,
- displaySets,
- dataSource,
- ViewportService,
- ]);
-
- /**
- * There are two scenarios for jump to click
- * 1. Current viewports contain the displaySet that the annotation was drawn on
- * 2. Current viewports don't contain the displaySet that the annotation was drawn on
- * and we need to change the viewports displaySet for jumping.
- * Since measurement_jump happens via events and listeners, the former case is handled
- * by the measurement_jump direct callback, but the latter case is handled first by
- * the viewportGrid to set the correct displaySet on the viewport, AND THEN we check
- * the cache for jumping to see if there is any jump queued, then we jump to the correct slice.
- */
- useEffect(() => {
- const unsubscribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents(
- MeasurementService,
- DisplaySetService,
- elementRef,
- viewportIndex,
- displaySets,
- viewportGridService
- );
-
- _checkForCachedJumpToMeasurementEvents(
- MeasurementService,
- DisplaySetService,
- elementRef,
- viewportIndex,
- displaySets,
- viewportGridService
- );
-
- return () => {
- unsubscribeFromJumpToMeasurementEvents();
- };
- }, [
- displaySets,
- elementRef,
- viewportIndex,
- viewportGridService,
- MeasurementService,
- DisplaySetService,
- ]);
-
- return (
-
-
- e.preventDefault()}
- onMouseDown={e => e.preventDefault()}
- ref={elementRef}
- >
-
- );
-}, areEqual);
-
-function _subscribeToJumpToMeasurementEvents(
- MeasurementService,
- DisplaySetService,
- elementRef,
- viewportIndex,
- displaySets,
- viewportGridService
-) {
- const displaysUIDs = displaySets.map(
- displaySet => displaySet.displaySetInstanceUID
- );
- const { unsubscribe } = MeasurementService.subscribe(
- MeasurementService.EVENTS.JUMP_TO_MEASUREMENT,
- ({ measurement }) => {
- if (!measurement) return;
-
- // Jump the the measurement if the viewport contains the displaySetUID (fusion)
- if (displaysUIDs.includes(measurement.displaySetInstanceUID)) {
- _jumpToMeasurement(
- measurement,
- elementRef,
- viewportIndex,
- MeasurementService,
- DisplaySetService,
- viewportGridService
- );
- }
- }
- );
-
- return unsubscribe;
-}
-
-// Check if there is a queued jumpToMeasurement event
-function _checkForCachedJumpToMeasurementEvents(
- MeasurementService,
- DisplaySetService,
- elementRef,
- viewportIndex,
- displaySets,
- viewportGridService
-) {
- const displaysUIDs = displaySets.map(
- displaySet => displaySet.displaySetInstanceUID
- );
-
- const measurementIdToJumpTo = MeasurementService.getJumpToMeasurement(
- viewportIndex
- );
-
- if (measurementIdToJumpTo && elementRef) {
- // Jump to measurement if the measurement exists
- const measurement = MeasurementService.getMeasurement(
- measurementIdToJumpTo
- );
-
- if (displaysUIDs.includes(measurement.displaySetInstanceUID)) {
- _jumpToMeasurement(
- measurement,
- elementRef,
- viewportIndex,
- MeasurementService,
- DisplaySetService,
- viewportGridService
- );
- }
- }
-}
-
-function _jumpToMeasurement(
- measurement,
- targetElementRef,
- viewportIndex,
- MeasurementService,
- DisplaySetService,
- viewportGridService
-) {
- const targetElement = targetElementRef.current;
- const { displaySetInstanceUID, SOPInstanceUID } = measurement;
-
- if (!SOPInstanceUID) {
- console.warn('cannot jump in a non-acquisition plane measurements yet');
- }
-
- const referencedDisplaySet = DisplaySetService.getDisplaySetByUID(
- displaySetInstanceUID
- );
-
- const imageIdIndex = referencedDisplaySet.images.findIndex(
- i => i.SOPInstanceUID === SOPInstanceUID
- );
-
- // Todo: setCornerstoneMeasurementActive should be handled by the toolGroupManager
- // to set it properly
- // setCornerstoneMeasurementActive(measurement);
-
- viewportGridService.setActiveViewportIndex(viewportIndex);
-
- if (targetElement !== null) {
- const metadata = {
- ...measurement.metadata,
- imageIdIndex,
- };
- utilities.jumpToSlice(targetElement, metadata);
-
- // Jump to measurement consumed, remove.
- MeasurementService.removeJumpToMeasurement(viewportIndex);
- }
-}
-
-// Component displayName
-OHIFCornerstoneViewport.displayName = 'OHIFCornerstoneViewport';
-
-export default OHIFCornerstoneViewport;
diff --git a/extensions/cornerstone-3d/src/Viewport/OHIFCornerstone3DViewport.tsx b/extensions/cornerstone-3d/src/Viewport/OHIFCornerstone3DViewport.tsx
index eb3e6ea9857..6620b1b070a 100644
--- a/extensions/cornerstone-3d/src/Viewport/OHIFCornerstone3DViewport.tsx
+++ b/extensions/cornerstone-3d/src/Viewport/OHIFCornerstone3DViewport.tsx
@@ -437,11 +437,9 @@ function _jumpToMeasurement(
viewportGridService.setActiveViewportIndex(viewportIndex);
if (targetElement !== null) {
- const metadata = {
- ...measurement.metadata,
- imageIdIndex,
- };
- cs3DTools.utilities.jumpToSlice(targetElement, metadata);
+ cs3DTools.utilities.jumpToSlice(targetElement, {
+ imageIndex: imageIdIndex,
+ });
cs3DTools.annotation.selection.setAnnotationSelected(measurement.uid);
// Jump to measurement consumed, remove.
diff --git a/extensions/cornerstone-3d/src/commandsModule.js b/extensions/cornerstone-3d/src/commandsModule.js
index f3402af9dc8..15e23569a81 100644
--- a/extensions/cornerstone-3d/src/commandsModule.js
+++ b/extensions/cornerstone-3d/src/commandsModule.js
@@ -6,6 +6,7 @@ import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownload
import { Enums } from '@cornerstonejs/tools';
import { getEnabledElement } from './state';
+import callInputDialog from './utils/callInputDialog';
const commandsModule = ({ servicesManager }) => {
const {
@@ -13,6 +14,7 @@ const commandsModule = ({ servicesManager }) => {
ToolGroupService,
CineService,
ToolBarService,
+ UIDialogService,
} = servicesManager.services;
function _getActiveViewportEnabledElement() {
@@ -26,6 +28,9 @@ const commandsModule = ({ servicesManager }) => {
getActiveViewportEnabledElement: () => {
return _getActiveViewportEnabledElement();
},
+ arrowTextCallback: ({ callback, data }) => {
+ callInputDialog(UIDialogService, data, callback);
+ },
toggleCine: () => {
const { viewports } = ViewportGridService.getState();
const { isCineEnabled } = CineService.getState();
@@ -35,11 +40,10 @@ const commandsModule = ({ servicesManager }) => {
CineService.setCine({ id: index, isPlaying: false })
);
},
- setWindowLevel({ windowLevel, toolGroupId }) {
- const { window: windowWidth, level: windowCenter } = windowLevel;
+ setWindowLevel({ window, level, toolGroupId }) {
// convert to numbers
- const windowWidthNum = Number(windowWidth);
- const windowCenterNum = Number(windowCenter);
+ const windowWidthNum = Number(window);
+ const windowCenterNum = Number(level);
const { viewportId } = _getActiveViewportEnabledElement();
const viewportToolGroupId = ToolGroupService.getToolGroupForViewport(
@@ -268,12 +272,7 @@ const commandsModule = ({ servicesManager }) => {
const { viewport } = enabledElement;
- let options = {};
- if (viewport instanceof cornerstone3D.StackViewport) {
- options = { direction };
- } else {
- throw new Error('scroll: volume viewport is not supported yet');
- }
+ const options = { delta: direction };
cornerstone3DTools.utilities.stackScrollTool.scrollThroughStack(
viewport,
@@ -358,6 +357,11 @@ const commandsModule = ({ servicesManager }) => {
storeContexts: [],
options: {},
},
+ arrowTextCallback: {
+ commandFn: actions.arrowTextCallback,
+ storeContexts: [],
+ options: {},
+ },
};
return {
diff --git a/extensions/cornerstone-3d/src/init.js b/extensions/cornerstone-3d/src/init.js
index b4d0e6d084c..bea513adc16 100644
--- a/extensions/cornerstone-3d/src/init.js
+++ b/extensions/cornerstone-3d/src/init.js
@@ -7,6 +7,7 @@ import {
eventTarget,
EVENTS,
imageLoadPoolManager,
+ Settings,
} from '@cornerstonejs/core';
import { Enums, utilities } from '@cornerstonejs/tools';
@@ -15,7 +16,7 @@ import Cornerstone3DViewportService from './services/ViewportService/Cornerstone
import initCornerstoneTools from './initCornerstoneTools';
import { connectToolsToMeasurementService } from './initMeasurementService';
-import callInputDialog from './callInputDialog';
+import callInputDialog from './utils/callInputDialog';
import initCineService from './initCineService';
const cs3DToolsEvents = Enums.Events;
@@ -36,6 +37,10 @@ export default async function init({
await cs3DInit();
initCornerstoneTools();
+ // Don't use cursors in viewports
+ // Todo: this should come from extension/app configuration
+ Settings.getRuntimeSettings().set('useCursors', false);
+
const {
UserAuthenticationService,
ToolGroupService,
diff --git a/extensions/cornerstone-3d/src/initCornerstoneTools.js b/extensions/cornerstone-3d/src/initCornerstoneTools.js
index d8ea721ba70..e0bb399f641 100644
--- a/extensions/cornerstone-3d/src/initCornerstoneTools.js
+++ b/extensions/cornerstone-3d/src/initCornerstoneTools.js
@@ -37,6 +37,20 @@ export default function initCornerstone3DTools(configuration = {}) {
cornerstone3DTools.addTool(AngleTool);
cornerstone3DTools.addTool(MagnifyTool);
// cornerstone3DTools.addTool(CrosshairsTool);
+
+ // Modify annotation tools to use dashed lines on SR
+ const annotationStyle = {
+ textBoxFontSize: '15px',
+ lineWidth: '1.5',
+ };
+
+ const defaultStyles = cornerstone3DTools.annotation.config.style.getDefaultToolStyles();
+ cornerstone3DTools.annotation.config.style.setDefaultToolStyles({
+ global: {
+ ...defaultStyles.global,
+ ...annotationStyle,
+ },
+ });
}
const toolNames = {
diff --git a/extensions/cornerstone-3d/src/initMeasurementService.js b/extensions/cornerstone-3d/src/initMeasurementService.js
index 893a1d84031..2a9b2aa2908 100644
--- a/extensions/cornerstone-3d/src/initMeasurementService.js
+++ b/extensions/cornerstone-3d/src/initMeasurementService.js
@@ -212,12 +212,12 @@ const connectMeasurementServiceToTools = (
return;
}
- const { id, label } = measurement;
+ const { uid, label } = measurement;
- const sourceAnnotation = annotation.state.getAnnotation(id);
+ const sourceAnnotation = annotation.state.getAnnotation(uid);
if (sourceAnnotation) {
- sourceAnnotation.label = label;
+ sourceAnnotation.data.label = label;
if (sourceAnnotation.hasOwnProperty('text')) {
// Deal with the weird case of ArrowAnnotate.
sourceAnnotation.text = label;
diff --git a/extensions/cornerstone-3d/src/callInputDialog.tsx b/extensions/cornerstone-3d/src/utils/callInputDialog.tsx
similarity index 100%
rename from extensions/cornerstone-3d/src/callInputDialog.tsx
rename to extensions/cornerstone-3d/src/utils/callInputDialog.tsx
diff --git a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/ArrowAnnotate.js b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/ArrowAnnotate.js
index 054bd094107..96eea1bddd5 100644
--- a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/ArrowAnnotate.js
+++ b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/ArrowAnnotate.js
@@ -60,7 +60,7 @@ const Length = {
DisplaySetService
);
- const displayText = getDisplayText(mappedAnnotations);
+ const displayText = getDisplayText(mappedAnnotations, displaySet);
return {
uid: annotationUID,
@@ -104,6 +104,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
annotations.push({
SeriesInstanceUID,
+ SOPInstanceUID,
SeriesNumber,
text,
});
@@ -111,7 +112,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
return annotations;
}
-function getDisplayText(mappedAnnotations) {
+function getDisplayText(mappedAnnotations, displaySet) {
if (!mappedAnnotations) {
return '';
}
@@ -119,8 +120,22 @@ function getDisplayText(mappedAnnotations) {
const displayText = [];
// Area is the same for all series
- const { SeriesNumber } = mappedAnnotations[0];
- displayText.push(`(S: ${SeriesNumber})`);
+ const { SeriesNumber, SOPInstanceUID } = mappedAnnotations[0];
+
+ const instance = displaySet.images.find(
+ image => image.SOPInstanceUID === SOPInstanceUID
+ );
+
+ let InstanceNumber;
+ if (instance) {
+ InstanceNumber = instance.InstanceNumber;
+ }
+
+ displayText.push(
+ InstanceNumber
+ ? `(S: ${SeriesNumber} I: ${InstanceNumber})`
+ : `(S: ${SeriesNumber})`
+ );
return displayText;
}
diff --git a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/Bidirectional.js b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/Bidirectional.js
index 904338515b6..93913330751 100644
--- a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/Bidirectional.js
+++ b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/Bidirectional.js
@@ -55,7 +55,7 @@ const Bidirectional = {
DisplaySetService
);
- const displayText = getDisplayText(mappedAnnotations);
+ const displayText = getDisplayText(mappedAnnotations, displaySet);
const getReport = () =>
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
@@ -69,7 +69,7 @@ const Bidirectional = {
referenceStudyUID: StudyInstanceUID,
toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
- label: metadata.label,
+ label: data.label,
displayText: displayText,
data: data.cachedStats,
type: getValueTypeFromToolType(toolName),
@@ -94,10 +94,11 @@ function getMappedAnnotations(annotation, DisplaySetService) {
let displaySet;
+ let SeriesInstanceUID, SOPInstanceUID;
if (targetId.startsWith('imageId:')) {
- const { SOPInstanceUID, SeriesInstanceUID } = getSOPInstanceAttributes(
+ ({ SOPInstanceUID, SeriesInstanceUID } = getSOPInstanceAttributes(
referencedImageId
- );
+ ));
displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
@@ -109,12 +110,13 @@ function getMappedAnnotations(annotation, DisplaySetService) {
throw new Error('Not implemented');
}
- const { SeriesNumber, SeriesInstanceUID } = displaySet;
+ const { SeriesNumber } = displaySet;
const { length, width } = targetStats;
const unit = 'mm';
annotations.push({
SeriesInstanceUID,
+ SOPInstanceUID,
SeriesNumber,
unit,
length,
@@ -163,7 +165,7 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
};
}
-function getDisplayText(mappedAnnotations) {
+function getDisplayText(mappedAnnotations, displaySet) {
if (!mappedAnnotations || !mappedAnnotations.length) {
return '';
}
@@ -171,11 +173,24 @@ function getDisplayText(mappedAnnotations) {
const displayText = [];
// Area is the same for all series
- const { length, width, SeriesNumber } = mappedAnnotations[0];
- const roundedLength = utils.roundNumber(length, 2);
- const roundedWidth = utils.roundNumber(width, 2);
+ const { length, width, SeriesNumber, SOPInstanceUID } = mappedAnnotations[0];
+ const roundedLength = utils.roundNumber(length, 1);
+ const roundedWidth = utils.roundNumber(width, 1);
+
+ const instance = displaySet.images.find(
+ image => image.SOPInstanceUID === SOPInstanceUID
+ );
+
+ let InstanceNumber;
+ if (instance) {
+ InstanceNumber = instance.InstanceNumber;
+ }
- displayText.push(`L: ${roundedLength} mm (S: ${SeriesNumber})`);
+ displayText.push(
+ InstanceNumber
+ ? `L: ${roundedLength} mm (S: ${SeriesNumber} I: ${InstanceNumber})`
+ : `L: ${roundedLength} mm (S: ${SeriesNumber})`
+ );
displayText.push(`W: ${roundedWidth} mm`);
return displayText;
diff --git a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/EllipticalROI.js b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/EllipticalROI.js
index c1d1d57f81d..57cb1a5ca3a 100644
--- a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/EllipticalROI.js
+++ b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/EllipticalROI.js
@@ -56,7 +56,7 @@ const EllipticalROI = {
DisplaySetService
);
- const displayText = getDisplayText(mappedAnnotations);
+ const displayText = getDisplayText(mappedAnnotations, displaySet);
const getReport = () =>
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
@@ -70,7 +70,7 @@ const EllipticalROI = {
referenceStudyUID: StudyInstanceUID,
toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
- label: metadata.label,
+ label: data.label,
displayText: displayText,
data: data.cachedStats,
type: getValueTypeFromToolType(toolName),
@@ -95,10 +95,11 @@ function getMappedAnnotations(annotation, DisplaySetService) {
let displaySet;
+ let SeriesInstanceUID, SOPInstanceUID;
if (targetId.startsWith('imageId:')) {
- const { SOPInstanceUID, SeriesInstanceUID } = getSOPInstanceAttributes(
+ ({ SOPInstanceUID, SeriesInstanceUID } = getSOPInstanceAttributes(
referencedImageId
- );
+ ));
displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
@@ -110,12 +111,13 @@ function getMappedAnnotations(annotation, DisplaySetService) {
throw new Error('Not implemented');
}
- const { SeriesNumber, SeriesInstanceUID } = displaySet;
+ const { SeriesNumber } = displaySet;
const { mean, stdDev, max, area, Modality } = targetStats;
const unit = getModalityUnit(Modality);
annotations.push({
SeriesInstanceUID,
+ SOPInstanceUID,
SeriesNumber,
Modality,
unit,
@@ -177,7 +179,7 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
};
}
-function getDisplayText(mappedAnnotations) {
+function getDisplayText(mappedAnnotations, displaySet) {
if (!mappedAnnotations || !mappedAnnotations.length) {
return '';
}
@@ -185,23 +187,31 @@ function getDisplayText(mappedAnnotations) {
const displayText = [];
// Area is the same for all series
- const { area } = mappedAnnotations[0];
+ const { area, SOPInstanceUID } = mappedAnnotations[0];
+
+ const instance = displaySet.images.find(
+ image => image.SOPInstanceUID === SOPInstanceUID
+ );
+
+ let InstanceNumber;
+ if (instance) {
+ InstanceNumber = instance.InstanceNumber;
+ }
+
const roundedArea = utils.roundNumber(area, 2);
- displayText.push(`Area: ${roundedArea} mm2`);
+ displayText.push(`${roundedArea} mm2`);
+ // Todo: we need a better UI for displaying all these information
mappedAnnotations.forEach(mappedAnnotation => {
- const { mean, unit, max, SeriesNumber } = mappedAnnotation;
+ const { unit, max, SeriesNumber } = mappedAnnotation;
- if (mean && max) {
- const roundedMean = utils.roundNumber(mean, 2);
+ if (max) {
const roundedMax = utils.roundNumber(max, 2);
- // const roundedStdDev = utils.roundNumber(stdDev, 2);
displayText.push(
- `S:${SeriesNumber} - max: ${roundedMax} ${unit}`
- );
- displayText.push(
- `S:${SeriesNumber} - mean: ${roundedMean} ${unit}`
+ InstanceNumber
+ ? `Max: ${roundedMax} ${unit} (S:${SeriesNumber} I:${InstanceNumber})`
+ : `Max: ${roundedMax} ${unit} (S:${SeriesNumber})`
);
}
});
diff --git a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/Length.js b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/Length.js
index 522f5362022..87a688fcc06 100644
--- a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/Length.js
+++ b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/Length.js
@@ -1,6 +1,6 @@
import SUPPORTED_TOOLS from './constants/supportedTools';
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
-import { utils } from '@ohif/core';
+import { DisplaySetService, utils } from '@ohif/core';
const Length = {
toAnnotation: measurement => {},
@@ -60,7 +60,7 @@ const Length = {
DisplaySetService
);
- const displayText = getDisplayText(mappedAnnotations);
+ const displayText = getDisplayText(mappedAnnotations, displaySet);
const getReport = () =>
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
@@ -74,7 +74,7 @@ const Length = {
referenceStudyUID: StudyInstanceUID,
toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
- label: metadata.label,
+ label: data.label,
displayText: displayText,
data: data.cachedStats,
type: getValueTypeFromToolType(toolName),
@@ -99,10 +99,11 @@ function getMappedAnnotations(annotation, DisplaySetService) {
let displaySet;
+ let SeriesInstanceUID, SOPInstanceUID;
if (targetId.startsWith('imageId:')) {
- const { SOPInstanceUID, SeriesInstanceUID } = getSOPInstanceAttributes(
+ ({ SOPInstanceUID, SeriesInstanceUID } = getSOPInstanceAttributes(
referencedImageId
- );
+ ));
displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
@@ -114,12 +115,13 @@ function getMappedAnnotations(annotation, DisplaySetService) {
throw new Error('Not implemented');
}
- const { SeriesNumber, SeriesInstanceUID } = displaySet;
+ const { SeriesNumber } = displaySet;
const { length } = targetStats;
const unit = 'mm';
annotations.push({
SeriesInstanceUID,
+ SOPInstanceUID,
SeriesNumber,
unit,
length,
@@ -167,7 +169,7 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
};
}
-function getDisplayText(mappedAnnotations) {
+function getDisplayText(mappedAnnotations, displaySet) {
if (!mappedAnnotations || !mappedAnnotations.length) {
return '';
}
@@ -175,9 +177,23 @@ function getDisplayText(mappedAnnotations) {
const displayText = [];
// Area is the same for all series
- const { length, SeriesNumber } = mappedAnnotations[0];
+ const { length, SeriesNumber, SOPInstanceUID } = mappedAnnotations[0];
+
+ const instance = displaySet.images.find(
+ image => image.SOPInstanceUID === SOPInstanceUID
+ );
+
+ let InstanceNumber;
+ if (instance) {
+ InstanceNumber = instance.InstanceNumber;
+ }
+
const roundedLength = utils.roundNumber(length, 2);
- displayText.push(`${roundedLength} mm (S: ${SeriesNumber})`);
+ displayText.push(
+ InstanceNumber
+ ? `${roundedLength} mm (S: ${SeriesNumber} I: ${InstanceNumber})`
+ : `${roundedLength} mm (S: ${SeriesNumber})`
+ );
return displayText;
}
diff --git a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.js b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.js
index 6df0f3216f6..63b8b8395db 100644
--- a/extensions/cornerstone-3d/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.js
+++ b/extensions/cornerstone-3d/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.js
@@ -22,6 +22,7 @@ const measurementServiceMappingsFactory = (
ELLIPSE,
RECTANGLE,
BIDIRECTIONAL,
+ POINT,
} = MeasurementService.VALUE_TYPES;
// TODO -> I get why this was attempted, but its not nearly flexible enough.
@@ -32,6 +33,7 @@ const measurementServiceMappingsFactory = (
EllipticalROI: ELLIPSE,
RectangleROI: RECTANGLE,
Bidirectional: BIDIRECTIONAL,
+ ArrowAnnotate: POINT,
};
return TOOL_TYPE_TO_VALUE_TYPE[toolType];
diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json
index abeb6ed474a..9e1d4b06400 100644
--- a/extensions/cornerstone-dicom-sr/package.json
+++ b/extensions/cornerstone-dicom-sr/package.json
@@ -34,7 +34,7 @@
"peerDependencies": {
"@ohif/core": "^3.0.0",
"@ohif/ui": "^2.0.0",
- "dcmjs": "0.22.0",
+ "dcmjs": "^0.22.1",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
@@ -45,7 +45,7 @@
"dependencies": {
"@babel/runtime": "7.16.3",
"classnames": "^2.2.6",
- "@cornerstonejs/core": "^0.8.1",
- "@cornerstonejs/tools": "^0.15.0"
+ "@cornerstonejs/core": "^0.10.2",
+ "@cornerstonejs/tools": "^0.17.2"
}
}
diff --git a/extensions/cornerstone-dicom-sr/src/getSopClassHandlerModule.js b/extensions/cornerstone-dicom-sr/src/getSopClassHandlerModule.js
index 5a5b4c136a1..723256b38f7 100644
--- a/extensions/cornerstone-dicom-sr/src/getSopClassHandlerModule.js
+++ b/extensions/cornerstone-dicom-sr/src/getSopClassHandlerModule.js
@@ -36,7 +36,10 @@ const CodeNameCodeSequenceValues = {
const CodingSchemeDesignators = {
SRT: 'SRT',
- cornerstone3DTools1: Cornerstone3DCodeScheme.CodingSchemeDesignator,
+ CornerstoneCodeSchemes: [
+ Cornerstone3DCodeScheme.CodingSchemeDesignator,
+ 'CST4',
+ ],
};
const RELATIONSHIP_TYPE = {
@@ -463,8 +466,9 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
if (
Finding &&
- Finding.ConceptCodeSequence.CodingSchemeDesignator ===
- CodingSchemeDesignators.cornerstone3DTools1 &&
+ CodingSchemeDesignators.CornerstoneCodeSchemes.includes(
+ Finding.ConceptCodeSequence.CodingSchemeDesignator
+ ) &&
Finding.ConceptCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.CornerstoneFreeText
) {
@@ -478,8 +482,9 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
if (FindingSites.length) {
const cornerstoneFreeTextFindingSite = FindingSites.find(
FindingSite =>
- FindingSite.ConceptCodeSequence.CodingSchemeDesignator ===
- CodingSchemeDesignators.cornerstone3DTools1 &&
+ CodingSchemeDesignators.CornerstoneCodeSchemes.includes(
+ Finding.ConceptCodeSequence.CodingSchemeDesignator
+ ) &&
FindingSite.ConceptCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.CornerstoneFreeText
);
diff --git a/extensions/cornerstone-dicom-sr/src/tools/DICOMSRDisplayTool.ts b/extensions/cornerstone-dicom-sr/src/tools/DICOMSRDisplayTool.ts
index ffa3f896653..809abea6e4a 100644
--- a/extensions/cornerstone-dicom-sr/src/tools/DICOMSRDisplayTool.ts
+++ b/extensions/cornerstone-dicom-sr/src/tools/DICOMSRDisplayTool.ts
@@ -1,4 +1,4 @@
-import { Types } from '@cornerstonejs/core';
+import { Types, metaData, utilities as csUtils } from '@cornerstonejs/core';
import {
AnnotationTool,
annotation,
@@ -100,6 +100,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
const annotationUID = annotation.annotationUID;
const { renderableData } = annotation.data.cachedStats;
const { label, cachedStats } = annotation.data;
+ const { referencedImageId } = annotation.metadata;
styleSpecifier.annotationUID = annotationUID;
@@ -146,6 +147,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
viewport,
renderableDataForGraphicType,
annotationUID,
+ referencedImageId,
options
);
@@ -212,6 +214,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
viewport,
renderableData,
annotationUID,
+ referencedImageId,
options
) {
// Todo: this needs to use the drawPolyLine from cs3D since it is implemented
@@ -247,6 +250,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
viewport,
renderableData,
annotationUID,
+ referencedImageId,
options
) {
let canvasCoordinates;
@@ -270,12 +274,41 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
viewport,
renderableData,
annotationUID,
+ referencedImageId,
options
) {
- let canvasCoordinates;
+ const canvasCoordinates = [];
renderableData.map((data, index) => {
- canvasCoordinates = data.map(p => viewport.worldToCanvas(p));
+ const point = data[0];
+ // This gives us one point for arrow
+ canvasCoordinates.push(viewport.worldToCanvas(point));
+
+ // We get the other point for the arrow by using the image size
+ const imagePixelModule = metaData.get(
+ 'imagePixelModule',
+ referencedImageId
+ );
+
+ let xOffset = 10;
+ let yOffset = 10;
+
+ if (imagePixelModule) {
+ const { columns, rows } = imagePixelModule;
+ xOffset = columns / 10;
+ yOffset = rows / 10;
+ }
+
+ const imagePoint = csUtils.worldToImageCoords(referencedImageId, point);
+ const arrowEnd = csUtils.imageToWorldCoords(referencedImageId, [
+ imagePoint[0] + xOffset,
+ imagePoint[1] + yOffset,
+ ]);
+
+ canvasCoordinates.push(viewport.worldToCanvas(arrowEnd));
+
const arrowUID = `${index}`;
+
+ // Todo: handle drawing probe as probe, currently we are drawing it as an arrow
drawing.drawArrow(
svgDrawingHelper,
annotationUID,
@@ -297,6 +330,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
viewport,
renderableData,
annotationUID,
+ referencedImageId,
options
) {
let canvasCoordinates;
diff --git a/extensions/cornerstone-dicom-sr/src/utils/addMeasurement.ts b/extensions/cornerstone-dicom-sr/src/utils/addMeasurement.ts
index 117e115f171..4146e2f7150 100644
--- a/extensions/cornerstone-dicom-sr/src/utils/addMeasurement.ts
+++ b/extensions/cornerstone-dicom-sr/src/utils/addMeasurement.ts
@@ -6,13 +6,14 @@ import SCOORD_TYPES from '../constants/scoordTypes';
const EPSILON = 1e-4;
+const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
+
export default function addMeasurement(
measurement,
imageId,
displaySetInstanceUID
) {
// TODO -> Render rotated ellipse .
-
const toolName = toolNames.DICOMSRDisplay;
const measurementData = {
@@ -30,7 +31,12 @@ export default function addMeasurement(
}
measurementData.renderableData[GraphicType].push(
- _getRenderableData(GraphicType, GraphicData, imageId)
+ _getRenderableData(
+ GraphicType,
+ GraphicData,
+ imageId,
+ measurement.TrackingIdentifier
+ )
);
});
@@ -75,7 +81,14 @@ export default function addMeasurement(
delete measurement.coords;
}
-function _getRenderableData(GraphicType, GraphicData, imageId) {
+function _getRenderableData(
+ GraphicType,
+ GraphicData,
+ imageId,
+ TrackingIdentifier
+) {
+ const [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
+
let renderableData: cornerstone3D.Types.Point3[];
switch (GraphicType) {
@@ -92,6 +105,7 @@ function _getRenderableData(GraphicType, GraphicData, imageId) {
renderableData.push(worldPos);
}
+
break;
case SCOORD_TYPES.CIRCLE: {
const pointsWorld = [];
diff --git a/extensions/cornerstone-dicom-sr/src/utils/isRehydratable.js b/extensions/cornerstone-dicom-sr/src/utils/isRehydratable.js
index 14ee4377825..44af7d27604 100644
--- a/extensions/cornerstone-dicom-sr/src/utils/isRehydratable.js
+++ b/extensions/cornerstone-dicom-sr/src/utils/isRehydratable.js
@@ -2,6 +2,9 @@ import { adapters } from 'dcmjs';
const cornerstoneAdapters = adapters.Cornerstone3D;
+const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
+const CORNERSTONE_3D_TAG = cornerstoneAdapters.CORNERSTONE_3D_TAG;
+
/**
* Checks if the given `displaySet`can be rehydrated into the `MeasurementService`.
*
@@ -35,9 +38,18 @@ export default function isRehydratable(displaySet, mappings) {
for (let i = 0; i < measurements.length; i++) {
const TrackingIdentifier = measurements[i].TrackingIdentifier;
- const hydratable = adapters.some(adapter =>
- adapter.isValidCornerstoneTrackingIdentifier(TrackingIdentifier)
- );
+ const hydratable = adapters.some(adapter => {
+ let [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
+ if (supportedLegacyCornerstoneTags.includes(cornerstoneTag)) {
+ cornerstoneTag = CORNERSTONE_3D_TAG;
+ }
+
+ const mappedTrackingIdentifier = `${cornerstoneTag}:${toolName}`;
+
+ return adapter.isValidCornerstoneTrackingIdentifier(
+ mappedTrackingIdentifier
+ );
+ });
if (hydratable) {
return true;
diff --git a/extensions/default/package.json b/extensions/default/package.json
index 78dd7aa16be..29365890f84 100644
--- a/extensions/default/package.json
+++ b/extensions/default/package.json
@@ -32,7 +32,7 @@
"peerDependencies": {
"@ohif/core": "^3.0.0",
"@ohif/i18n": "^1.0.0",
- "dcmjs": "0.22.0",
+ "dcmjs": "^0.22.1",
"dicomweb-client": "^0.6.0",
"prop-types": "^15.6.2",
"react": "^17.0.2",
diff --git a/extensions/default/src/Panels/PanelMeasurementTable.tsx b/extensions/default/src/Panels/PanelMeasurementTable.tsx
index 73856e99a35..c076692ca80 100644
--- a/extensions/default/src/Panels/PanelMeasurementTable.tsx
+++ b/extensions/default/src/Panels/PanelMeasurementTable.tsx
@@ -199,12 +199,12 @@ function _getMappedMeasurements(MeasurementService) {
}
function _mapMeasurementToDisplay(measurement, index, types) {
- const { displayText } = measurement;
+ const { displayText, uid, label, type } = measurement;
return {
- uid: measurement.uid,
- label: measurement.label || '(empty)',
- measurementType: measurement.type,
+ uid,
+ label: label || '(empty)',
+ measurementType: type,
displayText: displayText || [],
// TODO: handle one layer down
isActive: false, // activeMeasurementItem === i + 1,
diff --git a/extensions/default/src/ViewerLayout/index.tsx b/extensions/default/src/ViewerLayout/index.tsx
index 063f6ee7dbb..e9f240f0187 100644
--- a/extensions/default/src/ViewerLayout/index.tsx
+++ b/extensions/default/src/ViewerLayout/index.tsx
@@ -58,7 +58,7 @@ function Toolbar({ servicesManager }) {
}
// Also need... to filter list for splitButton, and set primary based on most recently clicked
// Also need to kill the radioGroup button's magic logic
- // Everything should be reactive off these props, so commands can inform ToolBarService
+ // Everything should be reactive off these props, so commands can inform ToolbarService
// These can... Trigger toolbar events based on updates?
// Then sync using useEffect, or simply modify the state here?
@@ -162,11 +162,6 @@ function ViewerLayout({
const getPanelData = id => {
const entry = extensionManager.getModuleEntry(id);
-
- if (!entry) {
- throw new Error(`Could not find module entry for id: ${id}`);
- }
-
// TODO, not sure why sidepanel content has to be JSX, and not a children prop?
const content = entry.component;
@@ -206,11 +201,11 @@ function ViewerLayout({
{/* LEFT SIDEPANELS */}
- {leftPanelComponents.length && (
+ {leftPanelComponents.length ? (
- )}
+ ) : null}
{/* TOOLBAR + GRID */}
-
+
- {rightPanelComponents.length && (
+ {rightPanelComponents.length ? (
- )}
+ ) : null}
);
diff --git a/extensions/default/src/getHangingProtocolModule.js b/extensions/default/src/getHangingProtocolModule.js
index 9a16e66dd38..468d14887ae 100644
--- a/extensions/default/src/getHangingProtocolModule.js
+++ b/extensions/default/src/getHangingProtocolModule.js
@@ -47,241 +47,6 @@ const defaultProtocol = {
numberOfPriorsReferenced: -1,
};
-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: {},
- protocolMatchingRules: [
- {
- id: 'wauZK2QNEfDPwcAQo',
- weight: 1,
- attribute: 'StudyInstanceUID',
- constraint: {
- contains: {
- value: '1.3.6.1.4.',
- },
- },
- required: false,
- },
- {
- 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,
- },
- ],
- toolGroupIds: ['default'],
- stages: [
- {
- id: 'hYbmMy3b7pz7GLiaT',
- name: 'default',
- viewportStructure: {
- layoutType: 'grid',
- properties: {
- rows: 1,
- columns: 1,
- viewportOptions: [],
- },
- },
- displaySets: [
- {
- id: 'ctDisplaySet',
- imageMatchingRules: [],
- seriesMatchingRules: [
- {
- id: 'GPEYqFLv2dwzCM322',
- weight: 1,
- attribute: 'Modality',
- constraint: {
- equals: {
- value: 'CT',
- },
- },
- required: true,
- },
- {
- id: 'vSjk7NCYjtdS3XZAw',
- weight: 1,
- attribute: 'SeriesNumber',
- constraint: {
- equals: {
- value: '4',
- },
- },
- required: false,
- },
- {
- id: 'vSjk7NCYjtdS3XZAw',
- weight: 1,
- attribute: 'SeriesDescription',
- constraint: {
- contains: {
- value: 'CT',
- },
- },
- required: false,
- },
- {
- id: 'vSjk7NCYjtdS3XZAw',
- weight: 1,
- attribute: 'SeriesDescription',
- constraint: {
- contains: {
- value: 'CT WB',
- },
- },
- required: false,
- },
- ],
- studyMatchingRules: [],
- },
- ],
- viewports: [
- {
- id: 'ptACDisplaySet',
- imageMatchingRules: [],
- seriesMatchingRules: [
- {
- id: 'GPEYqFLv2dwzCM322',
- weight: 1,
- attribute: 'Modality',
- constraint: {
- equals: {
- value: 'PT',
- },
- },
- required: true,
- },
- {
- id: 'GPEYqFLv2dwzCM322',
- weight: 1,
- attribute: 'SeriesDescription',
- constraint: {
- contains: {
- value: 'Corrected',
- },
- },
- required: false,
- },
- {
- id: 'GPEYqFLv2dwzCM322',
- weight: 1,
- attribute: 'SeriesDescription',
- constraint: {
- contains: {
- value: 'AC',
- },
- },
- required: false,
- },
- ],
- 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,
- },
- {
- id: 'GPEYqFLv2dwzCM322',
- weight: 1,
- attribute: 'SeriesDescription',
- constraint: {
- contains: {
- value: 'NAC',
- },
- },
- 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,
-};
-
function getHangingProtocolModule() {
return [
{
diff --git a/extensions/dicom-pdf/package.json b/extensions/dicom-pdf/package.json
index ea053b263af..a3a761f95c4 100644
--- a/extensions/dicom-pdf/package.json
+++ b/extensions/dicom-pdf/package.json
@@ -30,7 +30,7 @@
"peerDependencies": {
"@ohif/core": "^3.0.0",
"@ohif/ui": "^2.0.0",
- "dcmjs": "0.22.0",
+ "dcmjs": "^0.22.1",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
diff --git a/extensions/dicom-video/package.json b/extensions/dicom-video/package.json
index 4c3515de683..2053e50a685 100644
--- a/extensions/dicom-video/package.json
+++ b/extensions/dicom-video/package.json
@@ -30,7 +30,7 @@
"peerDependencies": {
"@ohif/core": "^3.0.0",
"@ohif/ui": "^2.0.0",
- "dcmjs": "0.22.0",
+ "dcmjs": "^0.22.1",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json
index c4315981c15..7d0560e81e4 100644
--- a/extensions/measurement-tracking/package.json
+++ b/extensions/measurement-tracking/package.json
@@ -32,10 +32,10 @@
"peerDependencies": {
"@ohif/core": "^3.0.0",
"classnames": "^2.2.6",
- "@cornerstonejs/core": "^0.8.1",
- "@cornerstonejs/tools": "^0.15.0",
+ "@cornerstonejs/core": "^0.10.2",
+ "@cornerstonejs/tools": "^0.17.2",
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
- "dcmjs": "0.22.0",
+ "dcmjs": "^0.22.1",
"prop-types": "^15.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/_hydrateStructuredReport.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/_hydrateStructuredReport.js
index 1266ce7f1c4..f87f2ea5061 100644
--- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/_hydrateStructuredReport.js
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/_hydrateStructuredReport.js
@@ -4,11 +4,13 @@ import getLabelFromDCMJSImportedToolData from './utils/getLabelFromDCMJSImported
import { adapters } from 'dcmjs';
const { guid } = OHIF.utils;
-const { MeasurementReport } = adapters.Cornerstone3D;
+const { MeasurementReport, CORNERSTONE_3D_TAG } = adapters.Cornerstone3D;
const CORNERSTONE_3D_TOOLS_SOURCE_NAME = 'Cornerstone3DTools';
const CORNERSTONE_3D_TOOLS_SOURCE_VERSION = '0.1';
+const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
+
/**
*
*/
@@ -52,14 +54,16 @@ export default function _hydrateStructuredReport(
}
});
+ const datasetToUse = _mapLegacyDataSet(instance);
+
// Use dcmjs to generate toolState.
const storedMeasurementByAnnotationType = MeasurementReport.generateToolState(
- instance,
+ datasetToUse,
// NOTE: we need to pass in the imageIds to dcmjs since the we use them
// for the imageToWorld transformation. The following assumes that the order
// that measurements were added to the display set are the same order as
// the measurementGroups in the instance.
- imageIdsForToolState,
+ sopInstanceUIDToImageId,
cornerstone3D.utilities.imageToWorldCoords,
cornerstone3D.metaData
);
@@ -134,13 +138,12 @@ export default function _hydrateStructuredReport(
} = instance;
const annotation = {
- annotationUID: toolData.uid,
- data: toolData.data,
+ annotationUID: toolData.annotation.annotationUID,
+ data: toolData.annotation.data,
metadata: {
toolName: annotationType,
referencedImageId: imageId,
FrameOfReferenceUID,
- label: '',
},
};
@@ -148,7 +151,7 @@ export default function _hydrateStructuredReport(
CORNERSTONE_3D_TOOLS_SOURCE_NAME,
CORNERSTONE_3D_TOOLS_SOURCE_VERSION
);
- annotation.label = getLabelFromDCMJSImportedToolData(toolData);
+ annotation.data.label = getLabelFromDCMJSImportedToolData(toolData);
const matchingMapping = mappings.find(
m => m.annotationType === annotationType
@@ -175,3 +178,66 @@ export default function _hydrateStructuredReport(
SeriesInstanceUIDs,
};
}
+
+function _mapLegacyDataSet(dataset) {
+ const REPORT = 'Imaging Measurements';
+ const GROUP = 'Measurement Group';
+ const TRACKING_IDENTIFIER = 'Tracking Identifier';
+
+ // Identify the Imaging Measurements
+ const imagingMeasurementContent = toArray(dataset.ContentSequence).find(
+ codeMeaningEquals(REPORT)
+ );
+
+ // Retrieve the Measurements themselves
+ const measurementGroups = toArray(
+ imagingMeasurementContent.ContentSequence
+ ).filter(codeMeaningEquals(GROUP));
+
+ // For each of the supported measurement types, compute the measurement data
+ const measurementData = {};
+
+ const cornerstoneToolClasses =
+ MeasurementReport.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
+
+ const registeredToolClasses = [];
+
+ Object.keys(cornerstoneToolClasses).forEach(key => {
+ registeredToolClasses.push(cornerstoneToolClasses[key]);
+ measurementData[key] = [];
+ });
+
+ measurementGroups.forEach((measurementGroup, index) => {
+ const measurementGroupContentSequence = toArray(
+ measurementGroup.ContentSequence
+ );
+
+ const TrackingIdentifierGroup = measurementGroupContentSequence.find(
+ contentItem =>
+ contentItem.ConceptNameCodeSequence.CodeMeaning === TRACKING_IDENTIFIER
+ );
+
+ const TrackingIdentifier = TrackingIdentifierGroup.TextValue;
+
+ let [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
+ if (supportedLegacyCornerstoneTags.includes(cornerstoneTag)) {
+ cornerstoneTag = CORNERSTONE_3D_TAG;
+ }
+
+ const mappedTrackingIdentifier = `${cornerstoneTag}:${toolName}`;
+
+ TrackingIdentifierGroup.TextValue = mappedTrackingIdentifier;
+ });
+
+ return dataset;
+}
+
+const toArray = function(x) {
+ return Array.isArray(x) ? x : [x];
+};
+
+const codeMeaningEquals = codeMeaningName => {
+ return contentItem => {
+ return contentItem.ConceptNameCodeSequence.CodeMeaning === codeMeaningName;
+ };
+};
diff --git a/modes/basic-dev-mode/src/toolbarButtons.js b/modes/basic-dev-mode/src/toolbarButtons.js
index 3f1cc3d2b3a..2c751204ead 100644
--- a/modes/basic-dev-mode/src/toolbarButtons.js
+++ b/modes/basic-dev-mode/src/toolbarButtons.js
@@ -46,7 +46,7 @@ function _createWwwcPreset(preset, title, subtitle) {
{
commandName: 'setWindowLevel',
commandOptions: {
- windowLevel: windowLevelPresets[preset],
+ ...windowLevelPresets[preset],
},
context: 'CORNERSTONE3D',
},
diff --git a/modes/longitudinal/README.md b/modes/longitudinal/README.md
new file mode 100644
index 00000000000..4b6b907f463
--- /dev/null
+++ b/modes/longitudinal/README.md
@@ -0,0 +1,60 @@
+# Measurement Tracking Mode
+
+
+
+## Introduction
+Measurement tracking mode allows you to:
+
+- Draw annotations and have them shown in the measurement panel
+- Create a report from the tracked measurement and export them as DICOM SR
+- Use already exported DICOM SR to re-hydrate the measurements in the viewer
+
+![preview](https://user-images.githubusercontent.com/7490180/171255703-e6d46da8-8d12-4685-b358-0c8d4d5cb5fe.png)
+
+## Workflow
+
+
+### Status Icon
+Each viewport has a left icon indicating whether the series within the viewport contains:
+
+- tracked measurement OR
+- untracked measurement OR
+- Structured Report OR
+- Locked (uneditable) Structured Report
+
+In the following, we will discuss each category.
+
+![tracked](https://user-images.githubusercontent.com/7490180/171255750-c6903338-c295-4553-b8aa-8cb6a8d63943.png)
+
+### Tracked vs Untracked Measurements
+
+OHIF-v3 implements a workflow for measurement tracking that can be seen below.
+In summary, when you create an annotation, a prompt will be shown whether to start tracking or not. If you start the tracking, the annotation style will change to a solid line, and annotation details get displayed on the measurement panel. On the other hand, if you decline the tracking prompt, the measurement will be considered "temporary," and annotation style remains as a dashed line and not shown on the right panel, and cannot be exported.
+
+Below, you can see different icons that appear for a tracked vs. untracked series in OHIF-v3.
+
+
+![workflow](https://user-images.githubusercontent.com/7490180/171255780-dd249cbf-dd61-4e02-8d46-b91e01d53529.png)
+
+
+### Reading and Writing DICOM SR
+OHIF-v3 provides full support for reading, writing and mapping the DICOM Structured Report (SR) to interactable Cornerstone Tools. When you load an already exported DICOM SR into the viewer, you will be prompted whether to track the measurements for the series or not.
+
+
+![preview](https://user-images.githubusercontent.com/7490180/171255797-6c374780-8e94-4a7f-a125-69b67c18c18c.png)
+
+If you click Yes, DICOM SR measurements gets re-hydrated into the viewer and the series become a tracked series. However, If you say no and later decide to say track the measurements, you can always click on the SR button that will prompt you with the same message again.
+
+
+![restore](https://user-images.githubusercontent.com/7490180/171255813-8d460bd7-e64d-4bce-9467-ad5cf2615c56.png)
+
+The full workflow for saving measurements to SR and loading SR into the viewer is shown below.
+
+![sr-import](https://user-images.githubusercontent.com/7490180/171255826-c308ead6-9dad-4e91-9411-df62658cc839.png)
+
+
+### Loading DICOM SR into an Already Tracked Series
+
+If you have an already tracked series and try to load a DICOM SR measurements, you will be shown the following lock icon. This means that, you can review the DICOM SR measurement, manipulate image and draw "temporary" measurements; however, you cannot edit the DICOM SR measurement.
+
+![locked](https://user-images.githubusercontent.com/7490180/171255842-91b84f91-4e1c-4a20-b4a2-cf9653560c43.png)
diff --git a/modes/longitudinal/assets/locked.png b/modes/longitudinal/assets/locked.png
new file mode 100644
index 00000000000..40e782045b0
Binary files /dev/null and b/modes/longitudinal/assets/locked.png differ
diff --git a/modes/longitudinal/assets/preview.png b/modes/longitudinal/assets/preview.png
new file mode 100644
index 00000000000..2b8cfb39321
Binary files /dev/null and b/modes/longitudinal/assets/preview.png differ
diff --git a/modes/longitudinal/assets/restore.png b/modes/longitudinal/assets/restore.png
new file mode 100644
index 00000000000..cfd6622d2ba
Binary files /dev/null and b/modes/longitudinal/assets/restore.png differ
diff --git a/modes/longitudinal/assets/sr-import.png b/modes/longitudinal/assets/sr-import.png
new file mode 100644
index 00000000000..0d31e4c8dab
Binary files /dev/null and b/modes/longitudinal/assets/sr-import.png differ
diff --git a/modes/longitudinal/assets/tracked.png b/modes/longitudinal/assets/tracked.png
new file mode 100644
index 00000000000..7da69fbaa48
Binary files /dev/null and b/modes/longitudinal/assets/tracked.png differ
diff --git a/modes/longitudinal/assets/workflow.png b/modes/longitudinal/assets/workflow.png
new file mode 100644
index 00000000000..2291ac3ad87
Binary files /dev/null and b/modes/longitudinal/assets/workflow.png differ
diff --git a/modes/longitudinal/src/index.js b/modes/longitudinal/src/index.js
index c6edca659ac..6891f8d6e4f 100644
--- a/modes/longitudinal/src/index.js
+++ b/modes/longitudinal/src/index.js
@@ -54,11 +54,11 @@ function modeFactory({ modeConfiguration }) {
/**
* Lifecycle hooks
*/
- onModeEnter: ({ servicesManager, extensionManager }) => {
+ onModeEnter: ({ servicesManager, extensionManager, commandsManager }) => {
const { ToolBarService, ToolGroupService } = servicesManager.services;
// Init Default and SR ToolGroups
- initToolGroups(extensionManager, ToolGroupService);
+ initToolGroups(extensionManager, ToolGroupService, commandsManager);
let unsubscribe;
diff --git a/modes/longitudinal/src/initToolGroups.js b/modes/longitudinal/src/initToolGroups.js
index e30743bd7d4..3b4b9fdf7c3 100644
--- a/modes/longitudinal/src/initToolGroups.js
+++ b/modes/longitudinal/src/initToolGroups.js
@@ -1,4 +1,8 @@
-function initDefaultToolGroup(extensionManager, ToolGroupService) {
+function initDefaultToolGroup(
+ extensionManager,
+ ToolGroupService,
+ commandsManager
+) {
const utilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone-3d.utilityModule.tools'
);
@@ -36,11 +40,28 @@ function initDefaultToolGroup(extensionManager, ToolGroupService) {
// disabled
};
+ const toolsConfig = {
+ [toolNames.ArrowAnnotate]: {
+ getTextCallback: (callback, eventDetails) =>
+ commandsManager.runCommand('arrowTextCallback', {
+ callback,
+ eventDetails,
+ }),
+
+ changeTextCallback: (data, eventDetails, callback) =>
+ commandsManager.runCommand('arrowTextCallback', {
+ callback,
+ data,
+ eventDetails,
+ }),
+ },
+ };
+
const toolGroupId = 'default';
- ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {});
+ ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, toolsConfig);
}
-function initSRToolGroup(extensionManager, ToolGroupService) {
+function initSRToolGroup(extensionManager, ToolGroupService, commandsManager) {
const SRUtilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone-dicom-sr.utilityModule.tools'
);
@@ -96,13 +117,31 @@ function initSRToolGroup(extensionManager, ToolGroupService) {
],
// disabled
};
+
+ const toolsConfig = {
+ [toolNames.ArrowAnnotate]: {
+ getTextCallback: (callback, eventDetails) =>
+ commandsManager.runCommand('arrowTextCallback', {
+ callback,
+ eventDetails,
+ }),
+
+ changeTextCallback: (data, eventDetails, callback) =>
+ commandsManager.runCommand('arrowTextCallback', {
+ callback,
+ data,
+ eventDetails,
+ }),
+ },
+ };
+
const toolGroupId = 'SRToolGroup';
- ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {});
+ ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, toolsConfig);
}
-function initToolGroups(extensionManager, ToolGroupService) {
- initDefaultToolGroup(extensionManager, ToolGroupService);
- initSRToolGroup(extensionManager, ToolGroupService);
+function initToolGroups(extensionManager, ToolGroupService, commandsManager) {
+ initDefaultToolGroup(extensionManager, ToolGroupService, commandsManager);
+ initSRToolGroup(extensionManager, ToolGroupService, commandsManager);
}
export default initToolGroups;
diff --git a/modes/longitudinal/src/toolbarButtons.js b/modes/longitudinal/src/toolbarButtons.js
index f7e03445489..3abf30e84d0 100644
--- a/modes/longitudinal/src/toolbarButtons.js
+++ b/modes/longitudinal/src/toolbarButtons.js
@@ -46,7 +46,7 @@ function _createWwwcPreset(preset, title, subtitle) {
{
commandName: 'setWindowLevel',
commandOptions: {
- windowLevel: windowLevelPresets[preset],
+ ...windowLevelPresets[preset],
},
context: 'CORNERSTONE3D',
},
diff --git a/platform/core/package.json b/platform/core/package.json
index 7be27b9a783..863589c0c6e 100644
--- a/platform/core/package.json
+++ b/platform/core/package.json
@@ -37,7 +37,7 @@
},
"dependencies": {
"@babel/runtime": "7.16.3",
- "dcmjs": "0.22.0",
+ "dcmjs": "^0.22.1",
"dicomweb-client": "^0.6.0",
"isomorphic-base64": "^1.0.2",
"lodash.merge": "^4.6.1",
diff --git a/platform/core/src/services/HangingProtocolService/ProtocolEngine.js b/platform/core/src/services/HangingProtocolService/ProtocolEngine.js
index 59775f40b09..7649c2e5d82 100644
--- a/platform/core/src/services/HangingProtocolService/ProtocolEngine.js
+++ b/platform/core/src/services/HangingProtocolService/ProtocolEngine.js
@@ -96,7 +96,10 @@ export default class ProtocolEngine {
// We clone it so that we don't accidentally add the
// numberOfPriorsReferenced rule to the Protocol itself.
let rules = protocol.protocolMatchingRules.slice();
- if (!rules) {
+ if (!rules || !rules.length) {
+ console.warn(
+ 'ProtocolEngine::findMatchByStudy no matching rules - specify protocolMatchingRules'
+ );
return;
}
diff --git a/platform/ui/src/components/ViewportGrid/ViewportGrid.tsx b/platform/ui/src/components/ViewportGrid/ViewportGrid.tsx
index 71548ad42c0..79f3ceb2921 100644
--- a/platform/ui/src/components/ViewportGrid/ViewportGrid.tsx
+++ b/platform/ui/src/components/ViewportGrid/ViewportGrid.tsx
@@ -10,7 +10,6 @@ function ViewportGrid({ numRows, numCols, layoutType, children }) {
height: '100%',
width: '100%',
}}
- className="p-2"
>
{children}
diff --git a/platform/ui/src/components/ViewportPane/ViewportPane.tsx b/platform/ui/src/components/ViewportPane/ViewportPane.tsx
index eaa20a5b6fe..39023564c14 100644
--- a/platform/ui/src/components/ViewportPane/ViewportPane.tsx
+++ b/platform/ui/src/components/ViewportPane/ViewportPane.tsx
@@ -64,23 +64,29 @@ function ViewportPane({
onScroll={onInteractionHandler}
onWheel={onInteractionHandler}
className={classnames(
- 'flex flex-col w-full h-full',
- 'rounded-lg hover:border-primary-light transition duration-300 outline-none overflow-hidden',
+ 'w-full h-full rounded-lg overflow-hidden hover:border-primary-light transition duration-300 group',
{
- 'border border-primary-light': isActive,
- 'border border-secondary-light': !isActive,
+ 'border-2 border-primary-light': isActive,
+ 'border-2 border-transparent': !isActive,
},
className
)}
- // Normally, we'd use tailwindcss classes here, but margin and border classes use different units
- // m-# (rem), border-# (px). To make sure we don't change the box size of our viewports
- // and trigger a canvas resize, we have to use this little trick for margin.
- // Assumes a :root font-fize of `16px`
style={{
...customStyle,
}}
>
- {children}
+
+ {children}
+
);
}
diff --git a/platform/viewer/cypress/integration/measurement-tracking/OHIFCornerstoneToolbar.spec.js b/platform/viewer/cypress/integration/measurement-tracking/OHIFCornerstoneToolbar.spec.js
index 5838465f611..8d79e9007bf 100644
--- a/platform/viewer/cypress/integration/measurement-tracking/OHIFCornerstoneToolbar.spec.js
+++ b/platform/viewer/cypress/integration/measurement-tracking/OHIFCornerstoneToolbar.spec.js
@@ -91,7 +91,7 @@ describe('OHIF Cornerstone Toolbar', () => {
.trigger('mousemove', 'right', { buttons: 1, force: true })
.trigger('mouseup', { buttons: 1 });
- const expectedText = 'W:1926L:479';
+ const expectedText = 'W:1930L:479';
cy.get('@viewportInfoTopLeft').should('have.text', expectedText);
});
diff --git a/platform/viewer/package.json b/platform/viewer/package.json
index 25edfb3570f..9196bb9e339 100644
--- a/platform/viewer/package.json
+++ b/platform/viewer/package.json
@@ -1,7 +1,7 @@
{
"name": "@ohif/viewer",
"version": "5.0.0",
- "productVersion": "3.0.8",
+ "productVersion": "3.1.0",
"description": "OHIF Viewer",
"author": "OHIF Contributors",
"license": "MIT",
@@ -62,7 +62,7 @@
"core-js": "^3.16.1",
"cornerstone-math": "^0.1.9",
"cornerstone-wado-image-loader": "^4.1.2",
- "dcmjs": "0.22.0",
+ "dcmjs": "^0.22.1",
"detect-gpu": "^4.0.16",
"dicom-parser": "^1.8.9",
"dotenv-webpack": "^1.7.0",
diff --git a/platform/viewer/src/App.tsx b/platform/viewer/src/App.tsx
index 47a04e50554..9b483af2326 100644
--- a/platform/viewer/src/App.tsx
+++ b/platform/viewer/src/App.tsx
@@ -59,6 +59,7 @@ function App({ config, defaultExtensions, defaultModes }) {
dataSources,
extensionManager,
servicesManager,
+ commandsManager,
hotkeysManager,
routerBasename,
});
diff --git a/platform/viewer/src/components/ViewportGrid.tsx b/platform/viewer/src/components/ViewportGrid.tsx
index 4c2120c4b52..eeac0775cda 100644
--- a/platform/viewer/src/components/ViewportGrid.tsx
+++ b/platform/viewer/src/components/ViewportGrid.tsx
@@ -163,15 +163,7 @@ function ViewerViewportGrid(props) {
* Loading indicator until numCols and numRows are gotten from the HangingProtocolService
*/
if (!numRows || !numCols) {
- return (
-
- );
+ return null;
}
/**
@@ -309,10 +301,10 @@ function ViewerViewportGrid(props) {
onInteraction={onInteractionHandler}
customStyle={{
position: 'absolute',
- top: viewportY * 100 + 0.5 + '%',
- left: viewportX * 100 + 0.5 + '%',
- width: viewportWidth * 100 - 0.5 + '%',
- height: viewportHeight * 100 - 0.5 + '%',
+ top: viewportY * 100 + 0.2 + '%',
+ left: viewportX * 100 + 0.2 + '%',
+ width: viewportWidth * 100 - 0.3 + '%',
+ height: viewportHeight * 100 - 0.3 + '%',
}}
isActive={isActive}
>
diff --git a/platform/viewer/src/routes/CallbackPage.tsx b/platform/viewer/src/routes/CallbackPage.tsx
index 085dbd10a65..74474552fb6 100644
--- a/platform/viewer/src/routes/CallbackPage.tsx
+++ b/platform/viewer/src/routes/CallbackPage.tsx
@@ -11,8 +11,7 @@ function CallbackPage({ userManager, onRedirectSuccess }) {
.then(user => onRedirectSuccess(user))
.catch(error => onRedirectError(error));
- // todo: add i18n (or return null?)
- return Redirecting...
;
+ return null;
}
CallbackPage.propTypes = {
diff --git a/platform/viewer/src/routes/Mode/Mode.tsx b/platform/viewer/src/routes/Mode/Mode.tsx
index 18ccecd74f6..97e8f4f0010 100644
--- a/platform/viewer/src/routes/Mode/Mode.tsx
+++ b/platform/viewer/src/routes/Mode/Mode.tsx
@@ -61,6 +61,7 @@ export default function ModeRoute({
dataSourceName,
extensionManager,
servicesManager,
+ commandsManager,
hotkeysManager,
}) {
// Parse route params/querystring
@@ -206,7 +207,7 @@ export default function ModeRoute({
DisplaySetService.init(extensionManager, sopClassHandlers);
extensionManager.onModeEnter();
- mode?.onModeEnter({ servicesManager, extensionManager });
+ mode?.onModeEnter({ servicesManager, extensionManager, commandsManager });
// Adding hanging protocols of extensions after onModeEnter since
// it will reset the protocols
diff --git a/platform/viewer/src/routes/SignoutCallbackComponent.tsx b/platform/viewer/src/routes/SignoutCallbackComponent.tsx
index 4af3467d0a1..c24a0e0bdda 100644
--- a/platform/viewer/src/routes/SignoutCallbackComponent.tsx
+++ b/platform/viewer/src/routes/SignoutCallbackComponent.tsx
@@ -22,8 +22,7 @@ function SignoutCallbackComponent({ userManager }) {
.then(user => onRedirectSuccess(user))
.catch(error => onRedirectError(error));
- // todo: add i18n
- return Redirecting...
;
+ return null;
}
SignoutCallbackComponent.propTypes = {
diff --git a/platform/viewer/src/routes/buildModeRoutes.tsx b/platform/viewer/src/routes/buildModeRoutes.tsx
index ea72a873991..1447dff8990 100644
--- a/platform/viewer/src/routes/buildModeRoutes.tsx
+++ b/platform/viewer/src/routes/buildModeRoutes.tsx
@@ -27,6 +27,7 @@ export default function buildModeRoutes({
dataSources,
extensionManager,
servicesManager,
+ commandsManager,
hotkeysManager,
}) {
const routes = [];
@@ -58,6 +59,7 @@ export default function buildModeRoutes({
dataSourceName={dataSourceName}
extensionManager={extensionManager}
servicesManager={servicesManager}
+ commandsManager={commandsManager}
hotkeysManager={hotkeysManager}
/>
);
@@ -80,6 +82,7 @@ export default function buildModeRoutes({
dataSourceName={defaultDataSourceName}
extensionManager={extensionManager}
servicesManager={servicesManager}
+ commandsManager={commandsManager}
hotkeysManager={hotkeysManager}
/>
);
diff --git a/platform/viewer/src/routes/index.tsx b/platform/viewer/src/routes/index.tsx
index 8426bea990c..2b1570f673a 100644
--- a/platform/viewer/src/routes/index.tsx
+++ b/platform/viewer/src/routes/index.tsx
@@ -33,6 +33,7 @@ const createRoutes = ({
dataSources,
extensionManager,
servicesManager,
+ commandsManager,
hotkeysManager,
routerBasename,
}) => {
@@ -42,6 +43,7 @@ const createRoutes = ({
dataSources,
extensionManager,
servicesManager,
+ commandsManager,
hotkeysManager,
}) || [];
diff --git a/yarn.lock b/yarn.lock
index 800920efb03..ef5d4aeabc4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1313,28 +1313,28 @@
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjpeg/-/codec-openjpeg-0.1.1.tgz#5bd1c52a33a425299299e970312731fa0cc2711b"
integrity sha512-HOMMOLV6xy8O/agNGGvrl0a8DwShpBvWxAzEzv2pqq12d3r5z/3MyIgNA3Oj/8bIBVvvVXxh9RX7rMDRHJdowg==
-"@cornerstonejs/core@^0.8.1":
- version "0.8.1"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-0.8.1.tgz#27f578a19e12f037bee55aa40b60f626eb26e6a0"
- integrity sha512-ZNQ+uHo8eLi0JTQ5/7MxtX2Joittk7gLZY1BJ5FpPbM4rX+eCoJYNajMz0XybB9Lz89jwGX/3/9i+RGIX3Z7rA==
+"@cornerstonejs/core@^0.10.2", "@cornerstonejs/core@^0.10.3":
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-0.10.3.tgz#5f024bd69ae431c39b40e98c860351f583362df7"
+ integrity sha512-wsTLvRyAxffLBShnNhmFYBuYgUs4uKes9xFzsCiXQfN/bXAV9FWxuvVQLTPL588vp5jju0JHFmwpBXCyaBFnYw==
dependencies:
detect-gpu "^4.0.7"
lodash.clonedeep "4.5.0"
-"@cornerstonejs/streaming-image-volume-loader@^0.2.26":
- version "0.2.26"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-0.2.26.tgz#b7d8ef8e2429647ec7b82522ba52e9ef388e7871"
- integrity sha512-b4WpxA8EkssKr4/yAVW2utZURSWJec+xoaHK2Qlj67+slRKhbSeyaWI2QaPd3exIWEXs0095XN3tKBhoU2Yxlg==
+"@cornerstonejs/streaming-image-volume-loader@^0.3.2":
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-0.3.3.tgz#728bc42b3eeca653c0feffb650617f20f4d6d40f"
+ integrity sha512-XSvbL8kWtwmhwWO3vflaQZfhCnPZyZ/8PAFPypPhIMVbfEqUI5HUMRs4UCPecETyYtF/PfCLjM5AkJNwti7GcA==
dependencies:
- "@cornerstonejs/core" "^0.8.1"
+ "@cornerstonejs/core" "^0.10.3"
cornerstone-wado-image-loader "^4.1.2"
-"@cornerstonejs/tools@^0.15.0":
- version "0.15.2"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-0.15.2.tgz#743dc6a0b5229ddbf58c6f35ac408aba5672f786"
- integrity sha512-cGdOMpRynIMqGOP4q/MpDnClNhmoqbpzkFoPzUnZdP8wxRAObYb70wBTsrg3+3BM+L1GEspVpcZ+KsHX8gM8Fg==
+"@cornerstonejs/tools@^0.17.2":
+ version "0.17.3"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-0.17.3.tgz#90a1121de3bc509a7f2de8680a3025d8ca27f9b9"
+ integrity sha512-ejB/fdPQrfCRPyMOTgd52aQpbLRKnhrMAOQioHOKniQRmzzkZDfEXcgNQYYmVcHin70ytYRMjawUtvOSf6etRg==
dependencies:
- "@cornerstonejs/core" "^0.8.1"
+ "@cornerstonejs/core" "^0.10.3"
lodash.clonedeep "4.5.0"
"@csstools/postcss-color-function@^1.1.0":
@@ -8978,10 +8978,10 @@ dayjs@^1.10.4:
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.2.tgz#fa0f5223ef0d6724b3d8327134890cfe3d72fbe5"
integrity sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==
-dcmjs@0.22.0:
- version "0.22.0"
- resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.22.0.tgz#2b3ce332d0b546461c69f94ada168b2eac726e5a"
- integrity sha512-yIptgOUJyoXqp179OGDq05uJI7d06jb+M7aKunPctaiG/FPPmfkaEXokdJyTV0MwsdRqQr4c7Q/bA8dTCUo9Kg==
+dcmjs@^0.22.1:
+ version "0.22.1"
+ resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.22.1.tgz#45c845f228e5cf00415a5d3470715060bdcb23ae"
+ integrity sha512-pLYxMPx83fChiZrP3XER1ui2a6rKwZFFdlrkO6hdXUvfpossAdkeMuhTJ938usr/O2RZtWqPMBMGKW8YXzG2PA==
dependencies:
"@babel/runtime-corejs2" "^7.17.8"
gl-matrix "^3.1.0"