All files / core/src/utilities getClosestImageId.ts

82.35% Statements 28/34
71.42% Branches 15/21
100% Functions 1/1
81.81% Lines 27/33

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                  428x                                                 516x 516x   516x         516x       516x 516x 232x         284x 284x       284x       284x     284x 14436x     14436x 14436x       14436x     14436x 14436x     14436x     14436x             14436x 240x 240x         284x 44x         284x    
import type { mat3 } from 'gl-matrix';
import { vec3 } from 'gl-matrix';
import * as metaData from '../metaData';
import type { IImageVolume, Point3 } from '../types';
import { coreLog } from './logger';
 
import getSpacingInNormalDirection from './getSpacingInNormalDirection';
import { EPSILON } from '../constants';
 
const log = coreLog.getLogger('utilities', 'getClosestImageId');
 
/**
 * Given an image volume, a point in world space, and the view plane normal,
 * it returns the closest imageId based on the specified options.
 * If `options.ignoreSpacing` is true, it returns the imageId with the minimum
 * distance along the view plane normal, regardless of voxel spacing.
 * Otherwise, it returns the closest imageId within half voxel spacing along the normal.
 *
 * @param imageVolume - The image volume or object containing direction, spacing, and imageIds.
 * @param worldPos - The position in the world coordinate system.
 * @param viewPlaneNormal - The normal vector of the viewport.
 * @param options - Options object.
 * @param options.ignoreSpacing - If true, ignore spacing and find the absolute closest imageId.
 *
 * @returns The closest imageId based on the criteria, or undefined if none found.
 */
export default function getClosestImageId(
  imageVolume:
    | IImageVolume
    | { direction: mat3; spacing: Point3; imageIds: string[] },
  worldPos: Point3,
  viewPlaneNormal: Point3,
  options?: { ignoreSpacing?: boolean }
): string | undefined {
  const { direction, spacing, imageIds } = imageVolume;
  const { ignoreSpacing = false } = options || {};
 
  Iif (!imageIds?.length) {
    return;
  }
 
  // 1. Get ScanAxis vector (normal to the image plane)
  const kVector = direction.slice(6, 9) as Point3;
 
  // 2. Check if scanAxis is parallel to camera viewPlaneNormal
  // If not, the view is not aligned with the image plane, so return early.
  const dotProduct = vec3.dot(kVector, viewPlaneNormal);
  if (Math.abs(dotProduct) < 1 - EPSILON) {
    return;
  }
 
  // 3. Calculate spacing in the normal direction if needed
  let halfSpacingInNormalDirection: number | undefined;
  Eif (!ignoreSpacing) {
    const spacingInNormalDirection = getSpacingInNormalDirection(
      { direction, spacing },
      viewPlaneNormal
    );
    halfSpacingInNormalDirection = spacingInNormalDirection / 2;
  }
 
  let closestImageId: string | undefined;
  let minDistance = Infinity;
 
  // 4. Iterate over all imageIds to find the closest one
  for (let i = 0; i < imageIds.length; i++) {
    const imageId = imageIds[i];
 
    // 4.a Get metadata for the imageId
    const imagePlaneModule = metaData.get('imagePlaneModule', imageId);
    Iif (!imagePlaneModule?.imagePositionPatient) {
      log.warn(`Missing imagePositionPatient for imageId: ${imageId}`);
      continue; // Skip if essential metadata is missing
    }
    const { imagePositionPatient } = imagePlaneModule;
 
    // 4.b Calculate the direction vector from the world point to the image origin
    const dir = vec3.create();
    vec3.sub(dir, worldPos, imagePositionPatient);
 
    // 4.c Calculate the projected distance along the view plane normal
    const distance = Math.abs(vec3.dot(dir, viewPlaneNormal));
 
    // 4.d Check if this imageId is the closest one found so far based on options
    Iif (ignoreSpacing) {
      if (distance < minDistance) {
        minDistance = distance;
        closestImageId = imageId;
      }
    } else {
      // Check if within half spacing and closer than the current minimum
      if (distance < halfSpacingInNormalDirection && distance < minDistance) {
        minDistance = distance;
        closestImageId = imageId;
      }
    }
  }
 
  if (closestImageId === undefined) {
    log.warn(
      'No imageId found within the specified criteria (half spacing or absolute closest).'
    );
  }
 
  return closestImageId;
}