fix(measurement): fix for measurement jump is not switching series in 2DMPR (#4779)

This commit is contained in:
arul-trenser 2025-03-28 19:44:19 +05:30 committed by GitHub
parent 951f95ef0a
commit 3f06c966eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 100 additions and 26 deletions

View File

@ -264,11 +264,10 @@ const OHIFCornerstoneViewport = React.memo(
const presentations = getViewportPresentations(viewportId, viewportOptions);
let measurement;
if (cacheJumpToMeasurementEvent?.viewportId === viewportId) {
if (cacheJumpToMeasurementEvent?.cornerstoneViewport === viewportId) {
measurement = cacheJumpToMeasurementEvent.measurement;
// Delete the position presentation so that viewport navigates direct
presentations.positionPresentation = null;
cacheJumpToMeasurementEvent = null;
}
// Note: This is a hack to get the grid to re-render the OHIFCornerstoneViewport component
@ -281,7 +280,7 @@ const OHIFCornerstoneViewport = React.memo(
viewportOptions.needsRerendering = false;
}
cornerstoneViewportService.setViewportData(
await cornerstoneViewportService.setViewportData(
viewportId,
viewportData,
viewportOptions,
@ -290,7 +289,13 @@ const OHIFCornerstoneViewport = React.memo(
);
if (measurement) {
cs3DTools.annotation.selection.setAnnotationSelected(measurement.uid);
_checkForCachedJumpToMeasurementEvents(
elementRef,
viewportId,
displaySets,
servicesManager
);
cacheJumpToMeasurementEvent = null;
}
};
@ -318,8 +323,6 @@ const OHIFCornerstoneViewport = React.memo(
servicesManager
);
_checkForCachedJumpToMeasurementEvents(elementRef, viewportId, displaySets, servicesManager);
return () => {
unsubscribeFromJumpToMeasurementEvents();
};
@ -451,6 +454,16 @@ function _subscribeToJumpToMeasurementEvents(elementRef, viewportId, servicesMan
if (cacheJumpToMeasurementEvent.cornerstoneViewport !== viewportId) {
return;
}
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
// Confirm viewportInfo matches measurement displaySetInstanceUID and referencedImageId
if (
!viewportInfo?.contains(
measurement.displaySetInstanceUID,
measurement.referencedImageId || measurement.metadata?.referencedImageId
)
) {
return;
}
_jumpToMeasurement(measurement, elementRef, viewportId, servicesManager);
}
);

View File

@ -372,7 +372,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
* @param publicDisplaySetOptions - The public display set options.
* @param presentations - The presentations to set.
*/
public setViewportData(
public async setViewportData(
viewportId: string,
viewportData: StackViewportData | VolumeViewportData,
publicViewportOptions: PublicViewportOptions,
@ -458,21 +458,14 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
this.viewportsById.set(viewportId, viewportInfo);
const viewport = renderingEngine.getViewport(viewportId);
const displaySetPromise = this._setDisplaySets(
viewport,
viewportData,
viewportInfo,
presentations
);
await this._setDisplaySets(viewport, viewportData, viewportInfo, presentations);
// The broadcast event here ensures that listeners have a valid, up to date
// viewport to access. Doing it too early can result in exceptions or
// invalid data.
displaySetPromise.then(() => {
this._broadcastEvent(this.EVENTS.VIEWPORT_DATA_CHANGED, {
viewportData,
viewportId,
});
this._broadcastEvent(this.EVENTS.VIEWPORT_DATA_CHANGED, {
viewportData,
viewportId,
});
}

View File

@ -6,6 +6,7 @@ import { useViewportGrid } from '@ohif/ui-next';
import EmptyViewport from './EmptyViewport';
import classNames from 'classnames';
import { useAppConfig } from '@state';
import getClosestOrientationFromIOP from '../utils/getClosestOrientationFromIOP';
function ViewerViewportGrid(props: withAppTypes) {
const { servicesManager, viewportComponents = [], dataSource } = props;
@ -177,7 +178,20 @@ function ViewerViewportGrid(props: withAppTypes) {
const { displaySetInstanceUID: referencedDisplaySetInstanceUID } = measurement;
const updatedViewports = _getUpdatedViewports(viewportId, referencedDisplaySetInstanceUID);
if (!updatedViewports[0]) {
let viewportToUpdate;
if (updatedViewports.length > 1) {
// To get the viewport which orientation is closest to IOP
const closestOrientation = getClosestOrientationFromIOP(
displaySetService,
referencedDisplaySetInstanceUID
);
viewportToUpdate = updatedViewports.find(
viewport => viewport.viewportOptions?.orientation == closestOrientation
);
}
// If closest orientation viewport is not find then choose 0
viewportToUpdate = updatedViewports[0];
if (!viewportToUpdate) {
console.warn(
'ViewportGrid::Unable to navigate to viewport containing',
referencedDisplaySetInstanceUID
@ -185,14 +199,9 @@ function ViewerViewportGrid(props: withAppTypes) {
return;
}
// Arbitrarily assign the viewport to element 0
// TODO - this should perform a search to find the most suitable viewport.
updatedViewports[0] = { ...updatedViewports[0] };
const [viewport] = updatedViewports;
// Copy the viewport options to prevent modifying the internal data
viewport.viewportOptions = {
...viewport.viewportOptions,
viewportToUpdate.viewportOptions = {
...viewportToUpdate.viewportOptions,
orientation: 'acquisition',
// The preferred way to jump to the measurement view is to set the
// view reference, as this can hold information such as the orientation

View File

@ -0,0 +1,59 @@
import { vec3 } from 'gl-matrix';
import { Enums } from '@cornerstonejs/core';
import OrientationAxis = Enums.OrientationAxis;
/**
* Get the plane (orientation) to which the ImageOrientationPatient is most closely aligned
*
* @param displaySetService
* @param displaySetInstanceUID
* @returns orientation
*/
export default function getClosestOrientationFromIOP(
displaySetService,
displaySetInstanceUID
): OrientationAxis {
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
const imageOrientationPatient = displaySet.instances[0].ImageOrientationPatient as Array<number>;
// ImageOrientationPatient must be an array of length 6.
if (imageOrientationPatient?.length !== 6) {
return;
}
// Take cross product to get vector coming "out" of image plane
const rowCosineVec = vec3.fromValues(
imageOrientationPatient[0],
imageOrientationPatient[1],
imageOrientationPatient[2]
);
const colCosineVec = vec3.fromValues(
imageOrientationPatient[3],
imageOrientationPatient[4],
imageOrientationPatient[5]
);
const scanAxisNormal = vec3.cross(vec3.create(), rowCosineVec, colCosineVec);
// Define the reference vectors for axial, coronal, and sagittal planes
const unitVectors = {
[OrientationAxis.AXIAL]: vec3.fromValues(0, 0, 1),
[OrientationAxis.CORONAL]: vec3.fromValues(0, 1, 0),
[OrientationAxis.SAGITTAL]: vec3.fromValues(1, 0, 0),
};
// Compute dot products for each reference plane
// Because all vectors are normalized, dot product is bounded between -1 and 1
let maxDot = 0;
let maxOrientation: string = '';
for (const [k, v] of Object.entries(unitVectors)) {
// Absolute value of dot product because we only care about alignment with the axis
// For example, dot product of -1 for a given axis means perfect alignment
// but the image is pointing in the "opposite" direction
const res = Math.abs(vec3.dot(scanAxisNormal, v));
if (res > maxDot) {
maxDot = res;
maxOrientation = k;
}
}
return maxOrientation as OrientationAxis;
}