All files / packages/core/src/utilities getClosestStackImageIndexForPoint.ts

0% Statements 0/45
0% Branches 0/34
0% Functions 0/4
0% Lines 0/39

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117                                                                                                                                                                                                                                         
import { vec3 } from 'gl-matrix';
import { planar } from '.';
import { metaData } from '..';
import { IStackViewport, Point3 } from '../types';
 
/**
 * Given a point in 3D space and a viewport it returns the index of the closest imageId, it assumes that stack images are sorted according to their sliceLocation
 * @param point - [A, B, C] coordinates of the point in 3D space
 * @param viewport - The StackViewport to search for the closest imageId
 *
 * @returns The imageId index of the closest imageId or null if no imageId is found
 */
export default function getClosestStackImageIndexForPoint(
  point: Point3,
  viewport: IStackViewport
): number | null {
  const minimalDistance = calculateMinimalDistanceForStackViewport(
    point,
    viewport
  );
  return minimalDistance ? minimalDistance.index : null;
}
 
//assumes that imageIds are sorted by slice location
export function calculateMinimalDistanceForStackViewport(
  point: Point3,
  viewport: IStackViewport
): { distance: number; index: number } | null {
  const imageIds = viewport.getImageIds();
  const currentImageIdIndex = viewport.getCurrentImageIdIndex();
 
  if (imageIds.length === 0) return null;
 
  const getDistance = (imageId: string): null | number => {
    const planeMetadata = getPlaneMetadata(imageId);
    if (!planeMetadata) return null;
    const plane = planar.planeEquation(
      planeMetadata.planeNormal,
      planeMetadata.imagePositionPatient
    );
    const distance = planar.planeDistanceToPoint(plane, point);
    return distance;
  };
 
  const closestStack = {
    distance: getDistance(imageIds[currentImageIdIndex]) ?? Infinity,
    index: currentImageIdIndex,
  };
 
  //check higher indices
  const higherImageIds = imageIds.slice(currentImageIdIndex + 1);
 
  for (let i = 0; i < higherImageIds.length; i++) {
    const id = higherImageIds[i];
    const distance = getDistance(id);
    if (distance === null) continue;
    if (distance <= closestStack.distance) {
      closestStack.distance = distance;
      closestStack.index = i + currentImageIdIndex + 1;
    } else break;
  }
  //check lower indices
  const lowerImageIds = imageIds.slice(0, currentImageIdIndex);
  for (let i = lowerImageIds.length - 1; i >= 0; i--) {
    const id = lowerImageIds[i];
    const distance = getDistance(id);
    if (distance === null || distance === closestStack.distance) continue;
    if (distance < closestStack.distance) {
      closestStack.distance = distance;
      closestStack.index = i;
    } else break;
  }
  return closestStack.distance === Infinity ? null : closestStack;
}
 
function getPlaneMetadata(imageId: string): null | {
  rowCosines: Point3;
  columnCosines: Point3;
  imagePositionPatient: Point3;
  planeNormal: Point3;
} {
  const targetImagePlane = metaData.get('imagePlaneModule', imageId);
 
  if (
    !targetImagePlane ||
    !(
      targetImagePlane.rowCosines instanceof Array &&
      targetImagePlane.rowCosines.length === 3
    ) ||
    !(
      targetImagePlane.columnCosines instanceof Array &&
      targetImagePlane.columnCosines.length === 3
    ) ||
    !(
      targetImagePlane.imagePositionPatient instanceof Array &&
      targetImagePlane.imagePositionPatient.length === 3
    )
  ) {
    return null;
  }
  const {
    rowCosines,
    columnCosines,
    imagePositionPatient,
  }: {
    rowCosines: Point3;
    columnCosines: Point3;
    imagePositionPatient: Point3;
  } = targetImagePlane;
 
  const rowVec = vec3.set(vec3.create(), ...rowCosines);
  const colVec = vec3.set(vec3.create(), ...columnCosines);
  const planeNormal = vec3.cross(vec3.create(), rowVec, colVec) as Point3;
 
  return { rowCosines, columnCosines, imagePositionPatient, planeNormal };
}