Consider two circles of radii $R$ and $r$ whose centres are separated by a distance $d$.
The area of overlap between them (assuming $|R-r| \le d \le R+r$) is given by the following formula, derived below.
$$ A(d; R, r) = \beta R^2 + \alpha r^2 - \frac{1}{2}r^2\sin 2\alpha - \frac{1}{2}R^2\sin 2\beta, $$ where $$ \cos \alpha = \frac{r^2 + d^2 - R^2}{2rd} \quad \mathrm{and} \quad \cos \beta = \frac{R^2 + d^2 - r^2}{2Rd}. $$
Inverting this formula to find $d$ for a given overlap area $A=A_0$ cannot be achieved analytically, but a numerical solution can be found as the root of the function $A(d; R, r) - A_0$.
Write a Python function which takes arguments A
, the target overlap area, and R
and r
the two circle radii and returns d
, the distance between the circle centres giving overlap area A
. Use, for example, brentq
.
First note that $A=0$ if $d \ge R+r$: the circles do not intersect at all in this case. Also, $A = \pi (\mathrm{min}(R,r))^2$ if $d \le |R-r|$: the smaller circle is entirely enclosed in the larger in this case. For the case of partial overlap, $|R-r| < d < R+r$ the area to find is shaded in the diagram below.
The cosine formula gives the angles $\alpha$ and $\beta$:
$$ \cos \alpha = \frac{r^2 + d^2 - R^2}{2rd} \quad \mathrm{and} \quad \cos \beta = \frac{R^2 + d^2 - r^2}{2Rd}. $$
The required area may be found as the sum of the two circle segments cut off by the chord CD. For the circle centred at A in the diagram above, its segment is the area of the circular sector ACD minus the area of the triangle ACD:
With $x=\mathrm{AE} = R\cos\beta$ and $h=\mathrm{CE}=R\sin\beta$, triangle ACD has area $xh=R^2\cos\beta\sin\beta=\frac{1}{2}R^2\sin2\beta$. The area of the circular sector ACD is simply $\beta R^2$ with $\beta$ measured in radians (the entire circle has area $\pi R^2$ and we want the fraction $\beta/2\pi$ of it).
Therefore, the shaded circular segment has area $\beta R^2 - \frac{1}{2}R^2\sin2\beta$.
Similarly, the area of the segment of the circle centred at B cut off by chord CD is $\alpha r^2 - \frac{1}{2}r^2\sin2\alpha$.
The total intersection area is therefore
$$ A = \alpha r^2 + \beta R^2 - \frac{1}{2}r^2\sin2\alpha - \frac{1}{2}R^2\sin2\beta. $$
Here's one solution. Note that if you are using Python 2 you should ensure that d
, R
and r
are passed as float
s, perhaps by adding the line
d, R, r = float(d), float(R), float(r)
at the beginning of the intersection_area
function definition.
import numpy as np
from scipy.optimize import brentq
def intersection_area(d, R, r):
"""Return the area of intersection of two circles.
The circles have radii R and r, and their centres are separated by d.
"""
if d <= abs(R-r):
# One circle is entirely enclosed in the other.
return np.pi * min(R, r)**2
if d >= r + R:
# The circles don't overlap at all.
return 0
r2, R2, d2 = r**2, R**2, d**2
alpha = np.arccos((d2 + r2 - R2) / (2*d*r))
beta = np.arccos((d2 + R2 - r2) / (2*d*R))
return ( r2 * alpha + R2 * beta -
0.5 * (r2 * np.sin(2*alpha) + R2 * np.sin(2*beta))
)
def find_d(A, R, r):
"""
Find the distance between the centres of two circles giving overlap area A.
"""
# A cannot be larger than the area of the smallest circle!
if A > np.pi * min(r, R)**2:
raise ValueError("Intersection area can't be larger than the area"
" of the smallest circle")
if A == 0:
# If the circles don't overlap, place them next to each other
return R+r
if A < 0:
raise ValueError('Negative intersection area')
def f(d, A, R, r):
return intersection_area(d, R, r) - A
a, b = abs(R-r), R+r
d = brentq(f, a, b, args=(A, R, r))
return d
For example,
In [x]: find_d(0.35, 1, 0.4))
0.8489711717559927