fix: prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID (#5630)
* fix: prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID * refactor: extract viewport update strategies * Update version and use nearest orientation viewport --------- Co-authored-by: trenser-belbin <belbin.kunjumon@prenuvo.com> Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
This commit is contained in:
13 files changed
+322
-261
No files matched your search
@@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.14.4",
|
||||
"@cornerstonejs/core": "4.14.4",
|
||||
"@cornerstonejs/adapters": "4.14.5",
|
||||
"@cornerstonejs/core": "4.14.5",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"react-color": "2.19.3"
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.14.4",
|
||||
"@cornerstonejs/core": "4.14.4",
|
||||
"@cornerstonejs/adapters": "4.14.5",
|
||||
"@cornerstonejs/core": "4.14.5",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"react-color": "2.19.3"
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.14.4",
|
||||
"@cornerstonejs/core": "4.14.4",
|
||||
"@cornerstonejs/tools": "4.14.4",
|
||||
"@cornerstonejs/adapters": "4.14.5",
|
||||
"@cornerstonejs/core": "4.14.5",
|
||||
"@cornerstonejs/tools": "4.14.5",
|
||||
"classnames": "2.5.1"
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.14.4",
|
||||
"@cornerstonejs/tools": "4.14.4",
|
||||
"@cornerstonejs/core": "4.14.5",
|
||||
"@cornerstonejs/tools": "4.14.5",
|
||||
"classnames": "2.5.1"
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.14.4",
|
||||
"@cornerstonejs/dicom-image-loader": "4.14.5",
|
||||
"@ohif/core": "3.12.0-beta.109",
|
||||
"@ohif/ui": "3.12.0-beta.109",
|
||||
"dcmjs": "0.45.0",
|
||||
@@ -53,12 +53,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.14.4",
|
||||
"@cornerstonejs/ai": "4.14.4",
|
||||
"@cornerstonejs/core": "4.14.4",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.14.4",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.14.4",
|
||||
"@cornerstonejs/tools": "4.14.4",
|
||||
"@cornerstonejs/adapters": "4.14.5",
|
||||
"@cornerstonejs/ai": "4.14.5",
|
||||
"@cornerstonejs/core": "4.14.5",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.14.5",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.14.5",
|
||||
"@cornerstonejs/tools": "4.14.5",
|
||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"html2canvas": "1.4.1",
|
||||
|
||||
+104
-43
@@ -37,6 +37,7 @@ import { useLutPresentationStore } from '../../stores/useLutPresentationStore';
|
||||
import { usePositionPresentationStore } from '../../stores/usePositionPresentationStore';
|
||||
import { useSynchronizersStore } from '../../stores/useSynchronizersStore';
|
||||
import { useSegmentationPresentationStore } from '../../stores/useSegmentationPresentationStore';
|
||||
import getClosestOrientationFromIOP from '../../utils/isReferenceViewable';
|
||||
|
||||
const EVENTS = {
|
||||
VIEWPORT_DATA_CHANGED: 'event::cornerstoneViewportService:viewportDataChanged',
|
||||
@@ -634,67 +635,118 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
|
||||
/**
|
||||
* Figures out which viewport to update when the viewport type needs to change.
|
||||
* This may not be the active viewport if there is already a viewport showing
|
||||
* the display set, but in the wrong orientation.
|
||||
*
|
||||
* The viewport will need to update the viewport type and/or display set to
|
||||
* display the resulting data.
|
||||
*
|
||||
* The first choice will be a viewport already showing the correct display set,
|
||||
* but showing it as a stack.
|
||||
*
|
||||
* Second choice is to see if there is a viewport already showing the right
|
||||
* orientation for the image, but the wrong display set. This fixes the
|
||||
* case where the user is in MPR and a viewport other than active should be
|
||||
* the one to change to display the iamge.
|
||||
*
|
||||
* Final choice is to use the provide activeViewportId. This will cover
|
||||
* changes to/from video and wsi viewports and other cases where no
|
||||
* viewport is really even close to being able to display the measurement.
|
||||
* Orchestrates the search strategies in order of preference.
|
||||
*/
|
||||
public findUpdateableViewportConfiguration(activeViewportId: string, measurement) {
|
||||
const { metadata, displaySetInstanceUID } = measurement;
|
||||
const { volumeId, referencedImageId } = metadata;
|
||||
const { displaySetService, viewportGridService } = this.servicesManager.services;
|
||||
const { displaySetService } = this.servicesManager.services;
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
|
||||
// 1. Determine the target Viewport Type (Stack vs Volume)
|
||||
const viewportType = this.determineTargetViewportType(displaySet, metadata);
|
||||
|
||||
// 2. Strategy: Find viewport already showing this volume
|
||||
const volumeMatch = this.findViewportShowingVolume(
|
||||
metadata,
|
||||
displaySetInstanceUID,
|
||||
viewportType
|
||||
);
|
||||
if (volumeMatch) {
|
||||
return volumeMatch;
|
||||
}
|
||||
|
||||
// 3. Strategy: Find viewport with compatible orientation (even if different display set)
|
||||
const compatibleMatch = this.findViewportConvertibleToVolume(
|
||||
metadata,
|
||||
displaySetInstanceUID,
|
||||
viewportType
|
||||
);
|
||||
if (compatibleMatch) {
|
||||
return compatibleMatch;
|
||||
}
|
||||
|
||||
// 4. Strategy: Find viewport with matching orientation via IOP
|
||||
const orientationMatch = this.findViewportWithMatchingOrientation(
|
||||
displaySetInstanceUID,
|
||||
viewportType
|
||||
);
|
||||
if (orientationMatch) {
|
||||
return orientationMatch;
|
||||
}
|
||||
|
||||
// 5. Fallback: Use the active viewport
|
||||
return {
|
||||
viewportId: activeViewportId,
|
||||
displaySetInstanceUID,
|
||||
viewportOptions: { viewportType },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the viewport should be what is specified in
|
||||
* the viewportType of the display set, or stack if the display
|
||||
* set isn't reconstructable and there is a referenced image id, otherwise
|
||||
* volume.
|
||||
*
|
||||
* Expect there to be more rules in the future for different types of annotations/settings
|
||||
* such as 3d annotations.
|
||||
*/
|
||||
public determineTargetViewportType(displaySet, metadata): string {
|
||||
let { viewportType } = displaySet;
|
||||
|
||||
if (!viewportType) {
|
||||
if (referencedImageId && !displaySet.isReconstructable) {
|
||||
if (metadata.referencedImageId && !displaySet.isReconstructable) {
|
||||
viewportType = csEnums.ViewportType.STACK;
|
||||
} else if (volumeId) {
|
||||
} else if (metadata.volumeId) {
|
||||
viewportType = 'volume';
|
||||
}
|
||||
}
|
||||
return viewportType;
|
||||
}
|
||||
|
||||
// Find viewports that could be updated to be volumes to show this view
|
||||
// That prefers a viewport already showing the right display set.
|
||||
if (volumeId) {
|
||||
for (const id of this.viewportsById.keys()) {
|
||||
const viewport = this.getCornerstoneViewport(id);
|
||||
if (viewport?.isReferenceViewable(metadata, { asVolume: true, withNavigation: true })) {
|
||||
return {
|
||||
viewportId: id,
|
||||
displaySetInstanceUID,
|
||||
viewportOptions: { viewportType },
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Find viewports that could be updated to be volumes to show this view.
|
||||
* Prefers a viewport already showing the right display set.
|
||||
*/
|
||||
public findViewportShowingVolume(metadata, displaySetInstanceUID, viewportType) {
|
||||
if (!metadata.volumeId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find a viewport in the correct orientation showing a different display set
|
||||
// which could be used to display the annotation.
|
||||
for (const id of this.viewportsById.keys()) {
|
||||
const viewport = this.getCornerstoneViewport(id);
|
||||
if (viewport?.isReferenceViewable(metadata, { asVolume: true, withNavigation: true })) {
|
||||
return {
|
||||
viewportId: id,
|
||||
displaySetInstanceUID,
|
||||
viewportOptions: { viewportType },
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a viewport that could be converted to a volume to show this annotation,
|
||||
* already showing the right display set.
|
||||
*/
|
||||
public findViewportConvertibleToVolume(metadata, displaySetInstanceUID, viewportType) {
|
||||
const { viewportGridService } = this.servicesManager.services;
|
||||
const altMetadata = { ...metadata, volumeId: null, referencedImageId: null };
|
||||
|
||||
for (const id of this.viewportsById.keys()) {
|
||||
const viewport = this.getCornerstoneViewport(id);
|
||||
const viewportDisplaySetUID = viewportGridService.getDisplaySetsUIDsForViewport(id)?.[0];
|
||||
|
||||
if (!viewportDisplaySetUID || !viewport) {
|
||||
continue;
|
||||
}
|
||||
if (volumeId) {
|
||||
|
||||
if (metadata.volumeId) {
|
||||
altMetadata.volumeId = viewportDisplaySetUID;
|
||||
}
|
||||
altMetadata.FrameOfReferenceUID = this._getFrameOfReferenceUID(viewportDisplaySetUID);
|
||||
|
||||
if (viewport.isReferenceViewable(altMetadata, { asVolume: true, withNavigation: true })) {
|
||||
return {
|
||||
viewportId: id,
|
||||
@@ -703,13 +755,22 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Just display in the active viewport
|
||||
return {
|
||||
viewportId: activeViewportId,
|
||||
displaySetInstanceUID,
|
||||
viewportOptions: { viewportType },
|
||||
};
|
||||
/**
|
||||
* Find a viewport with the closest orientation but on a different display set.
|
||||
*/
|
||||
public findViewportWithMatchingOrientation(metadata, displaySetInstanceUID, viewportType) {
|
||||
const viewportAlignmentData = this.getViewportAlignmentData(metadata);
|
||||
if (viewportAlignmentData?.length) {
|
||||
return {
|
||||
...viewportAlignmentData[0],
|
||||
displaySetInstanceUID,
|
||||
viewportOptions: { viewportType },
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "4.14.4",
|
||||
"@cornerstonejs/tools": "4.14.4",
|
||||
"@cornerstonejs/core": "4.14.5",
|
||||
"@cornerstonejs/tools": "4.14.5",
|
||||
"@ohif/core": "3.12.0-beta.109",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.109",
|
||||
"@ohif/extension-default": "3.12.0-beta.109",
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.14.4",
|
||||
"@cornerstonejs/tools": "4.14.4",
|
||||
"@cornerstonejs/core": "4.14.5",
|
||||
"@cornerstonejs/tools": "4.14.5",
|
||||
"@ohif/core": "3.12.0-beta.109",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.109",
|
||||
"@ohif/extension-default": "3.12.0-beta.109",
|
||||
|
||||
Reference in new issue
Block a user