圆的数据结构定义

1
2
3
4
struct circle {
point c; // 圆的中心
real r; // 半径
};

注意事项

  • 尽量避免直接用 (long) double 进行操作,考虑到浮点误差,更建议使用向量操作

圆与直线

过某点做某圆的切线

过一个点 PP 做圆 CC 的切线

若点 PP 在圆上,则切线垂直于半径 CPCP,直接令直线 s=P, d=perp(CP)\texttt{s=\(P\), d=perp(\(\vec{CP}\))}.

否则 PP 在圆外,否则没有切线。此时有两条切线,且应当关于 CPCP 对称。考虑用误差更小的向量组合求出 DD,然后另一边也能求了。

弦切角定理
弦切角定理

CP=d|CP|=d,推理 ΔPCD\Delta PCD 的面积,发现 AD=rd2r2d,AP=d2r2d|AD| = \frac{r\sqrt{d^2-r^2}}{d}, |AP|=\frac{d^2-r^2}{d}

所以 D=P+APPC±ADperp(PC)D = P+|AP|\cdot \vec{PC} \plusmn |AD|\cdot \texttt{perp}(\vec{PC})

Reference Code
1
2
3
4
5
6
7
8
9
10
11
12
std::vector<line> tangent(point x, circle c) {
real d = distance(c.c, x);
std::vector<line> res = {};
if (d == c.r) res.push_back(line{x, perp(c.c - x)}); // 点在圆上
else if(d > c.r) { // 点在圆外
real c1 = (d.sqr() - c.r.sqr()) / d;
real c2 = c.r * (sqrt(d.sqr() - c.r.sqr())) / d;
res.push_back(line{x, (c.c-x).rescale(c1) + (c.c-x).Rrot().rescale(c2)});
res.push_back(line{x, (c.c-x).rescale(c1) + (c.c-x).Lrot().rescale(c2)});
} // 否则点在圆内
return res;
}