• 【计算几何模板】求两个圆的交点


    问题描述:

    给两个相交的圆,第一个圆的圆心为((x_1, \, y_1)),半径为(r_1),第二个圆的圆心为((x_2, \, y_2)),半径为(r_2),求两个圆的交点。

    问题分析:

    《训练指南》上求两圆交点的模板用了atan2acos等库函数,精度损失比较严重。
    下面介绍一种精度损失较小的做法:
    原文地址

    首先回顾一下圆的两种表示方法:

    • 圆的标准方程:((x-x_0)^2+(y-y_0)^2=r^2)
    • 圆的参数方程:(left{egin{matrix} x=x_0+rcdot cos heta \ y=y_0+rcdot sin heta end{matrix} ight.)

    将第一个圆的参数方程带入第二个圆的标准方程:
    ((x_1+r_1cos heta-x_2)^2+(y_1+r_1sin heta-y_2)^2=r_2^2)
    展开后得到:
    (2r_1(x_1-x_2)cos heta+2r_1(y_1-y_2)sin heta=r_2^2-r_1^2-(x_1-x_2)^2-(y_1-y_2)^2)
    令:
    (a=2r_1(x_1-x_2))
    (b=2r_1(y_1-y_2))
    (c=r_2^2-r_1^2-(x_1-x_2)^2-(y_1-y_2)^2)
    原式变为:
    (a cos heta+b sin heta=c)
    (cos heta=x, \, sin heta=sqrt{1-x^2}),关于(sin heta)的正负后面再判断。
    代入方程,得到,(ax+bsqrt{1-x^2}=c)
    移项再两边平方,((ax-c)^2=b^2(1-x^2))
    整理得,((a^2+b^2)x^2-2acx+c^2-b^2=0)
    下面就是解一元二次方程了。

    (sin heta)(cos heta)代回到第一个圆的参数方程,能得到交点的坐标。
    如果该点不在第二个圆上,说明(sin heta)是个负数,还需要对这个交点稍作调整。
    还有一种特殊情况就是,如果已经确定有两个不同的交点,但解出来的(cos heta)值只有一个。
    说明对应的(sin heta)值必然一正一负。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    
    typedef long double LD;
    
    const LD eps = 1e-10;
    
    int dcmp(LD x) {
    	if(fabs(x) < eps) return 0;
    	return x < 0 ? -1 : 1;
    }
    
    LD sqr(LD x) { return x * x; }
    
    struct Point
    {
    	LD x, y;
    	Point(LD x = 0, LD y = 0):x(x), y(y) {}
    	void read() { cin >> x >> y; }
    };
    
    Point operator - (const Point& A, const Point& B) {
    	return Point(A.x - B.x, A.y - B.y);
    }
    
    bool operator == (const Point& A, const Point& B) {
    	return dcmp(A.x - B.x) == 0 && dcmp(A.y - B.x) == 0;
    }
    
    LD Dot(const Point& A, const Point& B) {
    	return A.x * B.x + A.y * B.y;
    }
    
    LD Length(const Point& A) { return sqrt(Dot(A, A)); }
    
    struct Circle
    {
    	Point c;
    	LD r;
    	Circle() {}
    	Circle(Point c, LD r):c(c), r(r) {}
    };
    
    int getCircleIntersection(Circle C1, Circle C2) {
    	LD &r1 = C1.r, &r2 = C2.r;
    	LD &x1 = C1.c.x, &x2 = C2.c.x, &y1 = C1.c.y, &y2 = C2.c.y;
    	LD d = Length(C1.c - C2.c);
    	if(dcmp(fabs(r1-r2) - d) > 0) return -1;
    	if(dcmp(r1 + r2 - d) < 0) return 0;
    	LD d2 = Dot(C1.c - C2.c, C1.c - C2.c);
    	LD a = r1*(x1-x2)*2, b = r1*(y1-y2)*2, c = r2*r2-r1*r1-d*d;
    	LD p = a*a+b*b, q = -a*c*2, r = c*c-b*b;
    
    	LD cosa, sina, cosb, sinb;
    	//One Intersection
    	if(dcmp(d - (r1 + r2)) == 0 || dcmp(d - fabs(r1 - r2)) == 0) {
    		cosa = -q / p / 2;
    		sina = sqrt(1 - sqr(cosa));
    		Point p(x1 + C1.r * cosa, y1 + C1.r * sina);
    		if(!OnCircle(p, C2)) p.y = y1 - C1.r * sina;
    		inter.push_back(p);
    		return 1;
    	}
    	//Two Intersections
    	LD delta = sqrt(q * q - p * r * 4);
    	cosa = (delta - q) / p / 2;
    	cosb = (-delta - q) / p / 2;
    	sina = sqrt(1 - sqr(cosa));
    	sinb = sqrt(1 - sqr(cosb));
    	Point p1(x1 + C1.r * cosa, y1 + C1.r * sina);
    	Point p2(x1 + C1.r * cosb, y1 + C1.r * sinb);
    	if(!OnCircle(p1, C2)) p1.y = y1 - C1.r * sina;
    	if(!OnCircle(p2, C2)) p2.y = y1 - C1.r * sinb;
    	if(p1 == p2)  p1.y = y1 - C1.r * sina;
    	inter.push_back(p1);
    	inter.push_back(p2);
    	return 2;
    }
    
  • 相关阅读:
    现代软件工程 第一章 概论 第4题——邓琨
    现代软件工程 第一章 概论 第9题——邓琨
    现代软件工程 第一章 概论 第7题——张星星
    现代软件工程 第一章 概论 第5题——韩婧
    hdu 5821 Ball 贪心(多校)
    hdu 1074 Doing Homework 状压dp
    hdu 1074 Doing Homework 状压dp
    hdu 1069 Monkey and Banana LIS变形
    最长上升子序列的初步学习
    hdu 1024 Max Sum Plus Plus(m段最大子列和)
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/5098304.html
Copyright © 2020-2023  润新知