#math#algorithm

Let $P$ be the intersection point, $A$ is the anchor point of the ray, $B$ is the endpoint of the ray (assuming the ray is length-limited) and $C$ is the circle's center point, with radius $r$.

\[ \left\{\begin{align} a &= (B_x - A_x) ^ 2 + (B_y - A_y) ^ 2 \\ b &= 2((B_x - A_x)(A_x - C_x) + (B_y - A_y)(A_y - C_y)) \\ c &= (A_x - C_x) ^ 2 + (A_y - C_y) ^ 2 - r ^ 2 \\ \Delta &= b ^ 2 - 4ac \end{align}\right. \]

Now we have 3 cases:


If $\Delta = 0$, we have $t = \tfrac{-b}{2a}$, and if $t \geq 0$, we can calculate the position of $P$ as:

\[ \left\{\begin{align} P_x &= t(B_x - A_x) + A_x \\ P_y &= t(B_y - A_y) + A_y \end{align}\right. \]

If $\Delta > 0$, we have:

\[ \left\{\begin{align} t_1 &= \tfrac{-b -\sqrt{\Delta}}{2a} \\ t_2 &= \tfrac{-b +\sqrt{\Delta}}{2a} \\ \end{align}\right. \]

In this case, if $t_i \geq 0$ then the two intersection points can be calculated as:

\[ \left\{\begin{align} P_{i_x} &= t_i(B_x - A_x) + A_x \\ P_{i_y} &= t_i(B_y - A_y) + A_y \end{align}\right. \]

Implementation:

function getIntersectionPoint(ray, circle) {
  const [A, B] = ray;
  const C = { x: circle.x, y: circle.y };
  const r = circle.radius;

  const a = (B.x - A.x) ** 2 + (B.y - A.y) ** 2;
  const b = 2 * ((B.x - A.x) * (A.x - C.x) + (B.y - A.y) * (A.y - C.y));
  const c = (A.x - C.x) ** 2 + (A.y - C.y) ** 2 - r ** 2;
  const discriminant = b ** 2 - 4 * a * c;

  const result = [];
  
  if(discriminant === 0) {
    const t = -b / (2 * a);

    if(t >= 0) {
      const x = t * (B.x - A.x) + A.x;
      const y = t * (B.y - A.y) + A.y;

      result.push({ x, y });
    }
  } else if(discriminant > 0) {
    const discriminantSqrt = Math.sqrt(discriminant);
    const t1 = (-b + discriminantSqrt) / (2 * a);
    const t2 = (-b - discriminantSqrt) / (2 * a);

    if(t1 >= 0) {
      const x = t1 * (B.x - A.x) + A.x;
      const y = t1 * (B.y - A.y) + A.y;

      result.push({ x, y })
    }

    if(t2 >= 0) {
      const x = t2 * (B.x - A.x) + A.x;
      const y = t2 * (B.y - A.y) + A.y;

      result.push({ x, y });
    }
  }

  return result;
}

References: