[OHIF 284] - JumpToMeasurement + prevent layout change on scroll Prevent layout change on scroll (#1889)

* No more setting on debounce, jumpToMeasurement service set up.

* Fix bug causing a race condition sometimes if you click super fast.

* Optimise rendering of Tracking viewport slightly.

* element => targetElement

* Remove complete TODO

* Respond to reviewer comments.

* Fix e2e workflow.

* Fix regression that removed highlighting on jump to.

* Make sure jump to works from panel as well as viewport.

* Fix typo and remove debugger.

Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
This commit is contained in:
James Petts 2020-07-22 18:40:40 +01:00 committed by GitHub
parent 4985fbf5a6
commit a4f286feee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 438 additions and 196 deletions

View File

@ -35,7 +35,7 @@
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.18.1", "cornerstone-tools": "4.18.1",
"cornerstone-wado-image-loader": "^3.1.2", "cornerstone-wado-image-loader": "^3.1.2",
"dcmjs": "0.16.0", "dcmjs": "0.16.1",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",

View File

@ -83,8 +83,6 @@ const commandsModule = ({ servicesManager }) => {
const enabledElement = _getActiveViewportsEnabledElement(); const enabledElement = _getActiveViewportsEnabledElement();
if (enabledElement) { if (enabledElement) {
debugger;
const cancelActiveManipulatorsForElement = cornerstoneTools.getModule( const cancelActiveManipulatorsForElement = cornerstoneTools.getModule(
'manipulatorState' 'manipulatorState'
).setters.cancelActiveManipulatorsForElement; ).setters.cancelActiveManipulatorsForElement;
@ -284,32 +282,6 @@ const commandsModule = ({ servicesManager }) => {
cornerstone.setViewport(enabledElement, viewport); cornerstone.setViewport(enabledElement, viewport);
} }
}, },
jumpToImage: ({
StudyInstanceUID,
SOPInstanceUID,
imageIndex,
activeViewportIndex,
}) => {
const study = studyMetadataManager.get(StudyInstanceUID);
const displaySet = study.findDisplaySet(ds => {
return (
ds.images &&
ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
);
});
displaySet.SOPInstanceUID = SOPInstanceUID;
displaySet.imageIndex = imageIndex;
window.store.dispatch(
setViewportSpecificData(activeViewportIndex, displaySet)
);
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
},
}; };
const definitions = { const definitions = {

View File

@ -18,7 +18,11 @@ import { setEnabledElement } from './state';
* @param {Object|Array} configuration.csToolsConfig * @param {Object|Array} configuration.csToolsConfig
*/ */
export default function init({ servicesManager, configuration }) { export default function init({ servicesManager, configuration }) {
const { UIDialogService, MeasurementService } = servicesManager.services; const {
UIDialogService,
MeasurementService,
DisplaySetService,
} = servicesManager.services;
const callInputDialog = (data, event, callback) => { const callInputDialog = (data, event, callback) => {
if (UIDialogService) { if (UIDialogService) {
@ -137,7 +141,7 @@ export default function init({ servicesManager, configuration }) {
); );
/* Measurement Service */ /* Measurement Service */
_connectToolsToMeasurementService(MeasurementService); _connectToolsToMeasurementService(MeasurementService, DisplaySetService);
/* Add extension tools configuration here. */ /* Add extension tools configuration here. */
const internalToolsConfig = { const internalToolsConfig = {
@ -220,21 +224,21 @@ export default function init({ servicesManager, configuration }) {
csTools.setToolEnabled('Overlay', {}); csTools.setToolEnabled('Overlay', {});
} }
const _initMeasurementService = measurementService => { const _initMeasurementService = (MeasurementService, DisplaySetService) => {
/* Initialization */ /* Initialization */
const { const {
Length, Length,
Bidirectional, Bidirectional,
EllipticalRoi, EllipticalRoi,
ArrowAnnotate, ArrowAnnotate,
} = measurementServiceMappingsFactory(measurementService); } = measurementServiceMappingsFactory(MeasurementService, DisplaySetService);
const csToolsVer4MeasurementSource = measurementService.createSource( const csToolsVer4MeasurementSource = MeasurementService.createSource(
'CornerstoneTools', 'CornerstoneTools',
'4' '4'
); );
/* Mappings */ /* Mappings */
measurementService.addMapping( MeasurementService.addMapping(
csToolsVer4MeasurementSource, csToolsVer4MeasurementSource,
'Length', 'Length',
Length.matchingCriteria, Length.matchingCriteria,
@ -242,7 +246,7 @@ const _initMeasurementService = measurementService => {
Length.toMeasurement Length.toMeasurement
); );
measurementService.addMapping( MeasurementService.addMapping(
csToolsVer4MeasurementSource, csToolsVer4MeasurementSource,
'Bidirectional', 'Bidirectional',
Bidirectional.matchingCriteria, Bidirectional.matchingCriteria,
@ -250,7 +254,7 @@ const _initMeasurementService = measurementService => {
Bidirectional.toMeasurement Bidirectional.toMeasurement
); );
measurementService.addMapping( MeasurementService.addMapping(
csToolsVer4MeasurementSource, csToolsVer4MeasurementSource,
'EllipticalRoi', 'EllipticalRoi',
EllipticalRoi.matchingCriteria, EllipticalRoi.matchingCriteria,
@ -258,7 +262,7 @@ const _initMeasurementService = measurementService => {
EllipticalRoi.toMeasurement EllipticalRoi.toMeasurement
); );
measurementService.addMapping( MeasurementService.addMapping(
csToolsVer4MeasurementSource, csToolsVer4MeasurementSource,
'ArrowAnnotate', 'ArrowAnnotate',
ArrowAnnotate.matchingCriteria, ArrowAnnotate.matchingCriteria,
@ -269,12 +273,16 @@ const _initMeasurementService = measurementService => {
return csToolsVer4MeasurementSource; return csToolsVer4MeasurementSource;
}; };
const _connectToolsToMeasurementService = measurementService => { const _connectToolsToMeasurementService = (
MeasurementService,
DisplaySetService
) => {
const csToolsVer4MeasurementSource = _initMeasurementService( const csToolsVer4MeasurementSource = _initMeasurementService(
measurementService MeasurementService,
DisplaySetService
); );
_connectMeasurementServiceToTools( _connectMeasurementServiceToTools(
measurementService, MeasurementService,
csToolsVer4MeasurementSource csToolsVer4MeasurementSource
); );
const { addOrUpdate, remove } = csToolsVer4MeasurementSource; const { addOrUpdate, remove } = csToolsVer4MeasurementSource;
@ -330,9 +338,9 @@ const _connectToolsToMeasurementService = measurementService => {
} }
} }
const { MEASUREMENTS_CLEARED } = measurementService.EVENTS; const { MEASUREMENTS_CLEARED } = MeasurementService.EVENTS;
measurementService.subscribe(MEASUREMENTS_CLEARED, () => { MeasurementService.subscribe(MEASUREMENTS_CLEARED, () => {
cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState( cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState(
{} {}
); );
@ -350,16 +358,16 @@ const _connectToolsToMeasurementService = measurementService => {
}; };
const _connectMeasurementServiceToTools = ( const _connectMeasurementServiceToTools = (
measurementService, MeasurementService,
measurementSource measurementSource
) => { ) => {
const { const {
MEASUREMENTS_CLEARED, MEASUREMENTS_CLEARED,
MEASUREMENT_REMOVED, MEASUREMENT_REMOVED,
} = measurementService.EVENTS; } = MeasurementService.EVENTS;
const sourceId = measurementSource.id; const sourceId = measurementSource.id;
measurementService.subscribe(MEASUREMENTS_CLEARED, () => { MeasurementService.subscribe(MEASUREMENTS_CLEARED, () => {
cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState({}); cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState({});
cornerstone.getEnabledElements().forEach(enabledElement => { cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element); cornerstone.updateImage(enabledElement.element);
@ -367,7 +375,7 @@ const _connectMeasurementServiceToTools = (
}); });
/* TODO: Remove per measurement /* TODO: Remove per measurement
measurementService.subscribe(MEASUREMENT_REMOVED, MeasurementService.subscribe(MEASUREMENT_REMOVED,
({ source, measurement }) => { ({ source, measurement }) => {
if ([sourceId].includes(source.id)) { if ([sourceId].includes(source.id)) {
// const annotation = getAnnotation('Length', measurement.id); // const annotation = getAnnotation('Length', measurement.id);
@ -380,9 +388,9 @@ const _connectMeasurementServiceToTools = (
// const { // const {
// MEASUREMENT_ADDED, // MEASUREMENT_ADDED,
// MEASUREMENT_UPDATED, // MEASUREMENT_UPDATED,
// } = measurementService.EVENTS; // } = MeasurementService.EVENTS;
// measurementService.subscribe( // MeasurementService.subscribe(
// MEASUREMENT_ADDED, // MEASUREMENT_ADDED,
// ({ source, measurement }) => { // ({ source, measurement }) => {
// if (![sourceId].includes(source.id)) { // if (![sourceId].includes(source.id)) {
@ -397,7 +405,7 @@ const _connectMeasurementServiceToTools = (
// } // }
// ); // );
// measurementService.subscribe( // MeasurementService.subscribe(
// MEASUREMENT_UPDATED, // MEASUREMENT_UPDATED,
// ({ source, measurement }) => { // ({ source, measurement }) => {
// if (![sourceId].includes(source.id)) { // if (![sourceId].includes(source.id)) {

View File

@ -6,7 +6,11 @@ const ArrowAnnotate = {
toAnnotation: (measurement, definition) => { toAnnotation: (measurement, definition) => {
// TODO -> Implement when this is needed. // TODO -> Implement when this is needed.
}, },
toMeasurement: (csToolsAnnotation, getValueTypeFromToolType) => { toMeasurement: (
csToolsAnnotation,
DisplaySetService,
getValueTypeFromToolType
) => {
const { element, measurementData } = csToolsAnnotation; const { element, measurementData } = csToolsAnnotation;
const tool = const tool =
csToolsAnnotation.toolType || csToolsAnnotation.toolType ||
@ -26,6 +30,11 @@ const ArrowAnnotate = {
StudyInstanceUID, StudyInstanceUID,
} = getSOPInstanceAttributes(element); } = getSOPInstanceAttributes(element);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID
);
const points = []; const points = [];
points.push(measurementData.handles); points.push(measurementData.handles);
@ -35,6 +44,7 @@ const ArrowAnnotate = {
FrameOfReferenceUID, FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
label: measurementData.text, label: measurementData.text,
description: measurementData.description, description: measurementData.description,
unit: measurementData.unit, unit: measurementData.unit,

View File

@ -5,7 +5,11 @@ const Bidirectional = {
toAnnotation: (measurement, definition) => { toAnnotation: (measurement, definition) => {
// TODO -> Implement when this is needed. // TODO -> Implement when this is needed.
}, },
toMeasurement: (csToolsAnnotation, getValueTypeFromToolType) => { toMeasurement: (
csToolsAnnotation,
DisplaySetService,
getValueTypeFromToolType
) => {
const { element, measurementData } = csToolsAnnotation; const { element, measurementData } = csToolsAnnotation;
const tool = const tool =
csToolsAnnotation.toolType || csToolsAnnotation.toolType ||
@ -25,6 +29,11 @@ const Bidirectional = {
StudyInstanceUID, StudyInstanceUID,
} = getSOPInstanceAttributes(element); } = getSOPInstanceAttributes(element);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID
);
const { handles } = measurementData; const { handles } = measurementData;
const longAxis = [handles.start, handles.end]; const longAxis = [handles.start, handles.end];
@ -36,6 +45,7 @@ const Bidirectional = {
FrameOfReferenceUID, FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
label: measurementData.text, label: measurementData.text,
description: measurementData.description, description: measurementData.description,
unit: measurementData.unit, unit: measurementData.unit,

View File

@ -5,7 +5,11 @@ const EllipticalRoi = {
toAnnotation: (measurement, definition) => { toAnnotation: (measurement, definition) => {
// TODO -> Implement when this is needed. // TODO -> Implement when this is needed.
}, },
toMeasurement: (csToolsAnnotation, getValueTypeFromToolType) => { toMeasurement: (
csToolsAnnotation,
DisplaySetService,
getValueTypeFromToolType
) => {
const { element, measurementData } = csToolsAnnotation; const { element, measurementData } = csToolsAnnotation;
const tool = const tool =
csToolsAnnotation.toolType || csToolsAnnotation.toolType ||
@ -25,6 +29,11 @@ const EllipticalRoi = {
StudyInstanceUID, StudyInstanceUID,
} = getSOPInstanceAttributes(element); } = getSOPInstanceAttributes(element);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID
);
const { start, end } = measurementData.handles; const { start, end } = measurementData.handles;
const halfXLength = Math.abs(start.x - end.x) / 2; const halfXLength = Math.abs(start.x - end.x) / 2;
@ -58,6 +67,7 @@ const EllipticalRoi = {
FrameOfReferenceUID, FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
label: measurementData.text, label: measurementData.text,
description: measurementData.description, description: measurementData.description,
unit: measurementData.unit, unit: measurementData.unit,

View File

@ -37,7 +37,11 @@ const Length = {
* @param {Object} cornerstone Cornerstone event data * @param {Object} cornerstone Cornerstone event data
* @return {Measurement} Measurement instance * @return {Measurement} Measurement instance
*/ */
toMeasurement: (csToolsAnnotation, getValueTypeFromToolType) => { toMeasurement: (
csToolsAnnotation,
DisplaySetService,
getValueTypeFromToolType
) => {
const { element, measurementData } = csToolsAnnotation; const { element, measurementData } = csToolsAnnotation;
const tool = const tool =
csToolsAnnotation.toolType || csToolsAnnotation.toolType ||
@ -57,12 +61,18 @@ const Length = {
StudyInstanceUID, StudyInstanceUID,
} = getSOPInstanceAttributes(element); } = getSOPInstanceAttributes(element);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID
);
return { return {
id: measurementData.id, id: measurementData.id,
SOPInstanceUID: SOPInstanceUID, SOPInstanceUID: SOPInstanceUID,
FrameOfReferenceUID, FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
label: measurementData.text, label: measurementData.text,
description: measurementData.description, description: measurementData.description,
unit: measurementData.unit, unit: measurementData.unit,

View File

@ -3,7 +3,10 @@ import Bidirectional from './Bidirectional';
import ArrowAnnotate from './ArrowAnnotate'; import ArrowAnnotate from './ArrowAnnotate';
import EllipticalRoi from './EllipticalRoi'; import EllipticalRoi from './EllipticalRoi';
const measurementServiceMappingsFactory = measurementService => { const measurementServiceMappingsFactory = (
MeasurementService,
DisplaySetService
) => {
/** /**
* Maps measurement service format object to cornerstone annotation object. * Maps measurement service format object to cornerstone annotation object.
* *
@ -18,7 +21,7 @@ const measurementServiceMappingsFactory = measurementService => {
ELLIPSE, ELLIPSE,
POINT, POINT,
BIDIRECTIONAL, BIDIRECTIONAL,
} = measurementService.VALUE_TYPES; } = MeasurementService.VALUE_TYPES;
// TODO -> I get why this was attemped, but its not nearly flexible enough. // TODO -> I get why this was attemped, but its not nearly flexible enough.
// A single measurement may have an ellipse + a bidirectional measurement, for instances. // A single measurement may have an ellipse + a bidirectional measurement, for instances.
@ -37,10 +40,14 @@ const measurementServiceMappingsFactory = measurementService => {
Length: { Length: {
toAnnotation: Length.toAnnotation, toAnnotation: Length.toAnnotation,
toMeasurement: csToolsAnnotation => toMeasurement: csToolsAnnotation =>
Length.toMeasurement(csToolsAnnotation, _getValueTypeFromToolType), Length.toMeasurement(
csToolsAnnotation,
DisplaySetService,
_getValueTypeFromToolType
),
matchingCriteria: [ matchingCriteria: [
{ {
valueType: measurementService.VALUE_TYPES.POLYLINE, valueType: MeasurementService.VALUE_TYPES.POLYLINE,
points: 2, points: 2,
}, },
], ],
@ -50,17 +57,18 @@ const measurementServiceMappingsFactory = measurementService => {
toMeasurement: csToolsAnnotation => toMeasurement: csToolsAnnotation =>
Bidirectional.toMeasurement( Bidirectional.toMeasurement(
csToolsAnnotation, csToolsAnnotation,
DisplaySetService,
_getValueTypeFromToolType _getValueTypeFromToolType
), ),
matchingCriteria: [ matchingCriteria: [
// TODO -> We should eventually do something like shortAxis + longAxis, // TODO -> We should eventually do something like shortAxis + longAxis,
// But its still a little unclear how these automatic interpretations will work. // But its still a little unclear how these automatic interpretations will work.
{ {
valueType: measurementService.VALUE_TYPES.POLYLINE, valueType: MeasurementService.VALUE_TYPES.POLYLINE,
points: 2, points: 2,
}, },
{ {
valueType: measurementService.VALUE_TYPES.POLYLINE, valueType: MeasurementService.VALUE_TYPES.POLYLINE,
points: 2, points: 2,
}, },
], ],
@ -70,11 +78,12 @@ const measurementServiceMappingsFactory = measurementService => {
toMeasurement: csToolsAnnotation => toMeasurement: csToolsAnnotation =>
ArrowAnnotate.toMeasurement( ArrowAnnotate.toMeasurement(
csToolsAnnotation, csToolsAnnotation,
DisplaySetService,
_getValueTypeFromToolType _getValueTypeFromToolType
), ),
matchingCriteria: [ matchingCriteria: [
{ {
valueType: measurementService.VALUE_TYPES.POINT, valueType: MeasurementService.VALUE_TYPES.POINT,
points: 1, points: 1,
}, },
], ],
@ -84,11 +93,12 @@ const measurementServiceMappingsFactory = measurementService => {
toMeasurement: csToolsAnnotation => toMeasurement: csToolsAnnotation =>
EllipticalRoi.toMeasurement( EllipticalRoi.toMeasurement(
csToolsAnnotation, csToolsAnnotation,
DisplaySetService,
_getValueTypeFromToolType _getValueTypeFromToolType
), ),
matchingCriteria: [ matchingCriteria: [
{ {
valueType: measurementService.VALUE_TYPES.ELLIPSE, valueType: MeasurementService.VALUE_TYPES.ELLIPSE,
}, },
], ],
}, },

View File

@ -34,7 +34,7 @@
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"webpack": "^4.0.0", "webpack": "^4.0.0",
"dcmjs": "0.16.0" "dcmjs": "0.16.1"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "7.7.6" "@babel/runtime": "7.7.6"

View File

@ -28,7 +28,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"dcmjs": "0.16.0", "dcmjs": "0.16.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.11.0", "react": "^16.11.0",
"react-dom": "^16.11.0" "react-dom": "^16.11.0"

View File

@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.18.1", "cornerstone-tools": "4.18.1",
"dcmjs": "0.16.0", "dcmjs": "0.16.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6" "react-dom": "^16.8.6"

View File

@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.18.1", "cornerstone-tools": "4.18.1",
"dcmjs": "0.16.0", "dcmjs": "0.16.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6" "react-dom": "^16.8.6"

View File

@ -35,7 +35,7 @@
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.18.1", "cornerstone-tools": "4.18.1",
"cornerstone-wado-image-loader": "^3.1.2", "cornerstone-wado-image-loader": "^3.1.2",
"dcmjs": "0.16.0", "dcmjs": "0.16.1",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",

View File

@ -372,6 +372,7 @@ function OHIFCornerstoneSRViewport({
const toMeasurementSchema = getToolStateToCornerstoneMeasurementSchema( const toMeasurementSchema = getToolStateToCornerstoneMeasurementSchema(
toolType, toolType,
MeasurementService, MeasurementService,
DisplaySetService,
imageId imageId
); );
@ -436,8 +437,8 @@ function OHIFCornerstoneSRViewport({
spacing: spacing:
PixelSpacing && PixelSpacing.length PixelSpacing && PixelSpacing.length
? `${PixelSpacing[0].toFixed(2)}mm x ${PixelSpacing[1].toFixed( ? `${PixelSpacing[0].toFixed(2)}mm x ${PixelSpacing[1].toFixed(
2 2
)}mm` )}mm`
: '', : '',
scanner: ManufacturerModelName || '', scanner: ManufacturerModelName || '',
}, },

View File

@ -1,6 +1,7 @@
export default function getToolStateToCornerstoneMeasurementSchema( export default function getToolStateToCornerstoneMeasurementSchema(
toolType, toolType,
MeasurementService, MeasurementService,
DisplaySetService,
imageId imageId
) { ) {
const _getValueTypeFromToolType = toolType => { const _getValueTypeFromToolType = toolType => {
@ -28,20 +29,45 @@ export default function getToolStateToCornerstoneMeasurementSchema(
switch (toolType) { switch (toolType) {
case 'Length': case 'Length':
return measurementData => return measurementData =>
Length(measurementData, imageId, _getValueTypeFromToolType); Length(
measurementData,
imageId,
DisplaySetService,
_getValueTypeFromToolType
);
case 'Bidirectional': case 'Bidirectional':
return measurementData => return measurementData =>
Bidirectional(measurementData, imageId, _getValueTypeFromToolType); Bidirectional(
measurementData,
imageId,
DisplaySetService,
_getValueTypeFromToolType
);
case 'EllipticalRoi': case 'EllipticalRoi':
return measurementData => return measurementData =>
EllipticalRoi(measurementData, imageId, _getValueTypeFromToolType); EllipticalRoi(
measurementData,
imageId,
DisplaySetService,
_getValueTypeFromToolType
);
case 'ArrowAnnotate': case 'ArrowAnnotate':
return measurementData => return measurementData =>
ArrowAnnotate(measurementData, imageId, _getValueTypeFromToolType); ArrowAnnotate(
measurementData,
imageId,
DisplaySetService,
_getValueTypeFromToolType
);
} }
} }
function Length(measurementData, imageId, _getValueTypeFromToolType) { function Length(
measurementData,
imageId,
DisplaySetService,
_getValueTypeFromToolType
) {
const tool = measurementData.toolType || measurementData.toolName; const tool = measurementData.toolType || measurementData.toolName;
const instance = cornerstone.metaData.get('instance', imageId); const instance = cornerstone.metaData.get('instance', imageId);
const { const {
@ -51,6 +77,12 @@ function Length(measurementData, imageId, _getValueTypeFromToolType) {
StudyInstanceUID, StudyInstanceUID,
} = instance; } = instance;
const displaySetInstanceUID = _getDisplaySetInstanceUID(
DisplaySetService,
SeriesInstanceUID,
SOPInstanceUID
);
const { handles, label } = measurementData; const { handles, label } = measurementData;
const points = []; const points = [];
@ -69,7 +101,7 @@ function Length(measurementData, imageId, _getValueTypeFromToolType) {
FrameOfReferenceUID, FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
label: measurementData.text, displaySetInstanceUID,
description: measurementData.description, description: measurementData.description,
unit: measurementData.unit, unit: measurementData.unit,
length: measurementData.length, length: measurementData.length,
@ -79,7 +111,12 @@ function Length(measurementData, imageId, _getValueTypeFromToolType) {
}; };
} }
function Bidirectional(measurementData, imageId, _getValueTypeFromToolType) { function Bidirectional(
measurementData,
imageId,
DisplaySetService,
_getValueTypeFromToolType
) {
const tool = measurementData.toolType || measurementData.toolName; const tool = measurementData.toolType || measurementData.toolName;
const instance = cornerstone.metaData.get('instance', imageId); const instance = cornerstone.metaData.get('instance', imageId);
const { const {
@ -88,6 +125,11 @@ function Bidirectional(measurementData, imageId, _getValueTypeFromToolType) {
SeriesInstanceUID, SeriesInstanceUID,
StudyInstanceUID, StudyInstanceUID,
} = instance; } = instance;
const displaySetInstanceUID = _getDisplaySetInstanceUID(
DisplaySetService,
SeriesInstanceUID,
SOPInstanceUID
);
const { handles, label } = measurementData; const { handles, label } = measurementData;
@ -100,7 +142,7 @@ function Bidirectional(measurementData, imageId, _getValueTypeFromToolType) {
FrameOfReferenceUID, FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
label: measurementData.text, displaySetInstanceUID,
description: measurementData.description, description: measurementData.description,
unit: measurementData.unit, unit: measurementData.unit,
shortestDiameter: measurementData.shortestDiameter, shortestDiameter: measurementData.shortestDiameter,
@ -111,7 +153,12 @@ function Bidirectional(measurementData, imageId, _getValueTypeFromToolType) {
}; };
} }
function EllipticalRoi(measurementData, imageId, _getValueTypeFromToolType) { function EllipticalRoi(
measurementData,
imageId,
DisplaySetService,
_getValueTypeFromToolType
) {
const tool = measurementData.toolType || measurementData.toolName; const tool = measurementData.toolType || measurementData.toolName;
const instance = cornerstone.metaData.get('instance', imageId); const instance = cornerstone.metaData.get('instance', imageId);
const { const {
@ -121,6 +168,11 @@ function EllipticalRoi(measurementData, imageId, _getValueTypeFromToolType) {
StudyInstanceUID, StudyInstanceUID,
} = instance; } = instance;
const displaySetInstanceUID = _getDisplaySetInstanceUID(
DisplaySetService,
SeriesInstanceUID,
SOPInstanceUID
);
const { handles, label } = measurementData; const { handles, label } = measurementData;
const { start, end } = handles; const { start, end } = handles;
@ -155,7 +207,7 @@ function EllipticalRoi(measurementData, imageId, _getValueTypeFromToolType) {
FrameOfReferenceUID, FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
label: measurementData.text, displaySetInstanceUID,
description: measurementData.description, description: measurementData.description,
unit: measurementData.unit, unit: measurementData.unit,
area: area:
@ -168,7 +220,12 @@ function EllipticalRoi(measurementData, imageId, _getValueTypeFromToolType) {
}; };
} }
function ArrowAnnotate(measurementData, imageId, _getValueTypeFromToolType) { function ArrowAnnotate(
measurementData,
imageId,
DisplaySetService,
_getValueTypeFromToolType
) {
const tool = measurementData.toolType || measurementData.toolName; const tool = measurementData.toolType || measurementData.toolName;
const instance = cornerstone.metaData.get('instance', imageId); const instance = cornerstone.metaData.get('instance', imageId);
const { const {
@ -178,6 +235,12 @@ function ArrowAnnotate(measurementData, imageId, _getValueTypeFromToolType) {
StudyInstanceUID, StudyInstanceUID,
} = instance; } = instance;
const displaySetInstanceUID = _getDisplaySetInstanceUID(
DisplaySetService,
SeriesInstanceUID,
SOPInstanceUID
);
const { handles } = measurementData; const { handles } = measurementData;
const points = []; const points = [];
@ -196,12 +259,25 @@ function ArrowAnnotate(measurementData, imageId, _getValueTypeFromToolType) {
FrameOfReferenceUID, FrameOfReferenceUID,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
displaySetInstanceUID,
label: measurementData.text, label: measurementData.text,
description: measurementData.description, description: measurementData.description,
unit: measurementData.unit, unit: measurementData.unit,
text: measurementData.text, text: measurementData.text,
type: _getValueTypeFromToolType(tool), type: _getValueTypeFromToolType(tool),
points, points,
label,
}; };
} }
function _getDisplaySetInstanceUID(
DisplaySetService,
SeriesInstanceUID,
SOPInstanceUID
) {
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID
);
return displaySet.displaySetInstanceUID;
}

View File

@ -29,7 +29,7 @@
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-tools": "4.18.1", "cornerstone-tools": "4.18.1",
"dcmjs": "0.16.0", "dcmjs": "0.16.1",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",

View File

@ -161,29 +161,7 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
} }
const jumpToImage = ({ id, isActive }) => { const jumpToImage = ({ id, isActive }) => {
const measurement = MeasurementService.getMeasurement(id); MeasurementService.jumpToMeasurement(viewportGrid.activeViewportIndex, id);
const { referenceSeriesUID, SOPInstanceUID } = measurement;
setCornerstoneMeasurementActive(measurement);
const displaySets = DisplaySetService.getDisplaySetsForSeries(
referenceSeriesUID
);
const displaySet = displaySets.find(ds => {
return (
ds.images && ds.images.some(i => i.SOPInstanceUID === SOPInstanceUID)
);
});
const imageIndex = displaySet.images
.map(i => i.SOPInstanceUID)
.indexOf(SOPInstanceUID);
viewportGridService.setDisplaysetForViewport({
viewportIndex: viewportGrid.activeViewportIndex,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
imageIndex,
});
onMeasurementItemClickHandler({ id, isActive }); onMeasurementItemClickHandler({ id, isActive });
}; };

View File

@ -16,6 +16,7 @@ import ViewportOverlay from './ViewportOverlay';
import ViewportLoadingIndicator from './ViewportLoadingIndicator'; import ViewportLoadingIndicator from './ViewportLoadingIndicator';
import setCornerstoneMeasurementActive from '../_shared/setCornerstoneMeasurementActive'; import setCornerstoneMeasurementActive from '../_shared/setCornerstoneMeasurementActive';
const scrollToIndex = cornerstoneTools.importInternal('util/scrollToIndex');
const { formatDate } = utils; const { formatDate } = utils;
// TODO -> Get this list from the list of tracked measurements. // TODO -> Get this list from the list of tracked measurements.
@ -47,12 +48,13 @@ function TrackedCornerstoneViewport({
viewportIndex, viewportIndex,
servicesManager, servicesManager,
}) { }) {
const { ToolBarService, DisplaySetService } = servicesManager.services; const {
ToolBarService,
DisplaySetService,
MeasurementService,
} = servicesManager.services;
const [trackedMeasurements] = useTrackedMeasurements(); const [trackedMeasurements] = useTrackedMeasurements();
const [ const [{ activeViewportIndex, viewports }] = useViewportGrid();
{ activeViewportIndex, viewports },
viewportGridService,
] = useViewportGrid();
// viewportIndex, onSubmit // viewportIndex, onSubmit
const [viewportDialogState, viewportDialogApi] = useViewportDialog(); const [viewportDialogState, viewportDialogApi] = useViewportDialog();
const [viewportData, setViewportData] = useState(null); const [viewportData, setViewportData] = useState(null);
@ -69,6 +71,28 @@ function TrackedCornerstoneViewport({
}; };
}, []); }, []);
useEffect(() => {
const unsubcribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents(
MeasurementService,
DisplaySetService,
element,
viewportIndex,
displaySet.displaySetInstanceUID
);
_checkForCachedJumpToMeasurementEvents(
MeasurementService,
DisplaySetService,
element,
viewportIndex,
displaySet.displaySetInstanceUID
);
return () => {
unsubcribeFromJumpToMeasurementEvents();
};
}, [element, displaySet]);
useEffect(() => { useEffect(() => {
if (!element) { if (!element) {
return; return;
@ -167,13 +191,6 @@ function TrackedCornerstoneViewport({
); );
} }
/*
* This grabs `imageIndex from first matching
* We actually want whichever is at our `viewportIndex`
*/
const { imageIndex } = viewports[viewportIndex];
displaySet.imageIndex = imageIndex;
_getViewportData(dataSource, displaySet).then(setViewportData); _getViewportData(dataSource, displaySet).then(setViewportData);
}, [dataSource, displaySet, viewports, viewportIndex]); }, [dataSource, displaySet, viewports, viewportIndex]);
@ -275,19 +292,10 @@ function TrackedCornerstoneViewport({
setCornerstoneMeasurementActive(measurement); setCornerstoneMeasurementActive(measurement);
const { MeasurementService.jumpToMeasurement(
displaySetInstanceUID,
imageIndex,
} = _getViewportDataFromTrackedMeasurementId(
measurement,
DisplaySetService
);
viewportGridService.setDisplaysetForViewport({
viewportIndex, viewportIndex,
displaySetInstanceUID, newTrackedMeasurementId
imageIndex, );
});
} }
const showNavArrows = isTracked && viewportIndex === activeViewportIndex; const showNavArrows = isTracked && viewportIndex === activeViewportIndex;
@ -336,14 +344,6 @@ function TrackedCornerstoneViewport({
viewportIndex={viewportIndex} viewportIndex={viewportIndex}
imageIds={imageIds} imageIds={imageIds}
imageIdIndex={currentImageIdIndex} imageIdIndex={currentImageIdIndex}
onNewImageDebounceTime={700}
onNewImageDebounced={({ currentImageIdIndex }) => {
viewportGridService.setDisplaysetForViewport({
viewportIndex: activeViewportIndex,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
imageIndex: currentImageIdIndex,
});
}}
// Sync resize throttle w/ sidepanel animation duration to prevent // Sync resize throttle w/ sidepanel animation duration to prevent
// seizure inducing strobe blinking effect // seizure inducing strobe blinking effect
resizeRefreshRateMs={150} resizeRefreshRateMs={150}
@ -402,16 +402,12 @@ const _viewportLabels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];
* @return {Object} CornerstoneTools Stack * @return {Object} CornerstoneTools Stack
*/ */
function _getCornerstoneStack(displaySet, dataSource) { function _getCornerstoneStack(displaySet, dataSource) {
const { imageIndex } = displaySet;
// Get stack from Stack Manager // Get stack from Stack Manager
const storedStack = StackManager.findOrCreateStack(displaySet, dataSource); const storedStack = StackManager.findOrCreateStack(displaySet, dataSource);
// Clone the stack here so we don't mutate it // Clone the stack here so we don't mutate it
const stack = Object.assign({}, storedStack); const stack = Object.assign({}, storedStack);
stack.currentImageIdIndex = imageIndex;
return stack; return stack;
} }
@ -498,26 +494,111 @@ function _getNextMeasurementId(
return newTrackedMeasurementId; return newTrackedMeasurementId;
} }
function _getViewportDataFromTrackedMeasurementId( 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, measurement,
targetElement,
viewportIndex,
MeasurementService,
DisplaySetService DisplaySetService
) { ) {
const { referenceSeriesUID: SeriesInstanceUID, SOPInstanceUID } = measurement; const { displaySetInstanceUID, SOPInstanceUID } = measurement;
const activeDisplaySets = DisplaySetService.getActiveDisplaySets(); const referencedDisplaySet = DisplaySetService.getDisplaySetByUID(
displaySetInstanceUID
const displaySet = activeDisplaySets.find(
ds => ds.SeriesInstanceUID === SeriesInstanceUID
); );
const imageIndex = displaySet.images.findIndex( const imageIndex = referencedDisplaySet.images.findIndex(
image => image.SOPInstanceUID === SOPInstanceUID i => i.SOPInstanceUID === SOPInstanceUID
); );
return { setCornerstoneMeasurementActive(measurement);
imageIndex,
displaySetInstanceUID: displaySet.displaySetInstanceUID, if (targetElement !== null) {
}; const enabledElement = cornerstone.getEnabledElement(targetElement);
if (enabledElement.image) {
// 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(
'cornerstoneimagerendered',
scrollToHandler
);
};
targetElement.addEventListener(
'cornerstoneimagerendered',
scrollToHandler
);
cornerstone.updateImage(targetElement);
}
// Jump to measurement consumed, remove.
MeasurementService.removeJumpToMeasurement(viewportIndex);
}
} }
export default TrackedCornerstoneViewport; export default TrackedCornerstoneViewport;

View File

@ -33,7 +33,7 @@
"@ohif/ui": "^2.0.0", "@ohif/ui": "^2.0.0",
"cornerstone-core": "^2.3.0", "cornerstone-core": "^2.3.0",
"cornerstone-wado-image-loader": "^3.1.2", "cornerstone-wado-image-loader": "^3.1.2",
"dcmjs": "0.16.0", "dcmjs": "0.16.1",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"i18next": "^17.0.3", "i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1", "i18next-browser-languagedetector": "^3.0.1",

View File

@ -38,7 +38,7 @@
"dependencies": { "dependencies": {
"@babel/runtime": "7.7.6", "@babel/runtime": "7.7.6",
"ajv": "^6.10.0", "ajv": "^6.10.0",
"dcmjs": "0.16.0", "dcmjs": "0.16.1",
"dicomweb-client": "^0.6.0", "dicomweb-client": "^0.6.0",
"immer": "6.0.2", "immer": "6.0.2",
"isomorphic-base64": "^1.0.2", "isomorphic-base64": "^1.0.2",

View File

@ -46,6 +46,20 @@ export default class DisplaySetService {
); );
}; };
getDisplaySetForSOPInstanceUID(SOPInstanceUID, SeriesInstanceUID) {
const displaySets = SeriesInstanceUID
? this.getDisplaySetsForSeries(SeriesInstanceUID)
: this.getDisplaySetCache();
const displaySet = displaySets.find(ds => {
return (
ds.images && ds.images.some(i => i.SOPInstanceUID === SOPInstanceUID)
);
});
return displaySet;
}
/** /**
* @param {string} displaySetInstanceUID * @param {string} displaySetInstanceUID
* @returns {object} displaySet * @returns {object} displaySet

View File

@ -53,6 +53,7 @@ const EVENTS = {
MEASUREMENT_ADDED: 'event::measurement_added', MEASUREMENT_ADDED: 'event::measurement_added',
MEASUREMENT_REMOVED: 'event::measurement_removed', MEASUREMENT_REMOVED: 'event::measurement_removed',
MEASUREMENTS_CLEARED: 'event::measurements_cleared', MEASUREMENTS_CLEARED: 'event::measurements_cleared',
JUMP_TO_MEASUREMENT: 'event:jump_to_measurement',
}; };
const VALUE_TYPES = { const VALUE_TYPES = {
@ -70,6 +71,7 @@ class MeasurementService {
this.mappings = {}; this.mappings = {};
this.measurements = {}; this.measurements = {};
this.listeners = {}; this.listeners = {};
this._jumpToMeasurementCache = {};
Object.defineProperty(this, 'EVENTS', { Object.defineProperty(this, 'EVENTS', {
value: EVENTS, value: EVENTS,
writable: false, writable: false,
@ -498,6 +500,40 @@ class MeasurementService {
this._broadcastChange(this.EVENTS.MEASUREMENTS_CLEARED); this._broadcastChange(this.EVENTS.MEASUREMENTS_CLEARED);
} }
jumpToMeasurement(viewportIndex, id) {
const measurement = this.measurements[id];
if (!measurement) {
log.warn(`No id provided, or unable to find measurement by id.`);
return;
}
this._addJumpToMeasurement(viewportIndex, id);
const eventName = this.EVENTS.JUMP_TO_MEASUREMENT;
const hasListeners = Object.keys(this.listeners).length > 0;
const hasCallbacks = Array.isArray(this.listeners[eventName]);
if (hasListeners && hasCallbacks) {
this.listeners[eventName].forEach(listener => {
listener.callback({ viewportIndex, measurement });
});
}
}
_addJumpToMeasurement(viewportIndex, id) {
this._jumpToMeasurementCache[viewportIndex] = id;
}
getJumpToMeasurement(viewportIndex) {
return this._jumpToMeasurementCache[viewportIndex];
}
removeJumpToMeasurement(viewportIndex) {
delete this._jumpToMeasurementCache[viewportIndex];
}
_getMappingByMeasurementSource(measurementId, definition) { _getMappingByMeasurementSource(measurementId, definition) {
const measurement = this.getMeasurement(measurementId); const measurement = this.getMeasurement(measurementId);
if (this._isValidSource(measurement.source)) { if (this._isValidSource(measurement.source)) {

View File

@ -27,14 +27,10 @@ export function ViewportGridProvider({ children, service }) {
return { ...state, ...{ activeViewportIndex: action.payload } }; return { ...state, ...{ activeViewportIndex: action.payload } };
} }
case 'SET_DISPLAYSET_FOR_VIEWPORT': { case 'SET_DISPLAYSET_FOR_VIEWPORT': {
const { const { viewportIndex, displaySetInstanceUID } = action.payload;
viewportIndex,
displaySetInstanceUID,
imageIndex,
} = action.payload;
const viewports = state.viewports.slice(); const viewports = state.viewports.slice();
viewports[viewportIndex] = { displaySetInstanceUID, imageIndex }; viewports[viewportIndex] = { displaySetInstanceUID };
return { ...state, ...{ viewports }, cachedLayout: null }; return { ...state, ...{ viewports }, cachedLayout: null };
} }
@ -63,7 +59,7 @@ export function ViewportGridProvider({ children, service }) {
numCols: 1, numCols: 1,
numRows: 1, numRows: 1,
activeViewportIndex: 0, activeViewportIndex: 0,
viewports: [{ displaySetInstanceUID: null, imageIndex: null }], viewports: [{ displaySetInstanceUID: null }],
cachedLayout: null, cachedLayout: null,
}; };
} }
@ -96,13 +92,12 @@ export function ViewportGridProvider({ children, service }) {
[dispatch] [dispatch]
); );
const setDisplaysetForViewport = useCallback( const setDisplaysetForViewport = useCallback(
({ viewportIndex, displaySetInstanceUID, imageIndex }) => ({ viewportIndex, displaySetInstanceUID }) =>
dispatch({ dispatch({
type: 'SET_DISPLAYSET_FOR_VIEWPORT', type: 'SET_DISPLAYSET_FOR_VIEWPORT',
payload: { payload: {
viewportIndex, viewportIndex,
displaySetInstanceUID, displaySetInstanceUID,
imageIndex,
}, },
}), }),
[dispatch] [dispatch]

View File

@ -70,7 +70,7 @@
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.18.1", "cornerstone-tools": "4.18.1",
"cornerstone-wado-image-loader": "^3.1.2", "cornerstone-wado-image-loader": "^3.1.2",
"dcmjs": "0.16.0", "dcmjs": "0.16.1",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"dicomweb-client": "^0.4.4", "dicomweb-client": "^0.4.4",
"dotenv-webpack": "^1.7.0", "dotenv-webpack": "^1.7.0",

View File

@ -1,7 +1,7 @@
/** /**
* CSS Grid Reference: http://grid.malven.co/ * CSS Grid Reference: http://grid.malven.co/
*/ */
import React, { useEffect } from 'react'; import React, { useEffect, useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui'; import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui';
import EmptyViewport from './EmptyViewport'; import EmptyViewport from './EmptyViewport';
@ -11,13 +11,18 @@ import classNames from 'classnames';
function ViewerViewportGrid(props) { function ViewerViewportGrid(props) {
const { servicesManager, viewportComponents, dataSource } = props; const { servicesManager, viewportComponents, dataSource } = props;
const [ const [viewportGrid, viewportGridService] = useViewportGrid();
{ numCols, numRows, activeViewportIndex, viewports, cachedLayout },
viewportGridService, const {
] = useViewportGrid(); numCols,
numRows,
activeViewportIndex,
viewports,
cachedLayout,
} = viewportGrid;
// TODO -> Need some way of selecting which displaySets hit the viewports. // TODO -> Need some way of selecting which displaySets hit the viewports.
const { DisplaySetService } = servicesManager.services; const { DisplaySetService, MeasurementService } = servicesManager.services;
useEffect(() => { useEffect(() => {
const { unsubscribe } = DisplaySetService.subscribe( const { unsubscribe } = DisplaySetService.subscribe(
@ -40,30 +45,56 @@ function ViewerViewportGrid(props) {
}; };
}, []); }, []);
// TODO -> Make a HangingProtocolService useEffect(() => {
const HangingProtocolService = displaySets => { const { unsubscribe } = MeasurementService.subscribe(
let displaySetInstanceUID; MeasurementService.EVENTS.JUMP_TO_MEASUREMENT,
({ viewportIndex, measurement }) => {
const referencedDisplaySetInstanceUID =
measurement.displaySetInstanceUID;
// Fallback // If the viewport does not contain the displaySet, then hang that displaySet.
if (!displaySets || !displaySets.length) { if (
const displaySet = DisplaySetService.activeDisplaySets[0]; viewports[viewportIndex].displaySetInstanceUID !==
displaySetInstanceUID = displaySet.displaySetInstanceUID; referencedDisplaySetInstanceUID
} else { ) {
const displaySet = displaySets[0]; viewportGridService.setDisplaysetForViewport({
displaySetInstanceUID = displaySet.displaySetInstanceUID; viewportIndex,
} displaySetInstanceUID: referencedDisplaySetInstanceUID,
});
}
}
);
return { return () => {
numRows: 1, unsubscribe();
numCols: 1,
activeViewportIndex: 0,
viewports: [
{
displaySetInstanceUID,
},
],
}; };
}; }, [viewports]);
// TODO -> Make a HangingProtocolService
// Commented out whilst not in use to avoid pointlessly regenerating this function.
// const HangingProtocolService = displaySets => {
// let displaySetInstanceUID;
// // Fallback
// if (!displaySets || !displaySets.length) {
// const displaySet = DisplaySetService.activeDisplaySets[0];
// displaySetInstanceUID = displaySet.displaySetInstanceUID;
// } else {
// const displaySet = displaySets[0];
// displaySetInstanceUID = displaySet.displaySetInstanceUID;
// }
// return {
// numRows: 1,
// numCols: 1,
// activeViewportIndex: 0,
// viewports: [
// {
// displaySetInstanceUID,
// },
// ],
// };
// };
const onDoubleClick = viewportIndex => { const onDoubleClick = viewportIndex => {
// TODO -> Disabled for now. // TODO -> Disabled for now.

View File

@ -7364,10 +7364,10 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
dcmjs@0.16.0: dcmjs@0.16.1:
version "0.16.0" version "0.16.1"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.16.0.tgz#714755d006c3a49cf65ef13e627ff7a23151d35a" resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.16.1.tgz#76bc61cbdf2c58a2f54990080729ca2d7ee19543"
integrity sha512-41nY3Isg5i5FoSd1s67aUgUHi146r+fb8JuiRvDsxvm7MEkkdjIzMYMtxjsrVwKRW1xRH+x34nxbV9+8cc7WQw== integrity sha512-t6vsKi5QXzwX1fwnHY2hdU99FfyzmK8aO0OSBDH6XvJNrBp2A6HpoVWbucybOy8eStIPDqs4v0FGP/m39cTCRA==
dependencies: dependencies:
"@babel/polyfill" "^7.8.3" "@babel/polyfill" "^7.8.3"
"@babel/runtime" "^7.8.4" "@babel/runtime" "^7.8.4"