计算任意两个圆的交点
这里是算法的数学思想:
http://mathworld.wolfram.com/Circle-CircleIntersection.html
其实懂了之后发现也很简单,中文概况是这样:
1 计算两个圆心之间的距离L
2 如果 L>r1+r2 无交点
3 如果 l = r1 + r2 则一个交点, 用圆心的坐标可以计算出具体点
4 假定 两个交点的连线和圆心连线的交点是D,D到第一个圆心的距离是d, 则
d*d + y*y = r1*r1
(l-d)*(l-d) + y*y = r2 * r2
其中y就是两交点连线距离的一半了
合并一下可得到:
r2*r2 - r1*r1 = l*l - 2dl
d = (l*l - r2*r2 + r1*r1) / 2l
5 根据前面的公式,还可以确定出来 y的数值
6 y = sqrt(r1*r1-d*d)
1 /// <summary> 2 /// 判断两个平行于x轴的圆的交点 3 /// </summary> 4 /// <param name="centerA">第一个圆的中点</param> 5 /// <param name="rA">半径</param> 6 /// <param name="centerB">第二个圆的中点</param> 7 /// <param name="rB">半径</param> 8 /// <param name="ptInter1">交点1(若不存在返回65536)</param> 9 /// <param name="ptInter2">交点1(若不存在返回65536)</param> 10 public static void CircleInterCircleOnXAxis(PointF centerA, double rA, PointF centerB, double rB, ref PointF ptInter1, ref PointF ptInter2) 11 { 12 ptInter1.X = ptInter2.X = 65536.0f; 13 ptInter1.Y = ptInter2.Y = 65536.0f; 14 PointF centerLeft; 15 double R, r, d; 16 if (centerA.X < centerB.X) 17 { 18 centerLeft = centerA; 19 R = rA; 20 r = rB; 21 d = centerB.X - centerA.X; 22 } 23 else 24 { 25 centerLeft = centerB; 26 R = rB; 27 r = rA; 28 d = centerA.X - centerB.X; 29 } 30 double R2 = R * R; 31 double x = (d*d - r*r + R2)/(2*d); 32 double y = Math.Sqrt(R2 - x * x); 33 ptInter1.X = centerLeft.X + (int)x; 34 ptInter1.Y = centerLeft.Y + (int)y; 35 ptInter2.X = centerLeft.X + (int)x; 36 ptInter2.Y = centerLeft.Y - (int)y; 37 }
当然上面说的方法需要两个圆平行于x轴,否则说不通。在数学计算上其实经常有这样的现象,就是变换坐标轴位置和方向,使计算简单。所以,这里思来想去需要另外一个工具函数来帮忙。
以某个点为中心点逆时针旋转Angle角度:(代码是网上找的)
1 /// <summary> 2 /// 以中心点逆时针旋转Angle角度 3 /// </summary> 4 /// <param name="center">中心点</param> 5 /// <param name="p1">待旋转的点</param> 6 /// <param name="angle">旋转角度(弧度)</param> 7 public static PointF PointRotate(PointF center, PointF p1, double angle) 8 { 9 double x1 = (p1.X - center.X) * Math.Cos(angle) + (p1.Y - center.Y) * Math.Sin(angle) + center.X; 10 double y1 = -(p1.X - center.X) * Math.Sin(angle) + (p1.Y - center.Y) * Math.Cos(angle) + center.Y; 11 return new PointF((float)x1, (float)y1); 12 }
思路就是,通过将两个圆的其中一个旋转到和另一个平行,计算出交点,再把交点反向旋转回来
所以这里就需要计算两个向量夹角的函数
1 /// <summary> 2 /// 计算两个向量的夹角 3 /// </summary> 4 /// <param name="Va"></param> 5 /// <param name="Vb"></param> 6 /// <returns></returns> 7 public static double GetAngleOfVectors(PointF Va, PointF Vb) 8 { 9 double da = Math.Sqrt(Va.X*Va.X + Va.Y*Va.Y); 10 double db = Math.Sqrt(Vb.X*Vb.X + Vb.Y*Vb.Y); 11 double theta = Math.Acos((Va.X*Vb.X + Va.Y*Vb.Y)/(da*db)); 12 return theta; 13 }
全部的代码我就不贴了,一方面是写的搓,一方面是希望各位自己也动动脑。哈
以上代码已经经过简单测试
好吧,还是贴出来好了:
/// <summary> /// 求任意两个圆的交点 /// </summary> /// <param name="centerA">第一个圆的中点</param> /// <param name="rA">半径</param> /// <param name="centerB">第二个圆的中点</param> /// <param name="rB">半径</param> /// <param name="ptInter1">交点1(若不存在返回65536)</param> /// <param name="ptInter2">交点1(若不存在返回65536)</param> public static void CircleInterCircle(PointF centerA, double rA, PointF centerB, double rB, ref PointF ptInter1, ref PointF ptInter2) { var v = new PointF(centerB.X - centerA.X, centerB.Y - centerA.Y); double angle = GetAngleWithXAxis(v); PointF bb = PointRotate(centerA, centerB, angle); PointF p1 = Point.Empty, p2 = Point.Empty; CircleInterCircleOnXAxis(centerA, rA, bb, rB, ref p1, ref p2); if (!Equal(p1.X, 65536.0f)) { p1 = PointRotate(centerA, p1, -angle); } if (!Equal(p2.X, 65536.0f)) { p2 = PointRotate(centerA, p2, -angle); } ptInter1 = p1; ptInter2 = p2; }