All files / packages/core/src/cache/classes ContourSet.ts

0% Statements 0/43
0% Branches 0/8
0% Functions 0/23
0% Lines 0/42

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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218                                                                                                                                                                                                                                                                                                                                                                                                                                                   
import vtkPolyData from '@kitware/vtk.js/Common/DataModel/PolyData';
import { Point3, IContourSet, IContour, ContourData } from '../../types';
import Contour from './Contour';
 
type ContourSetProps = {
  id: string;
  data: ContourData[];
  frameOfReferenceUID: string;
  segmentIndex: number;
  color?: Point3;
};
 
/**
 * This class represents a set of contours in 3d space.
 * Usually contours are grouped together in a contour set to represent a meaningful shape.
 */
export class ContourSet implements IContourSet {
  readonly id: string;
  readonly sizeInBytes: number;
  readonly frameOfReferenceUID: string;
  private color: Point3 = [200, 0, 0]; // default color
  private segmentIndex: number;
  private polyData: vtkPolyData;
  private centroid: Point3;
  contours: IContour[];
 
  constructor(props: ContourSetProps) {
    this.id = props.id;
    this.contours = [];
    this.color = props.color ?? this.color;
    this.frameOfReferenceUID = props.frameOfReferenceUID;
    this.segmentIndex = props.segmentIndex;
    this._createEachContour(props.data);
    this.sizeInBytes = this._getSizeInBytes();
  }
 
  _createEachContour(contourDataArray: ContourData[]): void {
    contourDataArray.forEach((contourData) => {
      const { points, type, color } = contourData;
 
      const contour = new Contour({
        id: `${this.id}-segment-${this.segmentIndex}`,
        data: {
          points,
          type,
          segmentIndex: this.segmentIndex,
          color: color ?? this.color,
        },
        segmentIndex: this.segmentIndex,
        color: color ?? this.color,
      });
 
      this.contours.push(contour);
    });
 
    this._updateContourSetCentroid();
  }
 
  // Todo: this centroid calculation has limitation in which
  // it will not work for MPR, the reason is that we are finding
  // the centroid of all points but at the end we are picking the
  // closest point to the centroid, which will not work for MPR
  // The reason for picking the closest is a rendering issue since
  // the centroid can be not exactly in the middle of the slice
  // and it might cause the contour to be rendered in the wrong slice
  // or not rendered at all
  _updateContourSetCentroid(): void {
    const numberOfPoints = this.getTotalNumberOfPoints();
    const flatPointsArray = this.getFlatPointsArray();
 
    const sumOfPoints = flatPointsArray.reduce(
      (acc, point) => {
        return [acc[0] + point[0], acc[1] + point[1], acc[2] + point[2]];
      },
      [0, 0, 0]
    );
 
    const centroid = [
      sumOfPoints[0] / numberOfPoints,
      sumOfPoints[1] / numberOfPoints,
      sumOfPoints[2] / numberOfPoints,
    ];
 
    const closestPoint = flatPointsArray.reduce((closestPoint, point) => {
      const distanceToPoint = this._getDistance(centroid, point);
      const distanceToClosestPoint = this._getDistance(centroid, closestPoint);
 
      if (distanceToPoint < distanceToClosestPoint) {
        return point;
      } else {
        return closestPoint;
      }
    }, flatPointsArray[0]);
 
    this.centroid = closestPoint;
  }
 
  _getSizeInBytes(): number {
    return this.contours.reduce((sizeInBytes, contour) => {
      return sizeInBytes + contour.sizeInBytes;
    }, 0);
  }
 
  public getCentroid(): Point3 {
    return this.centroid;
  }
 
  public getSegmentIndex(): number {
    return this.segmentIndex;
  }
 
  public getColor(): Point3 {
    // Currently, all contours in a contour set have the same color.
    // This may change in the future.
    return this.color;
  }
 
  /**
   * This function returns the contours of the image
   * @returns The contours of the image.
   */
  public getContours(): IContour[] {
    return this.contours;
  }
 
  public getSizeInBytes(): number {
    return this.sizeInBytes;
  }
 
  /**
   * It returns an array of all the points in the glyph
   * @returns An array of points.
   */
  public getFlatPointsArray(): Point3[] {
    return this.contours.map((contour) => contour.getPoints()).flat();
  }
 
  /**
   * This function returns the number of contours in the current shape.
   * @returns The number of contours in the glyph.
   */
  public getNumberOfContours(): number {
    return this.contours.length;
  }
 
  /**
   * It loops through each contour in the `contours` array, and adds the number of
   * points in each contour to the `numberOfPoints` variable
   * @returns The number of points in the contours.
   */
  public getTotalNumberOfPoints(): number {
    return this.contours.reduce((numberOfPoints, contour) => {
      return numberOfPoints + contour.getPoints().length;
    }, 0);
  }
 
  /**
   * It returns an array of the number of points in each contour.
   * @returns An array of numbers.
   */
  public getNumberOfPointsArray(): number[] {
    return this.contours.reduce((acc, _, i) => {
      acc[i] = this.getNumberOfPointsInAContour(i);
      return acc;
    }, []);
  }
 
  /**
   * It returns the points in a contour.
   * @param contourIndex - The index of the contour you want to get the
   * points from.
   * @returns An array of Point3 objects.
   */
  public getPointsInContour(contourIndex: number): Point3[] {
    return this.contours[contourIndex].getPoints();
  }
  /**
   * "This function returns the number of points in a contour."
   *
   * @param contourIndex - The index of the contour you want to get the
   * number of points from.
   * @returns The number of points in the contour.
   */
  public getNumberOfPointsInAContour(contourIndex: number): number {
    return this.getPointsInContour(contourIndex).length;
  }
 
  private _getDistance(pointA, pointB) {
    return Math.sqrt(
      (pointA[0] - pointB[0]) ** 2 +
        (pointA[1] - pointB[1]) ** 2 +
        (pointA[2] - pointB[2]) ** 2
    );
  }
  /**
  public convertToClosedSurface(): ClosedSurface {
    const flatPointsArray = this.getFlatPointsArray();
    const numPointsArray = this.getNumberOfPointsArray();
 
    const closedSurfaceData = polySeg.convertToClosedSurface(
      flatPointsArray,
      numPointsArray
    );
 
    const closedSurface = new ClosedSurface({
      id: this.id,
      data: closedSurfaceData,
      color: this.color,
    });
 
    // cache the closed surface
    return closedSurface;
  }
   */
}
 
export default Contour;