All files / tools/src/utilities/segmentation isLineInSegment.ts

0% Statements 0/35
0% Branches 0/10
0% Functions 0/7
0% Lines 0/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 108 109 110 111 112 113                                                                                                                                                                                                                                 
import type { Types } from '@cornerstonejs/core';
import { cache } from '@cornerstonejs/core';
import { vec3 } from 'gl-matrix';
import type vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
/**
 * Determines if there is a point between point1 and point2 which is not
 * contained in the segmentation
 */
export default function isLineInSegment(
  point1: Types.Point3,
  point2: Types.Point3,
  isInSegment
) {
  const ijk1 = isInSegment.toIJK(point1);
  const ijk2 = isInSegment.toIJK(point2);
  const testPoint = vec3.create();
  const { testIJK } = isInSegment;
  const delta = vec3.sub(vec3.create(), ijk1, ijk2);
 
  // Test once for index value between the two points, so the max of the
  // difference in IJK values
  const testSize = Math.round(Math.max(...delta.map(Math.abs)));
  if (testSize < 2) {
    // No need to test when there are only two points
    return true;
  }
  const unitDelta = vec3.scale(vec3.create(), delta, 1 / testSize);
 
  for (let i = 1; i < testSize; i++) {
    vec3.scaleAndAdd(testPoint, ijk2, unitDelta, i);
    if (!testIJK(testPoint)) {
      return false;
    }
  }
  return true;
}
 
/**
 * Creates a function that tests if points are contained in the segment using raw volume metadata
 * @param width - The width (dimension[0]) of the volume
 * @param dimensions - The dimensions of the volume
 * @param imageData - The imageData object containing worldToIndex transformation
 * @param voxelManager - The voxelManager instance for getting values at indices
 * @param segmentIndex - The index of the segment to test for
 * @param containedSegmentIndices - Optional set of additional segment indices to consider as contained
 */
function createIsInSegmentMetadata({
  dimensions,
  imageData,
  voxelManager,
  segmentIndex,
  containedSegmentIndices,
}: {
  dimensions: number[];
  imageData: vtkImageData;
  voxelManager: Types.IVoxelManager<number>;
  segmentIndex: number;
  containedSegmentIndices?: Set<number>;
}) {
  const width = dimensions[0];
  const pixelsPerSlice = width * dimensions[1];
 
  return {
    testCenter: (point1, point2) => {
      const point = vec3.add(vec3.create(), point1, point2).map((it) => it / 2);
      const ijk = imageData.worldToIndex(point as vec3).map(Math.round);
      const [i, j, k] = ijk;
      const index = i + j * width + k * pixelsPerSlice;
      const value = voxelManager.getAtIndex(index) as number;
      return value === segmentIndex || containedSegmentIndices?.has(value);
    },
 
    toIJK: (point) => imageData.worldToIndex(point as vec3),
 
    testIJK: (ijk) => {
      const [i, j, k] = ijk;
      const index =
        Math.round(i) + Math.round(j) * width + Math.round(k) * pixelsPerSlice;
      const value = voxelManager.getAtIndex(index) as number;
      return value === segmentIndex || containedSegmentIndices?.has(value);
    },
  };
}
 
/**
 * Creates a function that tests to see if the provided line segment, specified
 * in LPS space (as endpoints) is contained in the segment using a volume ID
 * @param segVolumeId - The ID of the segmentation volume
 * @param segmentIndex - The index of the segment to test for
 * @param containedSegmentIndices - Optional set of additional segment indices to consider as contained
 */
function createIsInSegment(
  segVolumeId: string,
  segmentIndex: number,
  containedSegmentIndices?: Set<number>
) {
  const vol = cache.getVolume(segVolumeId);
  if (!vol) {
    console.warn(`No volume found for ${segVolumeId}`);
    return;
  }
 
  return createIsInSegmentMetadata({
    dimensions: vol.dimensions,
    imageData: vol.imageData,
    voxelManager: vol.voxelManager as Types.IVoxelManager<number>,
    segmentIndex,
    containedSegmentIndices,
  });
}
 
export { createIsInSegment, createIsInSegmentMetadata, isLineInSegment };