All files / packages/tools/src/utilities/math/ellipse pointInEllipse.ts

100% Statements 25/25
52.63% Branches 10/19
100% Functions 3/3
100% Lines 25/25

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                                                                1459x 1459x   1459x                             1x 1459x       1459x         1459x 1459x 1459x     1459x 1459x 1459x   1459x   1459x 1459x 1459x 57x     1402x 1402x 1402x 418x     984x 984x     984x     1459x        
import type { Types } from '@cornerstonejs/core';
interface Inverts {
  invXRadiusSq?: number;
  invYRadiusSq?: number;
  invZRadiusSq?: number;
  fast?: boolean;
  /**
   * If you call the pointInEllipse.precalculateInverts first, then you
   * can call precalculated directly instead of having the extra time for
   * the if conditions.
   */
  precalculated?: (pointLPS: Types.Point3) => boolean;
}
 
/**
 * Given an ellipse and a point, return true if the point is inside the ellipse
 * @param ellipse - The ellipse object to check against.
 * @param pointLPS - The point in LPS space to test.
 * @param inverts - An object to cache the inverted radius squared values, if you
 * are testing multiple points against the same ellipse then it is recommended to
 * pass in the same object to cache the values. However, there is a simpler way
 * to do this by passing in the fast flag as true, then on the first iteration
 * the values will be cached and on subsequent iterations the cached values will
 * be used.
 *
 * @returns A boolean value.
 */
export default function pointInEllipse(
  ellipse,
  pointLPS,
  inverts: Inverts = {}
) {
  Eif (!inverts.precalculated) {
    precalculatePointInEllipse(ellipse, inverts);
  }
  return inverts.precalculated(pointLPS);
}
 
/**
 * This will perform some precalculations to make things faster.
 * Ideally, use the 'precalculated' function inside inverts to call the
 * test function.  This minimizes re-reading of variables and only needs the
 * LPS passed each time.
 * That is:
 *
 * ```
 *    const inverts = precalculatePointInEllipse(ellipse);
 *    if( inverts.precalculated(pointLPS) ) ...
 * ```
 */
const precalculatePointInEllipse = (ellipse, inverts: Inverts = {}) => {
  const { xRadius, yRadius, zRadius } = ellipse;
 
  // This will run only once since we are caching the values in the same
  // object that is passed in.
  Eif (
    inverts.invXRadiusSq === undefined ||
    inverts.invYRadiusSq === undefined ||
    inverts.invZRadiusSq === undefined
  ) {
    inverts.invXRadiusSq = xRadius !== 0 ? 1 / xRadius ** 2 : 0;
    inverts.invYRadiusSq = yRadius !== 0 ? 1 / yRadius ** 2 : 0;
    inverts.invZRadiusSq = zRadius !== 0 ? 1 / zRadius ** 2 : 0;
  }
 
  const { invXRadiusSq, invYRadiusSq, invZRadiusSq } = inverts;
  const { center } = ellipse;
  const [centerL, centerP, centerS] = center;
 
  inverts.precalculated = (pointLPS) => {
    // Calculate the sum of normalized squared distances
    const dx = pointLPS[0] - centerL;
    let inside = dx * dx * invXRadiusSq;
    if (inside > 1) {
      return false;
    }
 
    const dy = pointLPS[1] - centerP;
    inside += dy * dy * invYRadiusSq;
    if (inside > 1) {
      return false;
    }
 
    const dz = pointLPS[2] - centerS;
    inside += dz * dz * invZRadiusSq;
 
    // Check if the point is inside the ellipse
    return inside <= 1;
  };
 
  return inverts;
};
 
export { precalculatePointInEllipse };