交点 - 两圆交点 - 圆方程组方式
圆心为(xc, yc), 半径为r的圆:
两圆方程组联立后,求方程组的解
1) 圆1方程 - 圆2方程:
2) 把x=a*y+b代入圆1方程:
3) 利用一元二次方程求根公式,求出x, y
a, b, A, B, C, D1, E1, F1这些都可根据圆心和半径求出,所以x, y也能求出来了。
特殊情况
对于圆心连线与y轴平型时,即:x1==x2,
会出现分母为0的情况,遇到这样的情况,可以用下面的方式:
1) 圆1方程-圆2方程后,求y
2) 把y=a*x+b代入圆1方程:
3) 利用一元二次方程求根公式,求出x, y
效果
//求两圆交点 - 圆方程方式 public static bool IsTwoCircleIntersect2(Vector2 c1, float r1, Vector2 c2, float r2, out Vector2 p1, out Vector2 p2) { p1 = Vector2.zero; p2 = Vector2.zero; float D1 = -2 * c1.x; float E1 = -2 * c1.y; float F1 = c1.x * c1.x + c1.y * c1.y - r1 * r1; float D2 = -2 * c2.x; float E2 = -2 * c2.y; float F2 = c2.x * c2.x + c2.y * c2.y - r2 * r2; if (Mathf.Abs(c1.x - c2.x) < Mathf.Abs(c1.y - c2.y)) //1)圆心连线与x轴平行的情况, 2)以x,y差值大的方向来计算精度更高 { float a = (D2 - D1) / (E1 - E2); float b = (F2 - F1) / (E1 - E2); float A = a * a + 1; float B = 2 * a * b + E1 * a + D1; float C = b * b + E1 * b + F1; float B4AC = B * B - 4 * A * C; if (B4AC < 0) return false; float tempSqrt = Mathf.Sqrt(B4AC); float temp2A = 2 * A; float x1 = (-B + tempSqrt) / temp2A; float y1 = a * x1 + b; p1 = new Vector2(x1, y1); if (B4AC > 0) //2个交点 { float x2 = (-B - tempSqrt) / temp2A; float y2 = a * x2 + b; p2 = new Vector2(x2, y2); } else //1个交点 { p2 = p1; } } else { float a = (E2 - E1) / (D1 - D2); float b = (F2 - F1) / (D1 - D2); float A = a * a + 1; float B = 2 * a * b + D1 * a + E1; float C = b * b + D1 * b + F1; float B4AC = B * B - 4 * A * C; if (B4AC < 0) return false; float tempSqrt = Mathf.Sqrt(B4AC); float temp2A = 2 * A; float y1 = (-B + tempSqrt) / temp2A; float x1 = a * y1 + b; p1 = new Vector2(x1, y1); if (B4AC > 0) //2个交点 { float y2 = (-B - tempSqrt) / temp2A; float x2 = a * y2 + b; p2 = new Vector2(x2, y2); } else //1个交点 { p2 = p1; } } return true; }
测试代码
using System; using UnityEditor; using UnityEngine; public class CircleTest : CollideTestBase { public Transform m_C1; //圆1中心点 [Range(0.1f, 99)] public float m_R1 = 0.5f; //圆1半径 public Transform m_C2; //圆2中心点 [Range(0.1f, 99)] public float m_R2 = 0.5f; //圆2半径 public Vector2 m_Point1; //交点1 public Vector2 m_Point2; //交点2 private Vector3 m_PointSize = new Vector3(0.02f, 0.02f, 0.01f); void Update() { m_IsIntersect = false; m_Point1 = Vector3.zero; m_Point2 = Vector3.zero; if (m_C1 && m_R1 > 0 && m_C2 && m_R2 > 0) { var t1 = DateTime.Now; switch (m_ApiType) { case 1: for (int i = 0; i < m_InvokeCount; ++i) m_IsIntersect = Shape2DHelper.IsCircleIntersect2(m_C1.position, m_R1, m_C2.position, m_R2, out m_Point1, out m_Point2); break; } CheckTimeCost(t1, 1); } } private void OnDrawGizmos() { if (m_C1 && m_R1 > 0 && m_C2 && m_R2 > 0) { if (m_IsIntersect) { Gizmos.color = Color.red; DrawCircle(m_C1.position, m_R1); DrawCircle(m_C2.position, m_R2); Gizmos.DrawLine(m_C1.position, m_C2.position); Gizmos.color = Color.green; DrawPoint(m_Point1, ref m_PointSize); DrawPoint(m_Point2, ref m_PointSize); Gizmos.DrawLine(m_Point1, m_Point2); Gizmos.DrawLine(m_Point1, m_C1.position); Gizmos.DrawLine(m_Point2, m_C2.position); Gizmos.color = Color.white; } else { DrawCircle(m_C1.position, m_R1); DrawCircle(m_C2.position, m_R2); Gizmos.DrawLine(m_C1.position, m_C2.position); } } } private static void DrawCircle(Vector2 c, float r) { int segmentCount = 30; float radDelta = Mathf.PI * 2 / segmentCount; float rad = 0; var lastPoint = new Vector2(r * Mathf.Cos(rad), r * Mathf.Sin(rad)); for (int i = 1; i <= segmentCount; ++i) { rad += radDelta; var curPoint = new Vector2(r * Mathf.Cos(rad), r * Mathf.Sin(rad)); Gizmos.DrawLine(c + lastPoint, c + curPoint); lastPoint = curPoint; } } private static void DrawPoint(Vector2 point, ref Vector3 pointSize) { float handleSize = HandleUtility.GetHandleSize(point) * 0.08f; pointSize.Set(handleSize, handleSize, 0.01f); Gizmos.DrawCube(point, pointSize); } }
圆的相交情况参考
1) 没有交点
2) 一个交点
3) 两个交点
参考
求两个圆交点的坐标-解析表达式推导及编程_两个圆的交点坐标公式-CSDN博客
怎样求解一元二次方程(四种)-百度经验 (baidu.com)
求两圆相交的交点的方法 - 简书 (jianshu.com)