All files / packages/tools/src/utilities/planar getPointInLineOfSightWithCriteria.ts

4% Statements 1/25
0% Branches 0/11
0% Functions 0/2
4.16% Lines 1/24

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                                                                                                                                                                                    1x                            
import vtkMath from '@kitware/vtk.js/Common/Core/Math';
import { utilities as csUtils } from '@cornerstonejs/core';
import type { Types } from '@cornerstonejs/core';
/**
 * Returns a point based on some criteria (e.g., minimum or maximum intensity) in
 * the line of sight (on the line between the passed worldPosition and camera position).
 * It iterated over the points with a step size on the line.
 *
 * @param viewport - Volume viewport
 * @param worldPos - World coordinates of the clicked location
 * @param targetVolumeId - target Volume ID in the viewport
 * @param criteriaFunction - A function that returns the point if it passes a certain
 * written logic, for instance, it can be a maxValue function that keeps the
 * records of all intensity values, and only return the point if its intensity
 * is greater than the maximum intensity of the points passed before.
 * @param stepsSize - Percentage of the spacing in the normal direction, default value
 * is 0.25 which means steps = 1/4 of the spacing in the normal direction.
 * @returns the World pos of the point that passes the criteriaFunction
 */
export default function getPointInLineOfSightWithCriteria(
  viewport: Types.IVolumeViewport,
  worldPos: Types.Point3,
  targetVolumeId: string,
  criteriaFunction: (intensity: number, point: Types.Point3) => Types.Point3,
  stepSize = 0.25
): Types.Point3 {
  // 1. Getting the camera from the event details
  const camera = viewport.getCamera();
  const { position: cameraPosition } = camera;
 
  // 2. Calculating the spacing in the normal direction, this will get
  // used as the step size for iterating over the points in the line of sight
  const { spacingInNormalDirection } =
    csUtils.getTargetVolumeAndSpacingInNormalDir(
      viewport,
      camera,
      targetVolumeId
    );
  // 2.1 Making sure, we are not missing any point
  const step = spacingInNormalDirection * stepSize;
 
  // 3. Getting the bounds of the viewports. Search for brightest point is
  // limited to the visible bound
  // Todo: this might be a problem since bounds will change to spatial bounds.
  const bounds = viewport.getBounds();
  const xMin = bounds[0];
  const xMax = bounds[1];
 
  // 5. Calculating the line, we use a parametric line definition
  const vector = <Types.Point3>[0, 0, 0];
 
  // 5.1 Point coordinate on the line
  let point = <Types.Point3>[0, 0, 0];
 
  // 5.2 Calculating the line direction, and storing in vector
  vtkMath.subtract(worldPos, cameraPosition, vector);
 
  let pickedPoint;
 
  // 6. Iterating over the line from the lower bound to the upper bound, with the
  // specified step size
  for (let pointT = xMin; pointT <= xMax; pointT = pointT + step) {
    // 6.1 Calculating the point x location
    point = [pointT, 0, 0];
    // 6.2 Calculating the point y,z location based on the line equation
    const t = (pointT - cameraPosition[0]) / vector[0];
    point[1] = t * vector[1] + cameraPosition[1];
    point[2] = t * vector[2] + cameraPosition[2];
 
    // 6.3 Checking if the points is inside the bounds
    if (_inBounds(point, bounds)) {
      // 6.4 Getting the intensity of the point
      const intensity = viewport.getIntensityFromWorld(point);
      // 6.5 Passing the intensity to the maximum value functions which decides
      // whether the current point is of interest based on some criteria
      const pointToPick = criteriaFunction(intensity, point);
      if (pointToPick) {
        pickedPoint = pointToPick;
      }
    }
  }
 
  return pickedPoint;
}
 
/**
 * Returns whether the point in the world is inside the bounds of the viewport
 * @param point - coordinates in the world
 * @returns boolean
 */
const _inBounds = function (
  point: Types.Point3,
  bounds: Array<number>
): boolean {
  const [xMin, xMax, yMin, yMax, zMin, zMax] = bounds;
  return (
    point[0] > xMin &&
    point[0] < xMax &&
    point[1] > yMin &&
    point[1] < yMax &&
    point[2] > zMin &&
    point[2] < zMax
  );
};