如何检测椭圆是否与圆相交(碰撞)

时间:2010-05-31 18:32:27

标签: algorithm math geometry

我想改进碰撞系统。

现在我检测到2个不规则物体是否会碰撞它们的边界矩形。

我希望获得相应椭圆的for矩形,而另一个使用圆形。我找到了一种获取椭圆坐标的方法,但是当我试图检测它是否与圆相交时我遇到了问题。

你知道一个算法来测试圆是否与椭圆相交吗?

10 个答案:

答案 0 :(得分:20)

简短回答:准确地确定两个物体是否相交是复杂到足以使其不可能用于碰撞检测。对于某些n,将椭圆分离为n边多边形(取决于您需要的准确度),并使用该多边形进行碰撞检测。

长答案:如果你坚持确定光滑的椭圆和圆是否相交,有两种主要方法。两者都涉及首先求解椭圆上圆心的最近点,然后将该距离与圆的半径进行比较。

方法1 :使用椭圆的参数化。变换坐标,使椭圆位于原点,其轴与x-y轴对齐。那就是:

  • 椭圆中心:(0,0)
  • 圆心:c =(cx,cy)
  • 圆的半径:r
  • x对齐椭圆轴的半径:a
  • y对齐的椭圆轴的半径:b。

然后由a cos(t), b sin(t)给出椭圆的方程。为了找到最近的点,我们希望最小化平方距离 || (a cos t, b sin t) - c ||^2。正如Jean指出的那样,这只是“微积分”:取一个导数,并将其设置为0.除非我遗漏了某些东西,否则解析t得到的(非常讨厌的)等式是不可能的,并且必须使用例如近似值牛顿法。 将您找到的t插入参数方程以获得最近的点。

  • Pro:数值求解仅在一个变量中t
  • Con:您必须能够记下椭圆的参数化,或者转换您的坐标以便可以。对于椭圆的任何合理表示,这不应该太难。但是,我将向您展示第二种方法,这种方法更为通用,如果您必须将问题概括为3D,可能会有用。

方法2 :使用多维微积分。不需要改变坐标。

  • 圆心:c =(cx,cy)
  • cirlce半径:r
  • 对于函数g,椭圆由g(x,y)= 0给出。例如,根据Curd的答案,您可以使用g(x,y)=距焦点1的距离(x,y)+距焦点2的距离(e,y) - e。

然后,找到最接近圆心的椭圆上的点可以表示为约束最小化问题

Minimize ||(x,y) - c||^2 subject to g(x,y) = 0

(最小化平方距离相当于最小化距离,处理起来更加愉快,因为它是x,y中的二次多项式。)

为了解决约束最小化问题,我们引入拉格朗日乘数lambda,并求解方程组

2 * [ (x,y) -c ] + lambda * Jg(x,y) = 0
g(x,y) = 0

这里Jg是g的梯度。这是三个未知数的三个(非线性)方程组:x,y和lambda。我们可以使用牛顿方法求解这个系统,我们得到的(x,y)是最接近圆心的点。

  • Pro:不需要找到参数化
  • Pro:方法非常通用,只要写入g比查找参数方程(如3D)更容易,效果很好
  • Con:需要多变量Newton求解,如果您无法访问数值方法包,则非常多毛。

警告:这两种方法在技术上解决了极化到圆心的距离的点。因此,找到的点可能是圆的最远点,而不是最接近的点。对于这两种方法,通过良好的初始猜测来播种你的解决方案(方法2的圆心适用于方法2;你可以自己使用方法1)将减少这种危险。

潜在的第三种方法?:有可能在表示圆和椭圆的两个变量中直接求解两个二次方程组的根。如果存在真正的根,则对象相交。解决这个系统的最直接的方法,再次使用像牛顿方法这样的数值算法,将无济于事,因为缺乏收敛并不一定意味着不存在真正的根。然而,对于两个变量中的两个二次方程,可能存在一种专门的方法,如果它们存在,它们可以保证找到真正的根。我自己也想不出这样做的方法,但你可能想自己研究它(或者看看stackoverflow上的某个人是否可以详细说明。)

答案 1 :(得分:16)

椭圆定义为一组点 到点A的距离和到点B的距离之和是常数e。 (A和B称为椭圆的焦点)。

所有点P,其总和AP + BP小于e,位于椭圆内。

圆圈定义为一组点 到C点的距离是r。

圆与椭圆相交的简单测试如下:

查找
P作为圆与线AC和
的交点 Q作为圆与BC线的交点。

如果是,圆和椭圆相交(或圆完全位于椭圆内) AP + BP< = e或AQ + BQ< = e

alt text

修改

在Martin DeMello发表评论并相应地调整我的答案后,我更多地考虑了问题并发现答案(第二次检查)仍未检测到所有交叉点:

如果圆和椭圆仅相交很少(只是略微相切),P和Q不会位于椭圆内:

alt text

因此,只有当重叠“足够大”时,上述测试才会检测到碰撞。 也许这对你的实际目的来说已经足够了,虽然在数学上它并不完美。

答案 2 :(得分:3)

找到最接近圆心的椭圆上的点
然后检查距此点的距离是否小于圆的半径
如果你需要帮助这样做只是评论,但它只是微积分

编辑:这是解决问题的方法,因为凝固有问题

在椭圆上给出中心αβ 和(因为没有记住这个术语)x radius a,y radius b 参数化是
r(Θ)=(ab)/(((BcosΘ)^ 2 +(asinΘ)^ 2)^。5)
x(Θ)=α+ sin(Θ)r(Θ)
y(Θ)=β+ cos(Θ)r(Θ)

然后只取中心的圆(φ,ψ)和半径r 那么距离d(Θ)=((φ-x(Θ))^ 2 +(ψ-y(Θ))^ 2)^。5

该距离的最小值是当d'(Θ)= 0时('导数')

d'(Θ)= 1 / d(Θ)*( - φx'(Θ)+ x(Θ)x'(Θ) - ψy'(Θ)+ y(Θ)y'(Θ))
==>
x'(Θ)*( - φ+ x(Θ))= y'(Θ)*(ψ - y(Θ))

继续往前走,希望你能解决Θ
您正在使用的框架可能有一些东西可以帮助您解决这个问题,您可以随时通过Newton's Method

轻松解决根源问题。

答案 3 :(得分:3)

如果圆和椭圆碰撞,则它们的边界相交1,2,3或4次(或者在圆形椭圆与圆重合的情况下无限多),或者圆在椭圆内反之亦然。

我假设圆具有等式(x-a)^ 2 +(y-b)^ 2 <= r ^ 2(1)并且椭圆具有等式[(x-c) ^ 2] / [d ^ 2] + [(y - e)^ 2] / [f ^ 2]&lt; = 1(2)

要检查其中一个是否在另一个内部,您可以在椭圆中心坐标处评估圆的方程(x = c,y = e),反之亦然,并查看不等式成立。

要检查其边界相交的其他情况,您必须检查(1)和(2)所描述的方程组是否有任何解。

你可以通过添加(1)和(2),给你

来做到这一点

(x - a)^2 + (y - b)^2 + [(x - c)^2]/[d^2] + [(y - e)^2]/[f^2] = r^2 + 1

接下来,你将这些术语相乘,给出

x^2 - 2ax + a^2 + y^2 - 2by + b^2 + x^2/d^2 - 2cx/d^2 + c^2/d^2 + y^2/f^2 - 2ey/f^2 + e^2/f^2 = r^2 + 1

收集类似条款,我们得到

(1 + 1/d^2)x^2 - (2a + 2c/d^2)x + (1 + 1/f^2)y^2 - (2b + 2e/f^2)y = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2

现在让m = (1 + 1/d^2), n = -(2a + 2c/d^2), o = (1 + 1/f^2), and p = -(2b + 2e/f^2)

等式现在是mx^2 + nx + oy^2 + py = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2

现在我们需要完成左侧的方块

m[x^2 + (n/m)x] + o[y^2 + (p/o)y] = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2

m[x^2 + (n/m)x + (n/2m)^2 - (n/2m)^2] + o[y^2 + (p/o)y + (p/2o)^2 - (p/2o)^2] = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2

m[(x + n/2m)^2 - (n/2m)^2] + o[(y + p/2o)^2 - (p/2o)^2] = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2

m(x + n/2m)^2 - m(n/2m)^2 + o(y + p/2o)^2 - o(p/2o)^2 = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2

m(x + n/2m)^2 + o(y + p/2o)^2 = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2 + m(n/2m)^2 + o(p/2o)^2

此系统有一个解决方案iff 11 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2 + m(n/2m)^2 + o(p/2o)^2 >= 0

如果我没有犯任何代数错误,那么你有它。我不知道你能用多少简化结果表达式,所以如果要检查多个圆/椭圆,这个解决方案的计算成本可能很高

答案 4 :(得分:3)

忘记数学解决方案。您可以通过绘图轻松查看,最多可以有四个解,因此可能是四年级多项式。

而只是沿着其中一个数字的边缘进行二元搜索。很容易确定一个点是否位于一个椭圆内,在一个圆中更是如此(只要看看距离是否比半径短)。

如果你真的想要数学,Wolfram MathWorld在这里有一篇很好的文章:http://mathworld.wolfram.com/Circle-EllipseIntersection.html但是要注意,你仍然需要写一个多项式方程求解器,可能使用像二分法搜索这样的东西。 / p>

答案 5 :(得分:3)

用圆的半径放大椭圆的主要和次要半径。然后通过将距离放大到椭圆的焦点的距离求和来测试给定圆的中心是否在这个新的较大椭圆内。

此算法非常有效。如果给定的圆不与围绕椭圆的圆相交,则可以提前退出。这比边界框测试慢,但找到非轴对齐椭圆的边界框很棘手。

答案 6 :(得分:3)

我知道现在为时已晚,但我希望它会对某人有所帮助。我解决这个问题的方法是将椭圆插入到n维多边形中,然后在每2个点之间构造一条直线,并找出圆是否与任何直线相交。这不能提供最佳性能,但它易于实施。

要将椭圆插值为n维多边形,可以使用:

    float delta = (2 * PI) / n;

    std::vector<Point*> interpolation;

    for(float t = 0; t < (2 * PI); t += delta) {

        float x = rx * cos(t) + c->get_x();
        float y = ry * sin(t) + c->get_y();

        interpolation.push_back(new Point(x, y));
    }

c:椭圆的中心。 rx:椭圆的x对齐轴的半径。 ry:椭圆的y对齐轴的半径。

现在我们有了插值点,我们可以找到圆与每两点之间的线之间的交点。 here描述了找到线 - cricle交叉点的一种方法, 如果任何线与圆之间发生交叉,则会发生交叉。

希望这有助于任何人。

答案 7 :(得分:1)

假设:     椭圆以原点为中心,以半主要为中心 轴(长度为a)沿x轴取向,半长度 长度轴b; E2是偏心率的平方,即(a a-b b)/(a * a); 圆圈以X,Y为中心,半径为r。

简单的案例是:     圆心位于椭圆内(即,hypot(X / a,Y / b)<= 1) 所以有一个交叉点;     圆心位于以半径a + r为中心的圆之外 (即,hypot(X,Y)> a + r)因此没有交叉点。

其他情况的一种方法是计算大地测量 圆心的坐标(纬度,高度)。圈子 当且仅当高度小于半径时,才与椭圆相交。

椭圆上一个点的大地纬度是角度 该点处椭圆的法线与x轴和 椭圆外点的高度是距离的高度 从距离它最近的椭圆上的点开始。注意,大地纬度与从椭圆中心到该点的极角不同,除非 椭圆实际上是圆形的。

在公式中,从大地坐标lat,ht转换为 笛卡尔坐标X,Y是 X =(nu + ht)* cos(lat),Y =(nu *(1-E2)+ ht)* sin(lat) 其中nu = a / sqrt(1 - E2 * sin(lat) sin(lat))。 最靠近X,Y的椭圆上的点是点 具有相同的纬度,但零高度,即x = nu cos(lat), y = nu *(1-E2)* sin(纬度)。 请注意,nu是纬度的函数。

不幸的是,从X,Y找到lat,ht的过程是一个 迭代的。一种方法是首先找到纬度,然后 高度。

一个小代数表明纬度满足 lat = atan2(Y + E2 * nu sin(lat),X) 可用于计算纬度的连续近似值, 从lat = atan2(Y,X (1.0-E2))开始,或者(更有效地)可以 用牛顿法解决了。

E2越大,即椭圆越平坦越多 迭代将是必需的。例如,如果椭圆接近 循环(比如E2 <0.1)然后五次迭代将得到x,y 在* 1e-12范围内,但如果椭圆非常平坦,例如E2 = 0.999 你需要大约300次迭代才能获得相同的精确度!

最后,给定纬度,可以计算高度 通过计算(x,y): x = nu cos(lat),y = nu (1-E2)* sin(lat) 然后h是从x,y到圆心的距离, h = hypot(X-x,Y-y)

答案 8 :(得分:1)

这并不难。 user168715的答案通常是正确的,但是没有必要进行微积分。只是三角学。

找到两个物体中心之间的角度。使用此方法,您可以使用极坐标形式找到椭圆上圆心的最近点:

Ellipse Equation : Polar form relative to center

(摘自维基百科关于Ellipses的文章)

现在比较两个物体中心之间的距离,减去椭圆半径和圆半径。

也许我错过了一些东西;也许ArcTan / Cos / Sin很慢 - 但我不这么认为,如果需要,应该有快速逼近。

答案 9 :(得分:1)

我想对涉及两个省略号之间联系的更普遍的问题提供一些输入。计算两个椭圆的最接近距离是一个长期存在的问题,并且在过去的十年中只能通过分析解决 - 这绝不是简单的。可以在此处找到问题的解决方案http://www.e-lc.org/docs/2007_01_17_00_46_52/

确定两个椭圆之间是否存在接触的一般方法是首先计算椭圆在其当前配置中最接近的距离,然后从它们当前的分离大小中减去该距离。如果此结果小于或等于0,则表示它们处于联系状态。

如果有人感兴趣,我可以发布计算最接近距离的代码 - 它是用C ++编写的。该代码适用于两个任意椭圆的一般情况,但显然可以用圆和椭圆来表示,因为圆是一个具有相等的小轴和长轴的椭圆。