兩個圓的公切線
圓上任意一點擁有唯一的圓心角
struct circle{
Point p;
double r;
// 通過圓心角求圓上某一點
Point point(double a){
return Point(p.x + cos(a) * r, c.y + sin(a) * r);
}
}
根據兩個圓的位置關系來確定情況
- 兩個圓內含,沒有公共點,沒有公切線
- 兩圓內切,有一個條公切線
- 兩圓完全重合,有無數條公切線
- 兩圓相交。有2條公切線
- 兩圓外切,有3條公切線
- 兩圓相離,有4條公切線
1 與 3 什么都不求,情況 2 可以直接求出直線AB的極角進而轉換為圓心角來求切點,連接切點和圓心,旋轉90度即可得到切線。
情況 4 有兩條外公切線,求出圓心距 \(d\) 以及\(|AG|\) 即可求出 \(\alpha\) 的大小,根據 \(\vec{AB}\) 的極角進行旋轉即可求出切點,進而得到切線

情況 5 的內切線類似情況2
情況 6 的外公切線與情況4完全一樣

情況 6 的內切線也是先求出圓心角 \(\alpha\) ,如何求?\(\cos \alpha = \frac{A_r+B_r}{|AB|}\)

// a[i] 存放第 i 條公切線與 圓A 的交點
int getTangents(circle A, circle B, Point*a, Point *b){
int cnt = 0;
// 以A為半徑更大的那個圓進行計算
if(A.r < B.r) return getTangents(B, A, b, a);
db d2 = (A.p-B.p).len2(); // 圓心距平方
db rdiff = A.r - B.r; // 半徑差
db rsum = A.r + B.r; //半徑和
if(d2 < rdiff * rdiff) return 0; // 情況1,內含,沒有公切線
Vector AB = B.p - A.p; // 向量AB,其模對應圓心距
db base = atan2(AB.y, AB.x); // 求出向量AB對應的極角
if(d2 == 0 && A.r == B.r) return -1;// 情況3,兩個圓重合,無限多切線
if(d2 == rdiff * rdiff){ // 情況2,內切,有一條公切線
a[cnt] = A.point(base);
b[cnt] = B.point(base);cnt++;
return 1;
}
// 求外公切線
db ang = acos((A.r - B.r) / sqrt(d2)); //求阿爾法
// 兩條外公切線
a[cnt] = A.point(base+ang); b[cnt] = B.point(base+ang); cnt++;
a[cnt] = A.point(base-ang); b[cnt] = B.point(base-ang); cnt++;
if(d2 == rsum * rsum){ // 情況5,外切,if里面求出內公切線
a[cnt] = A.point(base); b[cnt] = B.point(pi+base); cnt++;
}
else if(d2 > rsum * rsum){ //情況6,相離,再求出內公切線
db ang = acos((A.r + B.r) / sqrt(d2));
a[cnt] = A.point(base + ang); b[cnt] = B.point(pi+base+ang);cnt++;
a[cnt] = A.point(base - ang); b[cnt] = B.point(pi+base-ang);cnt++;
}
// 此時,d2 < rsum * rsum 代表情況 4 只有兩條外公切線
return cnt;
}
例題測試:https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/7/CGL_7_G