From e0a31caebfee5c58269f3820a014d18822ec8975 Mon Sep 17 00:00:00 2001 From: Erik Ziegler Date: Wed, 25 Nov 2020 13:35:43 +0100 Subject: [PATCH] fix: Call load if necessary before rehydration prompt. Jump to first measurement on rehydration (OHIF-369) (#2184) --- .../dicom-sr/src/getSopClassHandlerModule.js | 4 +- .../dicom-sr/src/utils/isRehydratable.js | 2 +- .../viewports/OHIFCornerstoneSRViewport.js | 2 + .../setCornerstoneMeasurementActive.js | 2 +- .../TrackedMeasurementsContext.jsx | 46 +++++++++++++------ .../measurementTrackingMachine.js | 6 +-- .../viewports/TrackedCornerstoneViewport.js | 25 +++++----- package.json | 5 +- yarn.lock | 16 ++----- 9 files changed, 59 insertions(+), 49 deletions(-) diff --git a/extensions/dicom-sr/src/getSopClassHandlerModule.js b/extensions/dicom-sr/src/getSopClassHandlerModule.js index 2882460f5..cc877c409 100644 --- a/extensions/dicom-sr/src/getSopClassHandlerModule.js +++ b/extensions/dicom-sr/src/getSopClassHandlerModule.js @@ -122,9 +122,7 @@ function _load(displaySet, servicesManager, extensionManager) { ); displaySet.isHydrated = false; - displaySet.isRehydratable = isRehydratable(displaySet, mappings) - ? true - : false; + displaySet.isRehydratable = isRehydratable(displaySet, mappings); displaySet.isLoaded = true; // Check currently added displaySets and add measurements if the sources exist. diff --git a/extensions/dicom-sr/src/utils/isRehydratable.js b/extensions/dicom-sr/src/utils/isRehydratable.js index 750d184e0..cb95b288a 100644 --- a/extensions/dicom-sr/src/utils/isRehydratable.js +++ b/extensions/dicom-sr/src/utils/isRehydratable.js @@ -3,7 +3,7 @@ import { adapters } from 'dcmjs'; const cornerstoneAdapters = adapters.Cornerstone; /** - * Checks if the given `displySet`can be rehydrated into the `MeasurementService`. + * Checks if the given `displaySet`can be rehydrated into the `MeasurementService`. * * @param {object} displaySet The SR `displaySet` to check. * @param {object[]} mappings The CornerstoneTools 4 mappings to the `MeasurementService`. diff --git a/extensions/dicom-sr/src/viewports/OHIFCornerstoneSRViewport.js b/extensions/dicom-sr/src/viewports/OHIFCornerstoneSRViewport.js index ea6d15c04..f5dde8f2f 100644 --- a/extensions/dicom-sr/src/viewports/OHIFCornerstoneSRViewport.js +++ b/extensions/dicom-sr/src/viewports/OHIFCornerstoneSRViewport.js @@ -205,6 +205,8 @@ function OHIFCornerstoneSRViewport({ setTrackingUniqueIdentifiersForElement(targetElement); setElement(targetElement); + // TODO: Enabled Element appears to be incorrect here, it should be called + // 'element' since it is the DOM element, not the enabledElement object const OHIFCornerstoneEnabledElementEvent = new CustomEvent( 'ohif-cornerstone-enabled-element-event', { diff --git a/extensions/measurement-tracking/src/_shared/setCornerstoneMeasurementActive.js b/extensions/measurement-tracking/src/_shared/setCornerstoneMeasurementActive.js index efa243878..b9b689f08 100644 --- a/extensions/measurement-tracking/src/_shared/setCornerstoneMeasurementActive.js +++ b/extensions/measurement-tracking/src/_shared/setCornerstoneMeasurementActive.js @@ -3,7 +3,7 @@ import cornerstoneTools from 'cornerstone-tools'; const { globalImageIdSpecificToolStateManager } = cornerstoneTools; -export default function setMeasurementActive(measurement) { +export default function setCornerstoneMeasurementActive(measurement) { const { id } = measurement; const toolState = globalImageIdSpecificToolStateManager.saveToolState(); diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.jsx b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.jsx index fb6252cbe..f8293766f 100644 --- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.jsx +++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.jsx @@ -18,6 +18,8 @@ const TrackedMeasurementsContext = React.createContext(); TrackedMeasurementsContext.displayName = 'TrackedMeasurementsContext'; const useTrackedMeasurements = () => useContext(TrackedMeasurementsContext); +const SR_SOPCLASSHANDLERID = "org.ohif.dicom-sr.sopClassHandlerModule.dicom-sr"; + /** * * @param {*} param0 @@ -31,21 +33,19 @@ function TrackedMeasurementsContextProvider( const machineOptions = Object.assign({}, defaultOptions); machineOptions.actions = Object.assign({}, machineOptions.actions, { - showSeriesInActiveViewport: (ctx, evt) => { - const { DisplaySetService } = servicesManager.services; - const displaySetsForHydratedSeries = DisplaySetService.getDisplaySetsForSeries( - ctx.trackedSeries[0] + jumpToFirstMeasurementInActiveViewport: (ctx, evt) => { + const { DisplaySetService, MeasurementService } = servicesManager.services; + const { trackedStudy, trackedSeries } = ctx; + const measurements = MeasurementService.getMeasurements(); + const trackedMeasurements = measurements.filter( + m => + trackedStudy === m.referenceStudyUID && + trackedSeries.includes(m.referenceSeriesUID) ); - if (displaySetsForHydratedSeries.length > 0) { - const firstDisplaySetInstanceUID = - displaySetsForHydratedSeries[0].displaySetInstanceUID; + const id = trackedMeasurements[0].id; - viewportGridService.setDisplaysetForViewport({ - viewportIndex: evt.data.viewportIndex, - displaySetInstanceUID: firstDisplaySetInstanceUID, - }); - } + MeasurementService.jumpToMeasurement(viewportGrid.activeViewportIndex, id); }, showStructuredReportDisplaySetInActiveViewport: (ctx, evt) => { if (evt.data.createdDisplaySetInstanceUIDs.length > 0) { @@ -136,11 +136,29 @@ function TrackedMeasurementsContextProvider( activeViewport.displaySetInstanceUID ); + // If this is an SR produced by our SR SOPClassHandler, + // and it hasn't been loaded yet, do that now so we + // can check if it can be rehydrated or not. + // + // Note: This happens: + // - If the viewport is not currently an OHIFCornerstoneSRViewport + // - If the displaySet has never been hung + // + // Otherwise, the displaySet will be loaded by the useEffect handler + // listening to displaySet changes inside OHIFCornerstoneSRViewport. + // The issue here is that this handler in TrackedMeasurementsContext + // ends up occurring before the Viewport is created, so the displaySet + // is not loaded yet, and isRehydratable is undefined unless we call load(). + if (displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID && + !displaySet.isLoaded && + displaySet.load) { + displaySet.load(); + } + // Magic string // load function added by our sopClassHandler module if ( - displaySet.SOPClassHandlerId === - 'org.ohif.dicom-sr.sopClassHandlerModule.dicom-sr' && + displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID && displaySet.isRehydratable === true ) { console.log('sending event...', trackedMeasurements); diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js index ccc267a91..4c2d96776 100644 --- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js +++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js @@ -211,7 +211,7 @@ const machineConfiguration = { target: 'tracking', actions: [ 'setTrackedStudyAndMultipleSeries', - 'showSeriesInActiveViewport', + 'jumpToFirstMeasurementInActiveViewport', 'setIsDirtyToClean', ], cond: 'shouldHydrateStructuredReport', @@ -248,8 +248,8 @@ const defaultOptions = { clearAllMeasurements: (ctx, evt) => { console.log('clearAllMeasurements: not implemented'); }, - showSeriesInActiveViewport: (ctx, evt) => { - console.warn('showSeriesInActiveViewport: not implemented'); + jumpToFirstMeasurementInActiveViewport: (ctx, evt) => { + console.warn('jumpToFirstMeasurementInActiveViewport: not implemented'); }, showStructuredReportDisplaySetInActiveViewport: (ctx, evt) => { console.warn( diff --git a/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js b/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js index 9199a83fb..865b41090 100644 --- a/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js +++ b/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js @@ -78,7 +78,7 @@ function TrackedCornerstoneViewport({ }, [viewportIndex]); useEffect(() => { - const unsubcribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents( + const unsubscribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents( MeasurementService, DisplaySetService, element, @@ -95,7 +95,7 @@ function TrackedCornerstoneViewport({ ); return () => { - unsubcribeFromJumpToMeasurementEvents(); + unsubscribeFromJumpToMeasurementEvents(); }; }, [element, displaySet]); @@ -605,19 +605,20 @@ function _jumpToMeasurement( 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( + // 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 + ); + + if (enabledElement.image) { cornerstone.updateImage(targetElement); } diff --git a/package.json b/package.json index 8bc1aeaec..2f6f03103 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,8 @@ "docs:preview": "lerna run docs:preview --stream", "docs:publish": "chmod +x ./build-and-publish-docs.sh && ./build-and-publish-docs.sh", "release": "yarn run lerna:version && yarn run lerna:publish", - "lerna:cache": "[ $NETLIFY_RESTORE = 1 ] && ./netlify-lerna-cache.sh || :", - "lerna:restore": "[ $NETLIFY_RESTORE = 1 ] && ./netlify-lerna-restore.sh || :", - "preinstall": "yarn lerna:restore", + "lerna:cache": "./netlify-lerna-cache.sh", + "lerna:restore": "./netlify-lerna-restore.sh", "lerna:version": "npx lerna version prerelease --force-publish", "lerna:publish": "lerna publish from-package --canary --dist-tag canary", "link-list": "npm ls --depth=0 --link=true" diff --git a/yarn.lock b/yarn.lock index c1d215159..1c203988a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7907,18 +7907,10 @@ cornerstone-math@^0.1.8: resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5" integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw== -cornerstone-tools@4.20.1: - version "4.20.1" - resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.20.1.tgz#90f6c28356622eac037683b19f9175f85d33dff7" - integrity sha512-SyQsrg3bnGHUvWGZpUq7ia7/gj12S9zQFK2VMhpyAYxK9pWz+PRv8HBi0eEe/fWGAo/Vc62IRkdP3uF9MJlqiQ== - dependencies: - "@babel/runtime" "7.1.2" - cornerstone-math "0.1.7" - -cornerstone-tools@^4.22.0: - version "4.22.0" - resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.22.0.tgz#760f895ed1a02ec457dda23f8cb6abb20df77378" - integrity sha512-EKUvTqwiN6i5AwPOTvc/iiWEqOZUzEOX7hM8kBnV8E7G2o+0jAEQkNqy+3J+TpBUWHQ3qF2G1Hktmi60tspwow== +cornerstone-tools@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-5.1.2.tgz#d2a03341c837c9288e2b70e314136d31e85c8c46" + integrity sha512-MrOXOxkEuLPHd6cqJ0mwU+R9OaNk08XC1yKjUS9dF6UbIq9HdwFJ6Fj0rcIA2MXYYjVN6tFwMM8H+0k7R3KxCA== dependencies: "@babel/runtime" "7.1.2" cornerstone-math "0.1.7"