计算几何学 | 圆与圆的交点 | Cross Points of Circles | C/C++实现

问题描述

求2个圆c1、c2的交点。

输入:
输入按照下述格式给出:
c1x c1y c1r
c2x c2y c2r
c1x、c1y、c1r分别表示第1个圆的圆心x坐标、y坐标以及半径。同理,c2x、c2y、c2r表示第2个圆的坐标与半径。上述输入均为整数。

输出:
按下述规则输出交点p1、p2的坐标(x1, y1)、(x2, y2),相邻数据之间用空格隔开:
只有1个交点时输出2个相同的坐标
先输出x坐标较小的点。x坐标相同时先输出y坐标较小的点
允许误差不超过0.000001。

限制:
2个圆存在交点且圆心不同
-10000 ≤ c1x, c1y, c2x, c2y ≤ 10000
1 ≤ c1r, c2r ≤ 10000

输入示例

0 0 2
2 0 2

输出示例

1.0000000 -1.7320508 1.0000000 1.7320508

讲解

求两个圆交点的方法有很多,这里我们学习的算法使用了向量运算和余弦定理。

先求出两个圆的圆心距d。这个圆心距就是c1.到c2.的向量(反过来亦可)的大小
由两圆圆心以及其中一个交点所组成的三角形的三条边分别为c1.r、c2.r、d,根据余弦定理可求出向量c2.c - c1.c与c1.c到某交点的向量的夹角a。然后我们再求出c2.c - c1.c与x轴的夹角t备用
这样一来,我们所求的交点就是以圆心c1.c为起点,大小为c1.r,角度为t + a和t - a的两个向量
求圆c1与圆c2交点的程序可以像下面这样写。

圆c1与圆c2的交点:

double arg(Vector p) { return atan2(p.y, p.x); }
Vector polar(double a, double r) { return Point(cos(r) * a, sin(r) * a); }

pair<Point, Point> getCrossPoints(Circle c1, Circle c2) {
	assert(intersect(c1, c2));
	double d = abs(c1.c - c2.c);
	double a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
	double t = arg(c2.c - c1.c);
	return make_pair(c1.c + polar(c1.r, t + a), c1.c + polar(c1.r, t - a));
}

AC代码如下

#include
#include
#include
#include
using namespace std;

#define EPS (1e-10)
#define equals(a, b) (fabs((a) - (b)) < EPS)

class Point {//Point类,点 
	public:
		double x, y;
		
		Point(double x = 0, double y = 0): x(x), y(y) {}

		Point operator + (Point p) { return Point(x + p.x, y + p.y); }
		Point operator - (Point p) { return Point(x - p.x, y - p.y); }
		Point operator * (double a) { return Point(a * x, a * y); }
		Point operator / (double a) { return Point(x / a, y / a); }

		double abs() { return sqrt(norm()); }
		
		double norm() { return x * x + y * y; }
		
		bool operator < (const Point &p) const {
			return x != p.x ? x < p.x : y < p.y;
		}

		bool operator == (const Point &p) const {
			return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS;
		}
};

typedef Point Vector;//Vector类,向量 

struct Segment{//Segment 线段 
	Point p1, p2;
};

typedef Segment Line;//Line 直线 

class Circle {//Circle 圆 
	public:
		Point c;
		double r;
		Circle(Point c = Point(), double r = 0.0): c(c), r(r) {}
};

double dot(Vector a, Vector b) {//内积 
	return a.x * b.x + a.y * b.y;
}

double cross(Vector a, Vector b) {//外积 
	return a.x*b.y - a.y*b.x;
}

double getDistanceLP(Line l, Point p) {//直线l和点p的距离
	return abs(cross(l.p2 - l.p1, p - l.p1) / (l.p2 - l.p1).abs() );
}

double getDistanceSP(Segment s, Point p) {//线段s与点p的距离
	if( dot(s.p2 - s.p1, p - s.p1) < 0.0 ) return (p - s.p1).abs();
	if( dot(s.p1 - s.p2, p - s.p2) < 0.0 ) return (p - s.p2).abs();
	return getDistanceLP(s, p);
}

bool intersect(Circle c1, Circle c2) {//判断圆c1和圆c2是否相交
	if((c1.c - c2.c).abs() <= c1.r + c2.r) {
		return true;
	} else {
		return false;
	}
}
 
double arg(Vector p) { return atan2(p.y, p.x); }
Vector polar(double a, double r) { return Point(cos(r) * a, sin(r) * a); }

pair<Point, Point> getCrossPoints(Circle c1, Circle c2) {
	assert(intersect(c1, c2));
	double d = (c1.c - c2.c).abs();
	double a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
	double t = arg(c2.c - c1.c);
	return make_pair(c1.c + polar(c1.r, t + a), c1.c + polar(c1.r, t - a));
} 

int main(){
	Circle c1, c2;
	cin>>c1.c.x>>c1.c.y>>c1.r>>c2.c.x>>c2.c.y>>c2.r;
	
	pair<Point, Point> p;
	
	p = getCrossPoints(c1, c2);
	
	if(p.first.x < p.second.x || (p.first.x == p.second.x && p.first.y <= p.second.y) ) {
		printf("%.7f %.7f %.7f %.7f\n", p.first.x, p.first.y, p.second.x, p.second.y);
	} else {
		printf("%.7f %.7f %.7f %.7f\n", p.second.x, p.second.y, p.first.x, p.first.y);
	}
}

注:以上本文未涉及代码的详细解释参见:计算几何学

你可能感兴趣的:(计算几何学,算法学习,计算几何学,C/C++,算法,圆与圆的交点,Cross,Points,of,Circles)