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

87.09% Statements 27/31
91.66% Branches 11/12
83.33% Functions 5/6
88.46% Lines 23/26

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          1x 1590x 1x 795x   265x                                     265x   265x   265x   265x                                 265x         2120x   265x 265x   265x     265x 265x 265x 2120x 2120x 530x   2120x 484x       265x                  
import vtkMatrixBuilder from '@kitware/vtk.js/Common/Core/MatrixBuilder';
import getVolumeActorCorners from './getVolumeActorCorners';
import type { VolumeActor, Point3, ActorSliceRange } from '../types';
import { EPSILON } from '../constants';
 
const SMALL_EPSILON = EPSILON * EPSILON;
const isOne = (v) => Math.abs(Math.abs(v) - 1) < SMALL_EPSILON;
const isUnit = (v, off) =>
  isOne(v[off]) || isOne(v[off + 1]) || isOne(v[off + 2]);
 
const isOrthonormal = (v) => isUnit(v, 0) && isUnit(v, 3) && isUnit(v, 6);
 
/**
 * Given a `vtkVolumeActor`, and a normal direction,
 * calculate the range of slices in the focal normal direction that encapsulate
 * the volume. Also project the `focalPoint` onto this range.
 *
 * @param volumeActor - The `vtkVolumeActor`.
 * @param viewPlaneNormal - The normal to the camera view.
 * @param focalPoint - The focal point of the camera.
 *
 * @returns an object containing the `min`, `max` and `current`
 * positions in the normal direction.
 */
export default function getSliceRange(
  volumeActor: VolumeActor,
  viewPlaneNormal: Point3,
  focalPoint: Point3
): ActorSliceRange {
  const imageData = volumeActor.getMapper().getInputData();
  let corners;
  const direction = imageData.getDirection();
 
  if (isOrthonormal(direction)) {
    // This logic is only valid when the IJK vectors are unit vectors
    corners = getVolumeActorCorners(volumeActor);
  } else E{
    // This logic works for both unit and non-unit vectors, but is slower
    const [dx, dy, dz] = imageData.getDimensions();
    const cornersIdx = [
      [0, 0, 0],
      [dx - 1, 0, 0],
      [0, dy - 1, 0],
      [dx - 1, dy - 1, 0],
      [0, 0, dz - 1],
      [dx - 1, 0, dz - 1],
      [0, dy - 1, dz - 1],
      [dx - 1, dy - 1, dz - 1],
    ];
    corners = cornersIdx.map((it) => imageData.indexToWorld(it));
  }
  // Get rotation matrix from normal to +X (since bounds is aligned to XYZ)
  const transform = vtkMatrixBuilder
    .buildFromDegree()
    .identity()
    .rotateFromDirections(viewPlaneNormal, [1, 0, 0]);
 
  corners.forEach((pt) => transform.apply(pt));
 
  const transformedFocalPoint = [...focalPoint];
  transform.apply(transformedFocalPoint);
 
  const currentSlice = transformedFocalPoint[0];
 
  // range is now maximum X distance
  let minX = Infinity;
  let maxX = -Infinity;
  for (let i = 0; i < 8; i++) {
    const x = corners[i][0];
    if (x > maxX) {
      maxX = x;
    }
    if (x < minX) {
      minX = x;
    }
  }
 
  return {
    min: minX,
    max: maxX,
    current: currentSlice,
    actor: volumeActor,
    viewPlaneNormal,
    focalPoint,
  };
}