/*! \brief intersection functions \see http://www.kevlindev.com/gui/math/intersection/index.htm */ import { fuzzyIsNull, fuzzyEqual } from '../math/float'; import { Vector2D } from './Vector2D'; //! \see https://stackoverflow.com/questions/328107/how-can-you-determine-a-point-is-between-two-other-points-on-a-line-segment export function intersectLinePointD(p: Vector2D, r: Vector2D, q: Vector2D, epsilon?: number): undefined | Vector2D[] { if (r.fuzzyIsNull(epsilon)) { // null line if (p.fuzzyEquals(q, epsilon)) return [new Vector2D(q.x, q.y)]; return; } const s = q.minus(p); if (!fuzzyIsNull(s.crossProduct(r), epsilon)) return; const dp = s.dotProduct(r); if (dp < 0) return; if (dp > r.lengthSquared()) return; return [new Vector2D(q.x, q.y)]; } export function intersectLinePointE(p1: Vector2D, p2: Vector2D, p3: Vector2D, epsilon?: number): undefined | Vector2D[] { return intersectLinePointD(p1, p2.minus(p1), p3, epsilon); } export { intersectLinePointE as intersectLinePoint }; //! \see http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap export function intersectIntervals(x0: number, x1: number, y0: number, y1: number): undefined | number[] { if ((y1 < x0) || (y0 > x1)) return; if (x0 === y1) return [x0]; // or [y1] if (x1 === y0) return [x1]; // or [y0] return [(y0 < x0 ? x0 : y0), (y1 > x1 ? x1 : y1)]; } //! \see http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect export function intersectLineLineD(p: Vector2D, r: Vector2D, q: Vector2D, s: Vector2D, epsilon?: number): undefined | Vector2D[] { // direction vectors const d = q.minus(p); const numerator = d.crossProduct(r); const denominator = r.crossProduct(s); if (fuzzyIsNull(denominator, epsilon)) { if (fuzzyIsNull(numerator, epsilon)) { const denominator = r.dotProduct(r); const numerator = s.dotProduct(r); if (fuzzyIsNull(denominator, epsilon)) return; const t0 = d.dotProduct(r) / denominator; const t1 = t0 + numerator / denominator; const ta = numerator < 0 ? intersectIntervals(t1, t0, 0, 1) : intersectIntervals(t0, t1, 0, 1); if (ta !== undefined) return ta.map(t => p.plus(r.mul(t))); return; } return; } const u = numerator / denominator; const t = d.crossProduct(s) / denominator; if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) return [q.plus(s.mul(u))]; // or [p.plus(r.mul(t))] } export function intersectLineLineE(p1: Vector2D, p2: Vector2D, q1: Vector2D, q2: Vector2D, epsilon?: number): undefined | Vector2D[] { // endpoints return intersectLineLineD(p1, p2.minus(p1), q1, q2.minus(q1), epsilon); } export { intersectLineLineE as intersectLineLine }; //! \see http://stackoverflow.com/questions/1073336/circle-line-segment-collision-detection-algorithm function intersectLineCircleD(p: Vector2D, d: Vector2D, center: Vector2D, radius: number, epsilon?: number): undefined | Vector2D[] { const f = p.minus(center); const a = d.dotProduct(d); const b = 2 * f.dotProduct(d); const c = f.dotProduct(f) - radius * radius; if (fuzzyIsNull(a, epsilon)) { // null line (e.g. point) if (fuzzyIsNull(c, epsilon)) // point lies on circle return [new Vector2D(p.x, p.y)]; return; } let discriminant = b * b - 4 * a * c; if (discriminant >= 0) { discriminant = Math.sqrt(discriminant); const t1 = (-b - discriminant) / (2 * a); const t2 = (-b + discriminant) / (2 * a); let ret: undefined | Vector2D[]; if (t1 >= 0 && t1 <= 1) ret = [p.plus(d.mul(t1))]; if (!fuzzyEqual(t1, t2, epsilon)) { if (t2 >= 0 && t2 <= 1) { ret = ret || []; ret.push(p.plus(d.mul(t2))); } } return ret; } } export function intersectLineCircleE(p1: Vector2D, p2: Vector2D, center: Vector2D, radius: number, epsilon?: number): undefined | Vector2D[] { return intersectLineCircleD(p1, p2.minus(p1), center, radius, epsilon); } export { intersectLineCircleE as intersectLineCircle }; //! \see http://stackoverflow.com/questions/3349125/circle-circle-intersection-points export function intersectCircleCircle(c1: Vector2D, r1: number, c2: Vector2D, r2: number, epsilon?: number): undefined | Vector2D[] { const r_max = r1 + r2; const r_min = Math.abs(r1 - r2); const d = c2.minus(c1).length(); if (d > r_max) return; if (d < r_min) return; if (fuzzyIsNull(d, epsilon)) return []; const a = (r1 * r1 - r2 * r2 + d * d) / (2 * d); const h = Math.sqrt(r1 * r1 - a * a); const p = c1.plus(c2.minus(c1).mul(a / d)); const b = h / d; const ret = [new Vector2D(p.x - b * (c2.y - c1.y), p.y + b * (c2.x - c1.x))]; if (!fuzzyIsNull(b)) ret.push(new Vector2D(p.x + b * (c2.y - c1.y), p.y - b * (c2.x - c1.x))); return ret; }