diff --git a/extensions/measurement-tracking/src/getViewportModule.js b/extensions/measurement-tracking/src/getViewportModule.js
index 65203872e..5991db1f0 100644
--- a/extensions/measurement-tracking/src/getViewportModule.js
+++ b/extensions/measurement-tracking/src/getViewportModule.js
@@ -14,17 +14,17 @@ const OHIFCornerstoneViewport = props => {
function getViewportModule({ servicesManager }) {
const ExtendedOHIFCornerstoneSRViewport = props => {
- const { ToolBarService } = servicesManager.services;
-
return (
-
+
);
};
- return [{ name: 'cornerstone-tracked', component: ExtendedOHIFCornerstoneSRViewport }];
+ return [
+ {
+ name: 'cornerstone-tracked',
+ component: ExtendedOHIFCornerstoneSRViewport,
+ },
+ ];
}
export default getViewportModule;
diff --git a/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js b/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js
index 35a1ee8ef..8c4a2d61b 100644
--- a/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js
+++ b/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js
@@ -44,8 +44,9 @@ function TrackedCornerstoneViewport({
dataSource,
displaySet,
viewportIndex,
- ToolBarService,
+ servicesManager,
}) {
+ const { ToolBarService, DisplaySetService } = servicesManager.services;
const [trackedMeasurements] = useTrackedMeasurements();
const [
{ activeViewportIndex, viewports },
@@ -55,7 +56,10 @@ function TrackedCornerstoneViewport({
const [viewportDialogState, viewportDialogApi] = useViewportDialog();
const [viewportData, setViewportData] = useState(null);
const [element, setElement] = useState(null);
+
const [isTracked, setIsTracked] = useState(false);
+ const [trackedMeasurementId, setTrackedMeasurementId] = useState(null);
+
// TODO: Still needed? Better way than import `OHIF` and destructure?
// Why is this managed by `core`?
useEffect(() => {
@@ -246,14 +250,56 @@ function TrackedCornerstoneViewport({
? _viewportLabels[firstViewportIndexWithMatchingDisplaySetUid]
: '';
+ function switchMeasurement(direction) {
+ if (!element) {
+ // Element not yet enabled.
+ return;
+ }
+
+ const newTrackedMeasurementId = _getNextMeasurementId(
+ direction,
+ servicesManager,
+ trackedMeasurementId,
+ trackedMeasurements
+ );
+
+ if (!newTrackedMeasurementId) {
+ return;
+ }
+
+ setTrackedMeasurementId(newTrackedMeasurementId);
+
+ const { MeasurementService } = servicesManager.services;
+ const measurements = MeasurementService.getMeasurements();
+ const measurement = measurements.find(
+ m => m.id === newTrackedMeasurementId
+ );
+
+ const {
+ displaySetInstanceUID,
+ imageIndex,
+ } = _getViewportDataFromTrackedMeasurementId(
+ measurement,
+ DisplaySetService
+ );
+
+ viewportGridService.setDisplaysetForViewport({
+ viewportIndex,
+ displaySetInstanceUID,
+ imageIndex,
+ });
+ }
+
+ const showNavArrows = isTracked && viewportIndex === activeViewportIndex;
+
return (
<>
alert(`Series ${direction}`)}
- showNavArrows={viewportIndex === activeViewportIndex}
+ onSeriesChange={direction => switchMeasurement(direction)}
+ showNavArrows={showNavArrows}
studyData={{
label,
- isTracked: trackedSeries.includes(SeriesInstanceUID),
+ isTracked,
isLocked: false,
studyDate: formatDate(SeriesDate), // TODO: This is series date. Is that ok?
currentSeries: SeriesNumber,
@@ -270,8 +316,8 @@ function TrackedCornerstoneViewport({
spacing:
PixelSpacing && PixelSpacing.length
? `${PixelSpacing[0].toFixed(2)}mm x ${PixelSpacing[1].toFixed(
- 2
- )}mm`
+ 2
+ )}mm`
: '',
scanner: ManufacturerModelName || '',
},
@@ -379,4 +425,79 @@ async function _getViewportData(dataSource, displaySet) {
return viewportData;
}
+function _getNextMeasurementId(
+ direction,
+ servicesManager,
+ trackedMeasurementId,
+ trackedMeasurements
+) {
+ const { MeasurementService } = servicesManager.services;
+ const measurements = MeasurementService.getMeasurements();
+
+ const { trackedSeries } = trackedMeasurements.context;
+
+ // Get the potentially trackable measurements for this series,
+ // The measurements to jump between are the same
+ // regardless if this series is tracked or not.
+
+ const filteredMeasurements = measurements.filter(m =>
+ trackedSeries.includes(m.referenceSeriesUID)
+ );
+
+ if (!filteredMeasurements.length) {
+ // No measurements on this series.
+ return;
+ }
+
+ const measurementCount = filteredMeasurements.length;
+
+ const ids = filteredMeasurements.map(fm => fm.id);
+ let measurementIndex = ids.findIndex(id => id === trackedMeasurementId);
+
+ if (measurementIndex === -1) {
+ // Not tracking a measurement, or previous measurement now deleted, revert to 0.
+ measurementIndex = 0;
+ } else {
+ if (direction === 'left') {
+ measurementIndex--;
+
+ if (measurementIndex < 0) {
+ measurementIndex = measurementCount - 1;
+ }
+ } else if (direction === 'right') {
+ measurementIndex++;
+
+ if (measurementIndex === measurementCount) {
+ measurementIndex = 0;
+ }
+ }
+ }
+
+ const newTrackedMeasurementId = ids[measurementIndex];
+
+ return newTrackedMeasurementId;
+}
+
+function _getViewportDataFromTrackedMeasurementId(
+ measurement,
+ DisplaySetService
+) {
+ const { referenceSeriesUID: SeriesInstanceUID, SOPInstanceUID } = measurement;
+
+ const activeDisplaySets = DisplaySetService.getActiveDisplaySets();
+
+ const displaySet = activeDisplaySets.find(
+ ds => ds.SeriesInstanceUID === SeriesInstanceUID
+ );
+
+ const imageIndex = displaySet.images.findIndex(
+ image => image.SOPInstanceUID === SOPInstanceUID
+ );
+
+ return {
+ imageIndex,
+ displaySetInstanceUID: displaySet.displaySetInstanceUID,
+ };
+}
+
export default TrackedCornerstoneViewport;