概要
円1: 中心 \((x_1, y_1)\) 半径 \(r_1\) と 円2: 中心 \((x_2, y_2)\) 半径 \(r_2\) の2つの円がなす共通接線の接点を求める。
(円1の方の)共通内接線(最大2本)と共通外接線(最大2本)の接点を計算する。
もしくは、接線上の2点を求める。
具体的計算
2つの円の中心間の距離を \(r_0\) とし、 \(d_x = x_2 - x_1\), \(d_y = y_2 - y_1\) とした時、接点を
-
\(\displaystyle \left( \begin{array}{c} x_1 \\ y_2 \end{array} \right) + \frac{r_1}{r_0} R(\theta) \left( \begin{array}{c} d_x \\ d_y \end{array} \right)\)
として考える (\(R(\theta)\) は \(2 \times 2\) の回転行列)
この時、
-
共通内接線の場合は \(\displaystyle |\cos \theta| = \frac{|r_1 - r_2|}{r_0}\)
-
共通外接線の場合は \(\displaystyle |\cos \theta| = \frac{r_1 + r_2}{r_0}\)
を満たす。(実際に図を書いてみると確認できる)
この時 \(|\cos \theta| > 1\) の場合は接線が存在しない。また、 \(|\cos \theta| = 1\) の場合は接線が1本のみ存在する。
実装(円1の接点)
AOJ: "CGL_7_G: Circles - Common Tangent"で提出した実装のPython3版
x1, y1, r1 = map(int, input().split())
x2, y2, r2 = map(int, input().split())
from math import sqrt
def solve(x1, y1, r1, x2, y2, r2):
result = []
xd = x2 - x1; yd = y2 - y1
rr0 = xd**2 + yd**2
if (r1 - r2)**2 <= rr0:
# 共通外接線
cv = r1 - r2
if rr0 == (r1 - r2)**2:
# 円が内接する場合 => 共通外接線は1本
result.append((x1 + r1*cv*xd/rr0, y1 + r1*cv*yd/rr0))
else:
sv = sqrt(rr0 - cv**2)
# (x1, y1) + r1/r0*R(±θ)(x2 - x1, y2 - y1)
result.append((x1 + r1*(cv*xd - sv*yd)/rr0, y1 + r1*(sv*xd + cv*yd)/rr0))
result.append((x1 + r1*(cv*xd + sv*yd)/rr0, y1 + r1*(-sv*xd + cv*yd)/rr0))
if (r1 + r2)**2 <= rr0:
# 共通内接線
cv = r1 + r2
if rr0 == (r1 + r2)**2:
# 円が外接する場合 => 共通内接線は1本
result.append((x1 + r1*cv*xd/rr0, y1 + r1*cv*yd/rr0))
else:
sv = sqrt(rr0 - cv**2)
# (x1, y1) + r1/r0*R(±θ)(x2 - x1, y2 - y1)
result.append((x1 + r1*(cv*xd - sv*yd)/rr0, y1 + r1*(sv*xd + cv*yd)/rr0))
result.append((x1 + r1*(cv*xd + sv*yd)/rr0, y1 + r1*(-sv*xd + cv*yd)/rr0))
return result
result = sorted(solve(x1, y1, r1, x2, y2, r2))
if result:
print("\n".join("%.08f %.08f" % p for p in result))
Verified
-
AOJ: "CGL_7_G: Circles - Common Tangent": source (Python2, 0.01sec)
実装(接線上の2点)
def common_tangent_lines(x1, y1, r1, x2, y2, r2):
result = []
xd = x2 - x1; yd = y2 - y1
rr0 = xd**2 + yd**2
if (r1 - r2)**2 <= rr0:
cv = r1 - r2
if rr0 == (r1 - r2)**2:
bx = r1*cv*xd/rr0
by = r1*cv*yd/rr0
result.append([
(x1 + bx, y1 + by),
(x1 - yd + bx, y1 + xd + by),
])
else:
sv = (rr0 - cv**2)**.5
px = (cv*xd - sv*yd); py = (sv*xd + cv*yd)
result.append([
(x1 + r1*px/rr0, y1 + r1*py/rr0),
(x2 + r2*px/rr0, y2 + r2*py/rr0),
])
qx = (cv*xd + sv*yd); qy = (-sv*xd + cv*yd)
result.append([
(x1 + r1*qx/rr0, y1 + r1*qy/rr0),
(x2 + r2*qx/rr0, y2 + r2*qy/rr0),
])
if (r1 + r2)**2 <= rr0:
cv = r1 + r2
if rr0 == (r1 + r2)**2:
bx = r1*cv*xd/rr0
by = r1*cv*yd/rr0
result.append([
(x1 + bx, y1 + by),
(x1 - yd + bx, y1 + xd + by),
])
else:
sv = (rr0 - cv**2)**.5
px = (cv*xd - sv*yd); py = (sv*xd + cv*yd)
result.append([
(x1 + r1*px/rr0, y1 + r1*py/rr0),
(x2 - r2*px/rr0, y2 - r2*py/rr0),
])
qx = (cv*xd + sv*yd); qy = (-sv*xd + cv*yd)
result.append([
(x1 + r1*qx/rr0, y1 + r1*qy/rr0),
(x2 - r2*qx/rr0, y2 - r2*qy/rr0),
])
return result
Verified
-
AOJ: "2201: Immortal Jewels": source (Python3, 6.64sec)