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

0% Statements 0/45
0% Branches 0/32
0% Functions 0/4
0% Lines 0/43

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 118 119 120 121 122 123 124 125 126 127 128 129                                                                                                                                                                                                                                                                 
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 };
}