获取2个圆的交点
Getting the intersection points of 2 circles
我需要能够计算两个圆之间的交点。我确信总会有 2 个交点。不是 1,不是 0,不是无限,总是 2。这是我正在尝试做的事情的图表:
这是我目前的尝试:
public static List<Vector2> intersect(Vector3 c1, Vector3 c2, float rad1, float rad2)
{
List<Vector2> rLp = new List<Vector2>();
float d = Vector2.Distance(c1, c2);
if (d > (rad1 + rad2))
return rLp;
else if (d == 0 && rad1 == rad2)
return rLp;
else if ((d + Mathf.Min(rad1, rad2)) < Mathf.Max(rad1, rad2))
return rLp;
else
{
float a = (rad1 * rad1 - rad2 * rad2 + d * d) / (2 * d);
float h = Mathf.Sqrt(rad1 * rad1 - a * a);
Vector2 p2 = new Vector2((float)(c1.x + (a * (c2.x - c1.x)) / d), (float)(c1.y + (a * c2.y - c1.y) / d));
Vector2 i1 = new Vector2((float)(p2.x + (h * (c2.y - c1.y)) / d), (float)(p2.y - (h * (c2.x - c1.x)) / d));
Vector2 i2 = new Vector2((float)(p2.x - (h * (c2.y - c1.y)) / d), (float)(p2.y + (h * (c2.x - c1.x)) / d));
if (d == (rad1 + rad2))
rLp.Add(i1);
else
{
rLp.Add(i1);
rLp.Add(i2);
}
return rLp;
}
}
它给了我以下结果:
如你所见,代表两个圆圈之间截取点的白色方块放错了地方。在这方面我真的需要一些帮助。谁能看出哪里出了问题?
数学代码的复制和粘贴对我来说几乎总是出错(例如定义中的符号翻转等)。从其他人那里调试这样的代码简直是恐怖(当深入数学时,调试我自己的代码就够难的了)。您应该尝试自己进行计算并将其转化为代码。如果出现错误,您可以使用调试器逐步调试它并使用袖珍计算器进行交叉检查。
你真的应该自己试试。拿一张 sheet 的纸,做对数学。但这是我会使用的方法:
private void intersectionTwoCircles(double c1x, double c1y, double r1, double c2x, double c2y, double r2,
out double a1x, out double a1y, out double a2x, out double a2y)
{
/* error handling is missing complettely - left as an exercise
A1
/| \
r1 / | \ r2
/ | \
/ |h \
/g1 | \ (g1 means angle gamma1)
C1----P-----C2
d1 d2
*/
double dx = c1x - c2x;
double dy = c1y - c2y;
double d = Math.Sqrt(dx*dx + dy*dy); // d = |C1-C2|
double gamma1 = Math.Acos((r2*r2 + d*d - r1*r1)/(2*r2*d)); // law of cosines
double d1 = r1*Math.Cos(gamma1); // basic math in right triangle
double h = r1*Math.Sin(gamma1);
double px = c1x + (c2x - c1x) / d*d1;
double py = c1y + (c2y - c1y) / d*d1;
// (-dy, dx)/d is (C2-C1) normalized and rotated by 90 degrees
a1x = px + (-dy)/d*h;
a1y = py + (+dx) / d * h;
a2x = px - (-dy) / d * h;
a2y = py - (+dx) / d * h;
}
提供代码后更新:
您的方法与我的非常相似(a=r1 cos(gamma_1)
和 h=r1 sin(gamma_1)
)。你只是避免直接计算 gamma
。它更快(只有一个 sqrt
而不是 cos
、sin
和 acos
)。但我认为,我的可读性更好一些;)考虑重载向量 class (+, -, *, ...) 的操作数……您的代码将变得更易于阅读。
我想我发现了你的错误....
// before
Vector2 p2 = new Vector2((float)(c1.x + (a * (c2.x - c1.x)) / d), (float)(c1.y + (a * c2.y - c1.y) / d));
// after
Vector2 p2 = new Vector2((float)(c1.x + (a * (c2.x - c1.x)) / d), (float)(c1.y + a * (c2.y - c1.y) / d));
对于任何寻找使用 PointF 而不是离散坐标的实现的人,我改编了 Alex K 五年前提到的 link 中的解决方案以生成以下内容。尽管它没有像 OP 所描述的那样针对已知的 2 点情况进行优化,但它可以工作并适应与一个、两个或没有点相交的圆。
using System.Drawing;
using static System.Math;
public static class CircleIntersections {
/// <summary>
/// Gets the intersections of two circles
/// </summary>
/// <param name="center1">The first circle's center</param>
/// <param name="center2">The second circle's center</param>
/// <param name="radius1">The first circle's radius</param>
/// <param name="radius2">The second circle's radius. If omitted, assumed to equal the first circle's radius</param>
/// <returns>An array of intersection points. May have zero, one, or two values</returns>
/// <remarks>Adapted from http://csharphelper.com/blog/2014/09/determine-where-two-circles-intersect-in-c/</remarks>
public static PointF[] GetCircleIntersections(PointF center1, PointF center2, double radius1, double? radius2 = null) {
var (r1, r2) = (radius1, radius2 ?? radius1);
(double x1, double y1, double x2, double y2) = (center1.X, center1.Y, center2.X, center2.Y);
// d = distance from center1 to center2
double d = Sqrt(Pow(x1 - x2, 2) + Pow(y1 - y2, 2));
// Return an empty array if there are no intersections
if (!(Abs(r1 - r2) <= d && d <= r1 + r2)) { return new PointF[0]; }
// Intersections i1 and possibly i2 exist
var dsq = d * d;
var (r1sq, r2sq) = (r1 * r1, r2 * r2);
var r1sq_r2sq = r1sq - r2sq;
var a = r1sq_r2sq / (2 * dsq);
var c = Sqrt(2 * (r1sq + r2sq) / dsq - (r1sq_r2sq * r1sq_r2sq) / (dsq * dsq) - 1);
var fx = (x1 + x2) / 2 + a * (x2 - x1);
var gx = c * (y2 - y1) / 2;
var fy = (y1 + y2) / 2 + a * (y2 - y1);
var gy = c * (x1 - x2) / 2;
var i1 = new PointF((float)(fx + gx), (float)(fy + gy));
var i2 = new PointF((float)(fx - gx), (float)(fy - gy));
return i1 == i2 ? new PointF[] { i1 } : new PointF[] { i1, i2 };
}
}
我需要能够计算两个圆之间的交点。我确信总会有 2 个交点。不是 1,不是 0,不是无限,总是 2。这是我正在尝试做的事情的图表:
这是我目前的尝试:
public static List<Vector2> intersect(Vector3 c1, Vector3 c2, float rad1, float rad2)
{
List<Vector2> rLp = new List<Vector2>();
float d = Vector2.Distance(c1, c2);
if (d > (rad1 + rad2))
return rLp;
else if (d == 0 && rad1 == rad2)
return rLp;
else if ((d + Mathf.Min(rad1, rad2)) < Mathf.Max(rad1, rad2))
return rLp;
else
{
float a = (rad1 * rad1 - rad2 * rad2 + d * d) / (2 * d);
float h = Mathf.Sqrt(rad1 * rad1 - a * a);
Vector2 p2 = new Vector2((float)(c1.x + (a * (c2.x - c1.x)) / d), (float)(c1.y + (a * c2.y - c1.y) / d));
Vector2 i1 = new Vector2((float)(p2.x + (h * (c2.y - c1.y)) / d), (float)(p2.y - (h * (c2.x - c1.x)) / d));
Vector2 i2 = new Vector2((float)(p2.x - (h * (c2.y - c1.y)) / d), (float)(p2.y + (h * (c2.x - c1.x)) / d));
if (d == (rad1 + rad2))
rLp.Add(i1);
else
{
rLp.Add(i1);
rLp.Add(i2);
}
return rLp;
}
}
它给了我以下结果:
如你所见,代表两个圆圈之间截取点的白色方块放错了地方。在这方面我真的需要一些帮助。谁能看出哪里出了问题?
数学代码的复制和粘贴对我来说几乎总是出错(例如定义中的符号翻转等)。从其他人那里调试这样的代码简直是恐怖(当深入数学时,调试我自己的代码就够难的了)。您应该尝试自己进行计算并将其转化为代码。如果出现错误,您可以使用调试器逐步调试它并使用袖珍计算器进行交叉检查。
你真的应该自己试试。拿一张 sheet 的纸,做对数学。但这是我会使用的方法:
private void intersectionTwoCircles(double c1x, double c1y, double r1, double c2x, double c2y, double r2,
out double a1x, out double a1y, out double a2x, out double a2y)
{
/* error handling is missing complettely - left as an exercise
A1
/| \
r1 / | \ r2
/ | \
/ |h \
/g1 | \ (g1 means angle gamma1)
C1----P-----C2
d1 d2
*/
double dx = c1x - c2x;
double dy = c1y - c2y;
double d = Math.Sqrt(dx*dx + dy*dy); // d = |C1-C2|
double gamma1 = Math.Acos((r2*r2 + d*d - r1*r1)/(2*r2*d)); // law of cosines
double d1 = r1*Math.Cos(gamma1); // basic math in right triangle
double h = r1*Math.Sin(gamma1);
double px = c1x + (c2x - c1x) / d*d1;
double py = c1y + (c2y - c1y) / d*d1;
// (-dy, dx)/d is (C2-C1) normalized and rotated by 90 degrees
a1x = px + (-dy)/d*h;
a1y = py + (+dx) / d * h;
a2x = px - (-dy) / d * h;
a2y = py - (+dx) / d * h;
}
提供代码后更新:
您的方法与我的非常相似(a=r1 cos(gamma_1)
和 h=r1 sin(gamma_1)
)。你只是避免直接计算 gamma
。它更快(只有一个 sqrt
而不是 cos
、sin
和 acos
)。但我认为,我的可读性更好一些;)考虑重载向量 class (+, -, *, ...) 的操作数……您的代码将变得更易于阅读。
我想我发现了你的错误....
// before
Vector2 p2 = new Vector2((float)(c1.x + (a * (c2.x - c1.x)) / d), (float)(c1.y + (a * c2.y - c1.y) / d));
// after
Vector2 p2 = new Vector2((float)(c1.x + (a * (c2.x - c1.x)) / d), (float)(c1.y + a * (c2.y - c1.y) / d));
对于任何寻找使用 PointF 而不是离散坐标的实现的人,我改编了 Alex K 五年前提到的 link 中的解决方案以生成以下内容。尽管它没有像 OP 所描述的那样针对已知的 2 点情况进行优化,但它可以工作并适应与一个、两个或没有点相交的圆。
using System.Drawing;
using static System.Math;
public static class CircleIntersections {
/// <summary>
/// Gets the intersections of two circles
/// </summary>
/// <param name="center1">The first circle's center</param>
/// <param name="center2">The second circle's center</param>
/// <param name="radius1">The first circle's radius</param>
/// <param name="radius2">The second circle's radius. If omitted, assumed to equal the first circle's radius</param>
/// <returns>An array of intersection points. May have zero, one, or two values</returns>
/// <remarks>Adapted from http://csharphelper.com/blog/2014/09/determine-where-two-circles-intersect-in-c/</remarks>
public static PointF[] GetCircleIntersections(PointF center1, PointF center2, double radius1, double? radius2 = null) {
var (r1, r2) = (radius1, radius2 ?? radius1);
(double x1, double y1, double x2, double y2) = (center1.X, center1.Y, center2.X, center2.Y);
// d = distance from center1 to center2
double d = Sqrt(Pow(x1 - x2, 2) + Pow(y1 - y2, 2));
// Return an empty array if there are no intersections
if (!(Abs(r1 - r2) <= d && d <= r1 + r2)) { return new PointF[0]; }
// Intersections i1 and possibly i2 exist
var dsq = d * d;
var (r1sq, r2sq) = (r1 * r1, r2 * r2);
var r1sq_r2sq = r1sq - r2sq;
var a = r1sq_r2sq / (2 * dsq);
var c = Sqrt(2 * (r1sq + r2sq) / dsq - (r1sq_r2sq * r1sq_r2sq) / (dsq * dsq) - 1);
var fx = (x1 + x2) / 2 + a * (x2 - x1);
var gx = c * (y2 - y1) / 2;
var fy = (y1 + y2) / 2 + a * (y2 - y1);
var gy = c * (x1 - x2) / 2;
var i1 = new PointF((float)(fx + gx), (float)(fy + gy));
var i2 = new PointF((float)(fx - gx), (float)(fy - gy));
return i1 == i2 ? new PointF[] { i1 } : new PointF[] { i1, i2 };
}
}