Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • 线上赛板子(实时更新)
  • 数据结构

  • 数学

  • 计算几何

    • 二维计算几何基础(总结)
    • 三维计算几何基础
    • 圆相关计算
      • 圆的定义
      • 直线和圆的交点
      • 两圆相交
      • 过定点做圆的切线
      • 两圆的公切线
    • 凸包
    • 半平面交
  • 动态规划

  • 图论

  • 字符串

  • 杂项

  • 算法笔记
  • 计算几何
martian148
2024-08-29
目录

圆相关计算

# 圆相关计算

# 圆的定义

圆上一点拥有唯一的圆心角,在定义圆的时候,可以加一个通过圆心角求坐标的函数

struct Circle {
    Point c;
    double r;
    Circle(Point c = Point(), double r = 0) : c(c), r(r) {}
    Point point(double a) { return Point(c.x + cos(a) * r, c.y + sin(a) * r); } // 通过圆心角求圆上的点
};
1
2
3
4
5
6

# 直线和圆的交点

假设直线为 ABABAB 圆的圆心为 CCC,半径为 rrr。

第一种方法就是解方程组。设交点为 P=A+t(B−A)P=A+t(B-A)P=A+t(B−A) ,代入圆方程的整理后得到 (at+b)2+(ct+d)2=r2(at+b)^2+(ct+d)^2=r^2(at+b)2+(ct+d)2=r2 ,进一步得到一元二次方程 et2+ft+g=0et^2+ft+g=0et2+ft+g=0 根据判别式就可以判断是相离,相切,还是相交了

int line_circle_intersection(Point A, Point B, Circle C, vector<Point> &sol) {
    double a = B.x - A.x, b = A.x - C.c.x, c = B.y - A.y, d = A.y - C.c.y;
    double e = a * a + c * c, f = 2 * (a * b + c * d), g = b * b + d * d - C.r * C.r;
    double delta = f * f - 4 * e * g;
    if (dcmp(delta) < 0) return 0;
    if (dcmp(delta) == 0) {
        double t = -f / (2 * e);
        sol.push_back(Point(A.x + t * a, A.y + t * c));
        return 1;
    }
    double t1 = (-f - sqrt(delta)) / (2 * e), t2 = (-f + sqrt(delta)) / (2 * e);
    sol.push_back(Point(A.x + t1 * a, A.y + t1 * c));
    sol.push_back(Point(A.x + t2 * a, A.y + t2 * c));
    return 2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 两圆相交

image.png

假定圆心分别为 C1,C2C_1,C_2C1​,C2​ 半径为 r1,r2r1,r2r1,r2 圆心距为 ddd,通过余弦定理能算出 C1,C2C_1,C_2C1​,C2​ 到 C1P1C_1P_1C1​P1​ 的角 dadada 根据向量 C1C2C1C2C1C2 的极角 aaa,加减 dadada 就可以得到 C1P1C_1P_1C1​P1​ 和 C1P2C_1P_2C1​P2​ 的极角。有了极角,就可以很方便地计算出 P1P_1P1​ 和 P2P_2P2​ 的坐标了

//求两圆交点
double angle0(Vector v) { return atan2(v.y, v.x); } //求极角,注意不要错用 atan 了

int circle_circle_intersection(Circle C1, Circle C2, vector<Point> &sol) {
    double d = length(C1.c - C2.c);
    if (dcmp(d) == 0) {
        if (dcmp(C1.r - C2.r) == 0) return -1; // 重合
        return 0;
    }
    if (dcmp(C1.r + C2.r - d) < 0) return 0;  // 外离
    if (dcmp(fabs(C1.r - C2.r) - d) > 0) return 0;  // 内含
    double a = angle0(C2.c - C1.c); // C1 到 C2 的向量的极角
    double da = acos((C1.r * C1.r + d * d - C2.r * C2.r) / (2 * C1.r * d)); // 余弦定理
    Point p1 = C1.point(a - da), p2 = C1.point(a + da); // 两个交点
    sol.push_back(p1);
    if (p1 == p2) return 1;
    sol.push_back(p2);
    return 2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 过定点做圆的切线

image.png

先求出 pqpqpq 的距离, pcpcpc 与 pqpqpq 的夹角 angangang ,则向量 pcpcpc 的极角加减 angangang 就是两条切线的极角

int tangents(Point P, Circle C, Vector* v) {
    Vector u = C.c - P;
    double dist = length(u);
    if (dist < C.r) return 0;
    if (dcmp(dist - C.r) == 0) {
        v[0] = rotate(u, PI / 2);
        return 1;
    }
    double ang = asin(C.r / dist);
    v[0] = rotate(u, -ang);
    v[1] = rotate(u, ang);
    return 2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 两圆的公切线

两个圆的公切线,根据两圆的圆心距从小到大排列,一共有 6 种情况

  • 情况一:两圆完全重合。有无数条公切线。
  • 情况二:两圆内含,没有公共点。没有公切线
  • 情况三:两圆内切,有 111 条外公切线
  • 情况四:两圆相交,有 222 条外公切线
  • 情况五:两圆外切,有 333 条公切线,其中一条内公切线,两条外公切线
  • 情况六:两圆相离,有 444 条公切线,其中两条内公切线,两条外公切线

可以根据圆心距和半径的关系辨别出这 666 种情况

对于情况六种的内公切线

内切只需要用反三角函数求出 C1C2C_1C_2C1​C2​ 和 r1r_1r1​ 的夹角 angangang 就可以解决了

image.png

外切也是同理

image.png
上次更新: 2025/04/08, 18:03:31
三维计算几何基础
凸包

← 三维计算几何基础 凸包→

最近更新
01
Java基础语法
05-26
02
开发环境配置
05-26
03
pink 老师 JavaScript 学习笔记
05-26
更多文章>
Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式