99 lines
3.2 KiB
TypeScript
99 lines
3.2 KiB
TypeScript
import { vec3 } from 'gl-matrix';
|
|
import { Enums } from '@cornerstonejs/core';
|
|
|
|
import OrientationAxis = Enums.OrientationAxis;
|
|
|
|
export const isReferenceViewable = ({
|
|
viewportId,
|
|
reference,
|
|
viewportOptions,
|
|
servicesManager,
|
|
}) => {
|
|
const { cornerstoneViewportService, displaySetService } = servicesManager.services;
|
|
|
|
if (!viewportOptions) {
|
|
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
|
|
|
// we can make a customization for this to allow
|
|
const isViewable = viewport.isReferenceViewable(reference, {
|
|
withNavigation: true,
|
|
});
|
|
|
|
return isViewable;
|
|
}
|
|
|
|
if (viewportOptions.viewportType === 'stack') {
|
|
// we only need the viewport to include the referenced imageId
|
|
const displaySet = displaySetService.getDisplaySetByUID(reference.displaySetInstanceUID);
|
|
const imageIds = displaySet.instances.map(instance => instance.imageId);
|
|
return imageIds.includes(reference.referencedImageId);
|
|
}
|
|
|
|
// for the volume viewports, we need to check orientation
|
|
const { orientation } = viewportOptions;
|
|
|
|
// Todo: handle hanging protocols that have acquisition orientation
|
|
const closestOrientation = getClosestOrientationFromIOP(
|
|
displaySetService,
|
|
reference.displaySetInstanceUID
|
|
);
|
|
|
|
return closestOrientation === orientation;
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
}
|