好吧,所以我需要的是如果一条线穿过圆圈,它返回true。如果直线穿过圆圈,它也会返回true。一切都是假的。基本上是一个线段
以下是我尝试过的一些代码:
示例:
Vector2 pointA, pointB;
pointA = locationA;
pointB = locationB;
pointA.Normalize();
pointB.Normalize();
float dx = pointB.X - pointA.X;
float dy = pointB.Y - pointA.Y;
float dr = (float)Math.Sqrt((dx * dx) + (dy * dy));
float D = (pointA.X * pointB.Y) - (pointB.X * pointA.Y);
float delta = (circle.radius * circle.radius) * (dr * dr) - (D * D);
if (delta > 0) return true;
else return false;
或者
float dx = locationB.X - locationA.X;
float dy = locationB.Y - locationA.Y;
float a = (dx * dx) + (dy * dy);
float b = 2 * (dx * (locationA.X - circle.position.X) +
dy * (locationA.Y - circle.position.Y));
float c = (circle.position.X * circle.position.X) +
(circle.position.Y * circle.position.Y);
c += (locationA.X * locationA.X) +
(locationA.Y * locationA.Y);
c -= 2 * (circle.position.X * locationA.X + circle.position.Y * locationA.Y);
c -= circle.radius * circle.radius;
float delta = b * b - 4 * a * c;
if (delta < 0)
return false;
else
return true;
我试过这段代码以为它是针对线段的,但经过测试后,我意识到这是用于无限线检测。
有没有人知道有这个数学/伪/代码的网站?我所有的谷歌搜索都没有用。同样在Wolfram上,我只能找到无限的线圈碰撞。
非常感谢! Shyy,
编辑:
更新了尝试:
Vector2 d = locationB - locationA;
Vector2 p = locationA - circle.position;
Vector2 x = locationA + d;
Vector2 radius = x - circle.position;
Vector2 rSq = (d * d) + (p * d) + (d * p) + (p * p);
Vector2 t = (d * d) + 2 * (p * d) + (p * p) - (radius * radius);
Vector2 u = d * d;
Vector2 v = 2 * (p * d);
Vector2 w = (p * p) - (radius * radius); // circle.radius did not work does not work here, as radius was float. Converted to Vector2 above.
Vector2 r = (u * (t * t)) + v * t + w;
答案 0 :(得分:3)
假设您的线段位于点a
和b
之间。
将其视为parametric equation。第一,
let d = b - a
所以你的线段描述如下:
a + td for all real numbers between 0 and 1
包含它的无限行描述如下:
a + td for all real numbers
因为如果t
为负数或> 1
,您将继续在同一行,但不再在该段内。
同时,对于圆边上的任何点,以下情况都是正确的:
length(point - center) = radius
现在我们有了定义无限线和圆边的方程。如果它们之间有交叉点x
,则x
必须满足两个等式:
x = a + td for some real number t
length(x - center) = radius
将两者放在一起,我们正在寻找满足以下等式的t
值:
length(a + td - center) = radius
关于这个问题公式的好处是,如果我们找到一个交集,我们就会知道它的t
值,所以测试它是否在段内只需要更多的工作。
如果我们定义p = a - center
,那么我们的等式变得更简单:
length(td + p) = radius
扩展矢量长度的定义并将两边平方:
sqrt((td + p) · (td + p)) = radius
(td + p) · (td + p) = radius²
由点积的分布:
(td + p) · td + (td + p) · p = radius²
(td · td) + (p · td) + (td · p) + (p · p) = radius²
做一些代数来展示t
:
t² (d · d) + 2t (p · d) + (p · p) - radius² = 0
让我们通过为所有非t
术语定义变量来简化它,这些术语都是标量,而不是向量:
let u = d · d
v = 2 (p · d)
w = (p · p) - radius²
所以现在我们的等式是:
ut² + vt + w = 0
这是二次方程。我们可以使用quadratic formula来解决它。
因为我们的二次方程式是t
,所以它的根将是t
的值,我们可以将其插回到参数线方程中。
如果二次方程没有实根,则无限直线根本不与圆边相交,因此该段肯定不会。
如果它有一个或两个实根,则该线与圆边相交,但该段可能不相交。对于每个根r
,我们需要检查是否
0 <= r <= 1
如果是这样,那么通过我们的参数化定义,该段与
处的圆相交 a + rd
自然地,如果方程有2个根,这对两个都是正确的,那么圆和线段在2个位置相交。
在代码中实现此数学运算,从最后向后工作以确定需要计算的值。你的目标是解决二次方程ut² + vt + w = 0
。这将为您提供0,1或2个根。然后,如果至少有一个根位于true
范围内,则会返回0 <= t <= 1
。
因此,从u
,v
和w
的定义开始向后工作,直到您获得a
,b
,{{1}的所有内容}和center
- 函数的输入。
由于OP无法将上面的数学转换为代码,因此这里有一些代码。
radius
正如@Cimbali指出的那样,这种方法不会检测到该段何时完全位于圆内。如果您需要识别该案例,您只需检查是否:
Vector2 d = locationB - locationA;
Vector2 p = locationA - circle.position;
double u = d*d;
double v = 2*p*d;
double w = p*p - circle.radius*circle.radius;
// apply quadratic formula
// NOTE: THIS CODE IS PROBABLY NOT NUMERICALLY STABLE.
// LOOK IN A NUMERICAL ANALYSIS BOOK
// IF YOU NEED TO SOLVE THIS PROBLEM FOR REAL.
double discriminant = v*v - 4*u*w;
if (discriminant < 0) {
// no real roots, infinite line does not intersect circle
}
else if (discriminant == 0) {
// 1 real root, infinite line is tangent to circle.
// if tangent lines don't count, then return false here. otherwise...
double t = -v / (2*u);
return (t >= 0 && t <= 1);
}
else {
// 2 real roots, infinite line intersects circle
double t0 = (-v + Math.Sqrt(discriminant)) / (2*u);
double t1 = (-v - Math.Sqrt(discriminant)) / (2*u);
return (t0 >= 0 && t0 <= 1) || (t1 >= 0 && t1 <= 1);
}
但也可能有更少的计算工作量。
答案 1 :(得分:1)
作为对上一个答案(japreiss')的补充,您可以看到该段是否包含在圆圈中,没有更多的数学和一些额外的逻辑。
您可以使用相同的推理和符号来ut² + vt + w <= 0
,您想要验证[0,1]中的所有t。
如果它们存在,您解决了它的根r1
和r2
。你知道,在两端,你的多项式发散到无穷大(因为u > 0
)所以它在根之间是负的。这也意味着多项式总是非负的,少于两个根。因此,如果r1 <= 0 && r2 >= 1
完全包含它。
因此,初始表达u,v和w保持不变,并解决二阶多项式的根。然后,最终的逻辑是:
r
,则返回true
iff r >= 0 || r <= 1
r1
和r2
(通过构造r1 < r2
)返回false
iff r1 > 1 || r2 < 0
。实际上,这两个测试错误(至少)根在[0,1]中,因此该段与圆相交,或者如果没有,那么[r1,r2]必然与[0,1]重叠,意思是该段包含在圆圈中。