All files / packages/tools/src/utilities/math/polyline areLineSegmentsIntersecting.ts

60% Statements 18/30
55.76% Branches 29/52
66.66% Functions 2/3
60% Lines 18/30

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                                                        2225x     2225x 2225x 2225x 2225x     2225x 2225x 2225x 2225x       2225x           2223x     2x               2x 2x                                                               8x   8x 4x     4x                                    
import type { Types } from '@cornerstonejs/core';
 
// ATTENTION: this is an internal function and it should not be added to "polyline"
// namespace.
//
// TODO: there is a similar function in math.lineSegment.intersectLine but we
// need to investigate why it is 6x slower than this one when thousands of
// intersections are calculated. Also that one may return [NaN, NaN] for
// collinear points.
 
/**
 * Checks whether the line (`p1`,`q1`) intersects the line (`p2`,`q2`) via an
 * orientation algorithm.
 *
 * Credit and details: geeksforgeeks.org/check-if-two-given-line-segments-intersect/
 *
 * @param p1 - Start point of line segment 1
 * @param q1 - End point of line segment 1
 * @param p2 - Start point of line segment 2
 * @param q2 - End point of line segment 2
 * @returns True if the line segments intersect or false otherwise
 */
export default function areLineSegmentsIntersecting(
  p1: Types.Point2,
  q1: Types.Point2,
  p2: Types.Point2,
  q2: Types.Point2
): boolean {
  let result = false;
 
  // Line 1 AABB
  const line1MinX = p1[0] < q1[0] ? p1[0] : q1[0];
  const line1MinY = p1[1] < q1[1] ? p1[1] : q1[1];
  const line1MaxX = p1[0] > q1[0] ? p1[0] : q1[0];
  const line1MaxY = p1[1] > q1[1] ? p1[1] : q1[1];
 
  // Line 2 AABB
  const line2MinX = p2[0] < q2[0] ? p2[0] : q2[0];
  const line2MinY = p2[1] < q2[1] ? p2[1] : q2[1];
  const line2MaxX = p2[0] > q2[0] ? p2[0] : q2[0];
  const line2MaxY = p2[1] > q2[1] ? p2[1] : q2[1];
 
  // If AABBs do not intersect it is impossible for the lines to intersect.
  // Checking AABB before doing any math makes it run ~12% faster.
  if (
    line1MinX > line2MaxX ||
    line1MaxX < line2MinX ||
    line1MinY > line2MaxY ||
    line1MaxY < line2MinY
  ) {
    return false;
  }
 
  const orient = [
    orientation(p1, q1, p2),
    orientation(p1, q1, q2),
    orientation(p2, q2, p1),
    orientation(p2, q2, q1),
  ];
 
  // General Case
  Eif (orient[0] !== orient[1] && orient[2] !== orient[3]) {
    return true;
  }
 
  // Special Cases
  if (orient[0] === 0 && onSegment(p1, p2, q1)) {
    // If p1, q1 and p2 are colinear and p2 lies on segment p1q1
    result = true;
  } else if (orient[1] === 0 && onSegment(p1, q2, q1)) {
    // If p1, q1 and p2 are colinear and q2 lies on segment p1q1
    result = true;
  } else if (orient[2] === 0 && onSegment(p2, p1, q2)) {
    // If p2, q2 and p1 are colinear and p1 lies on segment p2q2
    result = true;
  } else if (orient[3] === 0 && onSegment(p2, q1, q2)) {
    // If p2, q2 and q1 are colinear and q1 lies on segment p2q2
    result = true;
  }
 
  return result;
}
 
/**
 * Checks the orientation of 3 points, returns a 0, 1 or 2 based on
 * the orientation of the points.
 */
function orientation(
  p: Types.Point2,
  q: Types.Point2,
  r: Types.Point2
): number {
  // Take the cross product between vectors PQ and QR
  const orientationValue =
    (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
 
  if (orientationValue === 0) {
    return 0; // Colinear
  }
 
  return orientationValue > 0 ? 1 : 2;
}
 
/**
 * Checks if point `q` lies on the segment (`p`,`r`).
 */
function onSegment(p: Types.Point2, q: Types.Point2, r: Types.Point2): boolean {
  if (
    q[0] <= Math.max(p[0], r[0]) &&
    q[0] >= Math.min(p[0], r[0]) &&
    q[1] <= Math.max(p[1], r[1]) &&
    q[1] >= Math.min(p[1], r[1])
  ) {
    return true;
  }
 
  return false;
}