All files / tools/src/stateManagement/segmentation/utilities decimateContours.ts

0% Statements 0/27
0% Branches 0/13
0% Functions 0/2
0% Lines 0/27

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                                                                                                                                                                       
import type { ContourSegmentationAnnotation } from '../../../types';
import {
  getAnnotation,
  invalidateAnnotation,
} from '../../annotation/annotationState';
import { getSegmentation } from '../getSegmentation';
import { extractSegmentPolylines } from './extractSegmentPolylines';
import decimate from '../../../utilities/math/polyline/decimate';
import {
  getViewportsAssociatedToSegmentation,
  getViewportWithMatchingViewPlaneNormal,
} from './getViewportAssociatedToSegmentation';
 
/**
 * Decimates contour polylines for a given segmentation and segment using the Ramer-Douglas-Peucker algorithm.
 * This reduces the number of points in the contour while preserving the overall shape within a specified tolerance.
 *
 * @param segmentationId - The unique identifier of the segmentation
 * @param segmentIndex - The index of the segment within the segmentation
 * @param options - Configuration options for decimation
 * @param options.epsilon - The maximum distance tolerance for point removal (default: 0.1)
 */
export default function decimateContours(
  segmentationId: string,
  segmentIndex: number,
  options: { epsilon: number } = { epsilon: 0.1 }
) {
  const segmentation = getSegmentation(segmentationId);
  if (!segmentation) {
    console.warn(`Invalid segmentation given ${segmentationId}`);
    return;
  }
  if (!segmentation.representationData.Contour) {
    console.warn(
      `No contour representation found for segmentation ${segmentationId}`
    );
    return;
  }
  const viewports = getViewportsAssociatedToSegmentation(segmentationId);
  if (!viewports) {
    console.warn('No viewport associated to the segmentation found');
    return;
  }
 
  const polylinesCanvasMap = extractSegmentPolylines(
    segmentationId,
    segmentIndex
  );
  if (!polylinesCanvasMap) {
    console.warn(
      `Error extracting contour data from segment ${segmentIndex} in segmentation ${segmentationId}`
    );
    return;
  }
 
  const keys = Array.from(polylinesCanvasMap?.keys());
 
  for (const annotationUID of keys) {
    const annotation = getAnnotation(
      annotationUID
    ) as ContourSegmentationAnnotation;
    if (!annotation) {
      continue;
    }
 
    const polylineCanvas = polylinesCanvasMap.get(annotationUID);
 
    // Decimate the polyline
    const decimatedPolyline2D = decimate(polylineCanvas, options.epsilon);
 
    // Convert back to 3D points
    const viewport = getViewportWithMatchingViewPlaneNormal(
      viewports,
      annotation
    );
    if (viewport) {
      annotation.data.contour.polyline = decimatedPolyline2D.map((point2D) =>
        viewport.canvasToWorld(point2D)
      );
      invalidateAnnotation(annotation);
    }
  }
}