All files / Sources/Rendering/Core/AbstractImageMapper helper.js

86.36% Statements 38/44
71.42% Branches 20/28
100% Functions 3/3
86.36% Lines 38/44

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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154                        2x 2x     2x 2x 2x     2x       2x 2x 2x     2x 2x 2x 2x 2x 2x 2x   2x           2x 2x 2x 2x     2x                                   1x 1x 1x 1x       1x             1x                     1x                                   1x 1x 1x 1x 1x       1x             1x                         1x           1x                
import { vec3 } from 'gl-matrix';
import vtkPlane from 'vtk.js/Sources/Common/DataModel/Plane';
 
/**
 * Perform plane-line intersection, where the line is defined by two points (p1, p2),
 * and the plane is defined by the imageData and slice number.
 *
 * @param {Vector3} p1
 * @param {Vector3} p2
 * @param {vtkImageMapper|vtkImageArrayMapper} mapper
 */
function doPicking(p1, p2, mapper) {
  const imageData = mapper.getCurrentImage();
  const extent = imageData.getExtent();
 
  // Slice origin
  const ijk = [extent[0], extent[2], extent[4]];
  const { ijkMode } = mapper.getClosestIJKAxis();
  let nSlice = mapper.isA('vtkImageArrayMapper')
    ? mapper.getSubSlice()
    : mapper.getSlice();
  Iif (ijkMode !== mapper.getSlicingMode()) {
    // If not IJK slicing, get the IJK slice from the XYZ position/slice
    nSlice = mapper.getSliceAtPosition(nSlice);
  }
  ijk[ijkMode] += nSlice;
  const worldOrigin = [0, 0, 0];
  imageData.indexToWorld(ijk, worldOrigin);
 
  // Normal computation
  ijk[ijkMode] += 1;
  const worldNormal = [0, 0, 0];
  imageData.indexToWorld(ijk, worldNormal);
  worldNormal[0] -= worldOrigin[0];
  worldNormal[1] -= worldOrigin[1];
  worldNormal[2] -= worldOrigin[2];
  vec3.normalize(worldNormal, worldNormal);
 
  const intersect = vtkPlane.intersectWithLine(
    p1,
    p2,
    worldOrigin,
    worldNormal
  );
  if (intersect.intersection) {
    const point = intersect.x;
    const absoluteIJK = [0, 0, 0];
    imageData.worldToIndex(point, absoluteIJK);
    // `t` is the parametric position along the line
    // defined in Plane.intersectWithLine
    return {
      t: intersect.t,
      absoluteIJK,
    };
  }
  return null;
}
 
/**
 * Implement point picking for image plane.
 * The plane is defined by the imageData and current slice number,
 * set in the input mapper.
 *
 * @param {Vector3} p1
 * @param {Vector3} p2
 * @param {vtkImageMapper|vtkImageArrayMapper} mapper
 */
export function intersectWithLineForPointPicking(p1, p2, mapper) {
  const pickingData = doPicking(p1, p2, mapper);
  if (pickingData) {
    const imageData = mapper.getCurrentImage();
    const extent = imageData.getExtent();
 
    // Get closer integer ijk
    // NB: point picking means closest slice, means rounding
    const ijk = [
      Math.round(pickingData.absoluteIJK[0]),
      Math.round(pickingData.absoluteIJK[1]),
      Math.round(pickingData.absoluteIJK[2]),
    ];
 
    // Are we outside our actual extent
    Iif (
      ijk[0] < extent[0] ||
      ijk[0] > extent[1] ||
      ijk[1] < extent[2] ||
      ijk[1] > extent[3] ||
      ijk[2] < extent[4] ||
      ijk[2] > extent[5]
    ) {
      return null;
    }
 
    return {
      t: pickingData.t,
      ijk,
    };
  }
  return null;
}
 
/**
 * Implement cell picking for image plane.
 * The plane is defined by the imageData and current slice number,
 * set in the input mapper.
 *
 * @param {Vector3} p1
 * @param {Vector3} p2
 * @param {vtkImageMapper|vtkImageArrayMapper} mapper
 */
export function intersectWithLineForCellPicking(p1, p2, mapper) {
  const pickingData = doPicking(p1, p2, mapper);
  if (pickingData) {
    const imageData = mapper.getCurrentImage();
    const extent = imageData.getExtent();
    const absIJK = pickingData.absoluteIJK;
 
    // Get closer integer ijk
    // NB: cell picking means closest voxel, means flooring
    const ijk = [
      Math.floor(absIJK[0]),
      Math.floor(absIJK[1]),
      Math.floor(absIJK[2]),
    ];
 
    // Are we outside our actual extent
    Iif (
      ijk[0] < extent[0] ||
      ijk[0] > extent[1] - 1 ||
      ijk[1] < extent[2] ||
      ijk[1] > extent[3] - 1 ||
      ijk[2] < extent[4] ||
      // handle single-slice images
      ijk[2] > (extent[5] ? extent[5] - 1 : extent[5])
    ) {
      return null;
    }
 
    // Parametric coordinates within cell
    const pCoords = [
      absIJK[0] - ijk[0],
      absIJK[1] - ijk[1],
      absIJK[2] - ijk[2],
    ];
 
    return {
      t: pickingData.t,
      ijk,
      pCoords,
    };
  }
  return null;
}