{"version":3,"file":"Intersection.min.mjs","names":[],"sources":["../../src/Intersection.ts"],"sourcesContent":["import { Point } from './Point';\nimport { createVector } from './util/misc/vectors';\n\n/* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */\n\nexport type IntersectionType = 'Intersection' | 'Coincident' | 'Parallel';\n\nexport class Intersection {\n  declare points: Point[];\n\n  declare status?: IntersectionType;\n\n  constructor(status?: IntersectionType) {\n    this.status = status;\n    this.points = [];\n  }\n\n  /**\n   * Used to verify if a point is alredy in the collection\n   * @param {Point} point\n   * @returns {boolean}\n   */\n  private includes(point: Point): boolean {\n    return this.points.some((p) => p.eq(point));\n  }\n\n  /**\n   * Appends points of intersection\n   * @param {...Point[]} points\n   * @return {Intersection} thisArg\n   */\n  private append(...points: Point[]): Intersection {\n    this.points = this.points.concat(\n      points.filter((point) => {\n        return !this.includes(point);\n      }),\n    );\n    return this;\n  }\n\n  /**\n   * check if point T is on the segment or line defined between A and B\n   *\n   * @param {Point} T the point we are checking for\n   * @param {Point} A one extremity of the segment\n   * @param {Point} B the other extremity of the segment\n   * @param [infinite] if true checks if `T` is on the line defined by `A` and `B`\n   * @returns true if `T` is contained\n   */\n  static isPointContained(T: Point, A: Point, B: Point, infinite = false) {\n    if (A.eq(B)) {\n      // Edge case: the segment is a point, we check for coincidence,\n      // infinite param has no meaning because there are infinite lines to consider\n      return T.eq(A);\n    } else if (A.x === B.x) {\n      // Edge case: horizontal line.\n      // we first check if T.x has the same value, and then if T.y is contained between A.y and B.y\n      return (\n        T.x === A.x &&\n        (infinite || (T.y >= Math.min(A.y, B.y) && T.y <= Math.max(A.y, B.y)))\n      );\n    } else if (A.y === B.y) {\n      // Edge case: vertical line.\n      // we first check if T.y has the same value, and then if T.x is contained between A.x and B.x\n      return (\n        T.y === A.y &&\n        (infinite || (T.x >= Math.min(A.x, B.x) && T.x <= Math.max(A.x, B.x)))\n      );\n    } else {\n      // Generic case: sloped line.\n      // we check that AT has the same slope as AB\n      // for the segment case we need both the vectors to have the same direction and for AT to be lte AB in size\n      // for the infinite case we check the absolute value of the slope, since direction is meaningless\n      const AB = createVector(A, B);\n      const AT = createVector(A, T);\n      const s = AT.divide(AB);\n      return infinite\n        ? Math.abs(s.x) === Math.abs(s.y)\n        : s.x === s.y && s.x >= 0 && s.x <= 1;\n    }\n  }\n\n  /**\n   * Use the ray casting algorithm to determine if point is in the polygon defined by points\n   * @see https://en.wikipedia.org/wiki/Point_in_polygon\n   * @param point\n   * @param points polygon points\n   * @returns\n   */\n  static isPointInPolygon(point: Point, points: Point[]) {\n    const other = new Point(point).setX(\n      Math.min(point.x - 1, ...points.map((p) => p.x)),\n    );\n    let hits = 0;\n    for (let index = 0; index < points.length; index++) {\n      const inter = this.intersectSegmentSegment(\n        // polygon side\n        points[index],\n        points[(index + 1) % points.length],\n        // ray\n        point,\n        other,\n      );\n      if (inter.includes(point)) {\n        // point is on the polygon side\n        return true;\n      }\n      hits += Number(inter.status === 'Intersection');\n    }\n    return hits % 2 === 1;\n  }\n\n  /**\n   * Checks if a line intersects another\n   * @see {@link https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection line intersection}\n   * @see {@link https://en.wikipedia.org/wiki/Cramer%27s_rule Cramer's rule}\n   * @param {Point} a1\n   * @param {Point} a2\n   * @param {Point} b1\n   * @param {Point} b2\n   * @param {boolean} [aInfinite=true] check segment intersection by passing `false`\n   * @param {boolean} [bInfinite=true] check segment intersection by passing `false`\n   * @return {Intersection}\n   */\n  static intersectLineLine(\n    a1: Point,\n    a2: Point,\n    b1: Point,\n    b2: Point,\n    aInfinite = true,\n    bInfinite = true,\n  ): Intersection {\n    const a2xa1x = a2.x - a1.x,\n      a2ya1y = a2.y - a1.y,\n      b2xb1x = b2.x - b1.x,\n      b2yb1y = b2.y - b1.y,\n      a1xb1x = a1.x - b1.x,\n      a1yb1y = a1.y - b1.y,\n      uaT = b2xb1x * a1yb1y - b2yb1y * a1xb1x,\n      ubT = a2xa1x * a1yb1y - a2ya1y * a1xb1x,\n      uB = b2yb1y * a2xa1x - b2xb1x * a2ya1y;\n    if (uB !== 0) {\n      const ua = uaT / uB,\n        ub = ubT / uB;\n      if (\n        (aInfinite || (0 <= ua && ua <= 1)) &&\n        (bInfinite || (0 <= ub && ub <= 1))\n      ) {\n        return new Intersection('Intersection').append(\n          new Point(a1.x + ua * a2xa1x, a1.y + ua * a2ya1y),\n        );\n      } else {\n        return new Intersection();\n      }\n    } else {\n      if (uaT === 0 || ubT === 0) {\n        const segmentsCoincide =\n          aInfinite ||\n          bInfinite ||\n          Intersection.isPointContained(a1, b1, b2) ||\n          Intersection.isPointContained(a2, b1, b2) ||\n          Intersection.isPointContained(b1, a1, a2) ||\n          Intersection.isPointContained(b2, a1, a2);\n        return new Intersection(segmentsCoincide ? 'Coincident' : undefined);\n      } else {\n        return new Intersection('Parallel');\n      }\n    }\n  }\n\n  /**\n   * Checks if a segment intersects a line\n   * @see {@link intersectLineLine} for line intersection\n   * @param {Point} s1 boundary point of segment\n   * @param {Point} s2 other boundary point of segment\n   * @param {Point} l1 point on line\n   * @param {Point} l2 other point on line\n   * @return {Intersection}\n   */\n  static intersectSegmentLine(\n    s1: Point,\n    s2: Point,\n    l1: Point,\n    l2: Point,\n  ): Intersection {\n    return Intersection.intersectLineLine(s1, s2, l1, l2, false, true);\n  }\n\n  /**\n   * Checks if a segment intersects another\n   * @see {@link intersectLineLine} for line intersection\n   * @param {Point} a1 boundary point of segment\n   * @param {Point} a2 other boundary point of segment\n   * @param {Point} b1 boundary point of segment\n   * @param {Point} b2 other boundary point of segment\n   * @return {Intersection}\n   */\n  static intersectSegmentSegment(\n    a1: Point,\n    a2: Point,\n    b1: Point,\n    b2: Point,\n  ): Intersection {\n    return Intersection.intersectLineLine(a1, a2, b1, b2, false, false);\n  }\n\n  /**\n   * Checks if line intersects polygon\n   *\n   * @todo account for stroke\n   *\n   * @see {@link intersectSegmentPolygon} for segment intersection\n   * @param {Point} a1 point on line\n   * @param {Point} a2 other point on line\n   * @param {Point[]} points polygon points\n   * @param {boolean} [infinite=true] check segment intersection by passing `false`\n   * @return {Intersection}\n   */\n  static intersectLinePolygon(\n    a1: Point,\n    a2: Point,\n    points: Point[],\n    infinite = true,\n  ): Intersection {\n    const result = new Intersection();\n    const length = points.length;\n\n    for (let i = 0, b1, b2, inter; i < length; i++) {\n      b1 = points[i];\n      b2 = points[(i + 1) % length];\n      inter = Intersection.intersectLineLine(a1, a2, b1, b2, infinite, false);\n      if (inter.status === 'Coincident') {\n        return inter;\n      }\n      result.append(...inter.points);\n    }\n\n    if (result.points.length > 0) {\n      result.status = 'Intersection';\n    }\n\n    return result;\n  }\n\n  /**\n   * Checks if segment intersects polygon\n   * @see {@link intersectLinePolygon} for line intersection\n   * @param {Point} a1 boundary point of segment\n   * @param {Point} a2 other boundary point of segment\n   * @param {Point[]} points polygon points\n   * @return {Intersection}\n   */\n  static intersectSegmentPolygon(\n    a1: Point,\n    a2: Point,\n    points: Point[],\n  ): Intersection {\n    return Intersection.intersectLinePolygon(a1, a2, points, false);\n  }\n\n  /**\n   * Checks if polygon intersects another polygon\n   *\n   * @todo account for stroke\n   *\n   * @param {Point[]} points1\n   * @param {Point[]} points2\n   * @return {Intersection}\n   */\n  static intersectPolygonPolygon(\n    points1: Point[],\n    points2: Point[],\n  ): Intersection {\n    const result = new Intersection(),\n      length = points1.length;\n    const coincidences: Intersection[] = [];\n\n    for (let i = 0; i < length; i++) {\n      const a1 = points1[i],\n        a2 = points1[(i + 1) % length],\n        inter = Intersection.intersectSegmentPolygon(a1, a2, points2);\n      if (inter.status === 'Coincident') {\n        coincidences.push(inter);\n        result.append(a1, a2);\n      } else {\n        result.append(...inter.points);\n      }\n    }\n\n    if (coincidences.length > 0 && coincidences.length === points1.length) {\n      return new Intersection('Coincident');\n    } else if (result.points.length > 0) {\n      result.status = 'Intersection';\n    }\n\n    return result;\n  }\n\n  /**\n   * Checks if polygon intersects rectangle\n   * @see {@link intersectPolygonPolygon} for polygon intersection\n   * @param {Point[]} points polygon points\n   * @param {Point} r1 top left point of rect\n   * @param {Point} r2 bottom right point of rect\n   * @return {Intersection}\n   */\n  static intersectPolygonRectangle(\n    points: Point[],\n    r1: Point,\n    r2: Point,\n  ): Intersection {\n    const min = r1.min(r2),\n      max = r1.max(r2),\n      topRight = new Point(max.x, min.y),\n      bottomLeft = new Point(min.x, max.y);\n\n    return Intersection.intersectPolygonPolygon(points, [\n      min,\n      topRight,\n      max,\n      bottomLeft,\n    ]);\n  }\n}\n"],"mappings":"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"}