All files / tools/src/utilities/contours updateContourPolyline.ts

75.67% Statements 28/37
50% Branches 10/20
100% Functions 1/1
75% Lines 27/36

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                                                                                    594x 594x 594x 594x 594x     594x             594x 594x 594x   594x 594x   594x                               594x   203x       203x       203x       203x   203x   203x   203x         203x     594x 69081x     594x 594x 594x   594x    
import { utilities as csUtils } from '@cornerstonejs/core';
import type { Types } from '@cornerstonejs/core';
import type { ContourAnnotation } from '../../types';
import type { ContourWindingDirection } from '../../types/ContourAnnotation';
import * as math from '../math';
import {
  getParentAnnotation,
  invalidateAnnotation,
} from '../../stateManagement/annotation/annotationState';
 
/**
 * Update the contour polyline data
 * @param annotation - Contour annotation
 * @param viewport - Viewport
 * @param polylineData - Polyline data (points, winding direction and closed)
 * @param transforms - Methods to convert points to/from canvas and world spaces
 * @param options - Options
 *   - decimate: allow to set some parameters to decimate the polyline reducing
 *   the amount of points stored which also affects how fast it will draw the
 *   annotation in a viewport, compute the winding direction, append/remove
 *   contours and create holes. A higher `epsilon` value results in a polyline
 *   with less points.
 */
export default function updateContourPolyline(
  annotation: ContourAnnotation,
  polylineData: {
    points: Types.Point2[];
    closed?: boolean;
    targetWindingDirection?: ContourWindingDirection;
  },
  transforms: {
    canvasToWorld: (point: Types.Point2) => Types.Point3;
    worldToCanvas: (point: Types.Point3) => Types.Point2;
  },
  options?: {
    updateWindingDirection?: boolean;
    decimate?: {
      enabled?: boolean;
      epsilon?: number;
    };
  }
) {
  const { canvasToWorld, worldToCanvas } = transforms;
  const { data } = annotation;
  const { targetWindingDirection } = polylineData;
  let { points: polyline } = polylineData;
  let windingDirection = math.polyline.getWindingDirection(polyline);
 
  // Decimate the polyline to reduce tha amount of points
  Iif (options?.decimate?.enabled) {
    polyline = math.polyline.decimate(
      polylineData.points,
      options?.decimate?.epsilon
    );
  }
 
  let { closed } = polylineData;
  const numPoints = polyline.length;
  const polylineWorldPoints = new Array(numPoints);
  const currentPolylineWindingDirection =
    math.polyline.getWindingDirection(polyline);
  const parentAnnotation = getParentAnnotation(annotation) as ContourAnnotation;
 
  Iif (closed === undefined) {
    let currentClosedState = false;
 
    // With two points it is just a line and do not make sense to consider it closed
    if (polyline.length > 3) {
      const lastToFirstDist = math.point.distanceToPointSquared(
        polyline[0],
        polyline[numPoints - 1]
      );
 
      currentClosedState = csUtils.isEqual(0, lastToFirstDist);
    }
 
    closed = currentClosedState;
  }
 
  if (options?.updateWindingDirection !== false) {
    // It must be in the opposite direction if it is a child annotation (hole)
    let updatedWindingDirection = parentAnnotation
      ? parentAnnotation.data.contour.windingDirection * -1
      : targetWindingDirection;
 
    Iif (updatedWindingDirection === undefined) {
      updatedWindingDirection = windingDirection;
    }
 
    Iif (updatedWindingDirection !== windingDirection) {
      polyline.reverse();
    }
 
    const handlePoints = (data.handles?.points ?? []).map(worldToCanvas);
 
    Eif (handlePoints.length > 2) {
      const currentHandlesWindingDirection =
        math.polyline.getWindingDirection(handlePoints);
 
      Iif (currentHandlesWindingDirection !== updatedWindingDirection) {
        data.handles.points.reverse();
      }
    }
 
    windingDirection = updatedWindingDirection;
  }
 
  for (let i = 0; i < numPoints; i++) {
    polylineWorldPoints[i] = canvasToWorld(polyline[i]);
  }
 
  data.contour.polyline = polylineWorldPoints;
  data.contour.closed = closed;
  data.contour.windingDirection = windingDirection;
 
  invalidateAnnotation(annotation);
}