raw Math

Let two disks have centers \(\mathbf{A}\) and \(\mathbf{B}\), radii \(r_A\) and \(r_B\), and center distance

\[ d=\lVert\mathbf{B}-\mathbf{A}\rVert =\sqrt{(B_x-A_x)^2+(B_y-A_y)^2}. \]

Their common area depends only on \(r_A\), \(r_B\), and \(d\). The absolute positions and orientation of the circles do not affect it.

The intersection is the lens contained in both disks.

Classify the Geometry

Before using any inverse trigonometric function, classify the position of the disks:

The containment case includes concentric circles, where \(d=0\). Handling it before division by \(d\) removes the singularity from the partial-overlap formulas.

Decompose the Lens

Assume partial overlap. Let \(\mathbf{P}_1\) and \(\mathbf{P}_2\) be the boundary intersections. The segments from each center to these points split the lens into two circular segments.

Each circular segment equals a sector minus its isosceles triangle.

The law of cosines gives the central angles

\[ \alpha=2\cos^{-1}\left(\frac{d^2+r_A^2-r_B^2}{2dr_A}\right), \qquad \beta=2\cos^{-1}\left(\frac{d^2+r_B^2-r_A^2}{2dr_B}\right). \]

A sector of radius \(r\) and angle \(\theta\), measured in radians, has area \(r^2\theta/2\). The triangle formed by its two radii has area \(r^2\sin\theta/2\). Therefore a circular segment has area

\[ A_{\mathrm{segment}}=\frac{r^2}{2}(\theta-\sin\theta). \]

Adding the two segments produces the lens area:

\[ \boxed{ A=\frac{1}{2}\left[ r_A^2(\alpha-\sin\alpha)+ r_B^2(\beta-\sin\beta) \right]. } \]

Closed Form

Substituting the angles and combining the two triangle terms gives an equivalent expression:

\[ \begin{aligned} A={}&r_A^2\cos^{-1}\left(\frac{d^2+r_A^2-r_B^2}{2dr_A}\right) +r_B^2\cos^{-1}\left(\frac{d^2+r_B^2-r_A^2}{2dr_B}\right)\\ &-\frac{1}{2}\sqrt{(-d+r_A+r_B)(d+r_A-r_B)(d-r_A+r_B)(d+r_A+r_B)}. \end{aligned} \]

The square-root factor is four times the area of the triangle with side lengths \(r_A\), \(r_B\), and \(d\). The factorized form also makes the partial-overlap conditions visible: every factor is nonnegative exactly when those three lengths can form a triangle.

JavaScript Implementation

The implementation applies the geometric cases before evaluating the partial-overlap formula. Clamping the cosine arguments to \([-1,1]\) prevents tiny floating-point errors near tangency from turning a valid result into NaN.

function circleIntersectionArea(circleA, circleB) {
  const values = [
    circleA.x, circleA.y, circleA.r,
    circleB.x, circleB.y, circleB.r
  ];

  if (!values.every(Number.isFinite)) {
    throw new TypeError("Circle coordinates and radii must be finite");
  }
  if (circleA.r < 0 || circleB.r < 0) {
    throw new RangeError("Circle radii must be non-negative");
  }

  const radiusA = circleA.r;
  const radiusB = circleB.r;
  const distance = Math.hypot(
    circleB.x - circleA.x,
    circleB.y - circleA.y
  );

  if (distance >= radiusA + radiusB) {
    return 0;
  }
  if (distance <= Math.abs(radiusA - radiusB)) {
    const smallerRadius = Math.min(radiusA, radiusB);
    return Math.PI * smallerRadius * smallerRadius;
  }

  const distanceSquared = distance * distance;
  const radiusASquared = radiusA * radiusA;
  const radiusBSquared = radiusB * radiusB;
  const clamp = value => Math.max(-1, Math.min(1, value));

  const alpha = 2 * Math.acos(clamp(
    (distanceSquared + radiusASquared - radiusBSquared) /
    (2 * distance * radiusA)
  ));
  const beta = 2 * Math.acos(clamp(
    (distanceSquared + radiusBSquared - radiusASquared) /
    (2 * distance * radiusB)
  ));

  return 0.5 * (
    radiusASquared * (alpha - Math.sin(alpha)) +
    radiusBSquared * (beta - Math.sin(beta))
  );
}

For two unit circles one unit apart, \(\alpha=\beta=2\pi/3\), and the area is

\[ A=\frac{2\pi}{3}-\frac{\sqrt{3}}{2}\approx1.2283697. \]

The returned value has squared coordinate units. If coordinates and radii are measured in centimeters, for example, the result is measured in square centimeters.