我试图检测一条线在javascript中与圆相交的时间。我发现一个功能几乎完美,但我最近注意到,当相交线完全水平或垂直时,它不起作用。由于我不太了解此功能的实际工作方式,因此我不确定如何编辑它以获得我喜欢的结果。
function lineCircleCollision(circleX,circleY,radius,lineX1,lineY1,lineX2,lineY2) {
var d1 = pDist(lineX1,lineY1,circleX,circleY);
var d2 = pDist(lineX2,lineY2,circleX,circleY);
if (d1<=radius || d2<=radius) {
return true;
}
var k1 = ((lineY2-lineY1)/(lineX2-lineX1));
var k2 = lineY1;
var k3 = -1/k1;
var k4 = circleY;
var xx = (k1*lineX1-k2-k3*circleX+k4)/(k1-k3);
var yy = k1*(xx-lineX1)+lineY1;
var allow = true;
if (lineX2>lineX1) {
if (xx>=lineX1 && xx<=lineX2) {}
else {allow = false;}
} else {
if (xx>=lineX2 && xx<=lineX1) {}
else {allow = false;}
}
if (lineY2>lineY1) {
if (yy>=lineY1 && yy<=lineY2) {}
else {allow = false;}
} else {
if (yy>=lineY2 && yy<=lineY1) {}
else {allow = false;}
}
if (allow) {
if (pDist(circleX,circleY,xx,yy)<radius) {
return true;
}
else {
return false;
}
} else {
return false;
}
}
function pDist(x1,y1,x2,y2) {
var xd = x2-x1;
var yd = y2-y1;
return Math.sqrt(xd*xd+yd*yd);
}
答案 0 :(得分:2)
您可以将该行表示为两种关系:
x = x1 + k * (x2 - x1) = x1 + k * dx
y = y1 + k * (y2 - y1) = y1 + k * dy
0 < k < 1
。圆上的一个点满足等式:
(x - Cx)² + (y - Cy)² = r²
用线方程替换x
和y
,您将获得二次方程式:
a*k² + b*k + c = 0
a = dx² + dy²
b = 2*dx*(x1 - Cx) + s*dy*(y1 - Cy)
c = (x1 - Cx)² + (y1 - Cy)² - r²
解决这个问题,如果k
的两个可能解决方案中的任何一个位于0和1之间的范围内,那么你就会受到影响。此方法检查实际交叉点并错过线条完全包含在圆圈中的情况,因此需要额外检查线条的终点是否位于圆圈内。
以下是代码:
function collision_circle_line(Cx, Cy, r, x1, y1, x2, y2) {
var dx = x2 - x1;
var dy = y2 - y1;
var sx = x1 - Cx;
var sy = y1 - Cy;
var tx = x2 - Cx;
var ty = y2 - Cy;
if (tx*tx + ty*ty < r*r) return true;
var c = sx*sx + sy*sy - r*r;
if (c < 0) return true;
var b = 2 * (dx * sx + dy * sy);
var a = dx*dx + dy*dy;
if (Math.abs(a) < 1.0e-12) return false;
var discr = b*b - 4*a*c;
if (discr < 0) return false;
discr = Math.sqrt(discr);
var k1 = (-b - discr) / (2 * a);
if (k1 >= 0 && k1 <= 1) return true;
var k2 = (-b + discr) / (2 * a);
if (k2 >= 0 && k2 <= 1) return true;
return false;
}
答案 1 :(得分:1)
查看交叉点检查的另一种方法是我们在最接近圆心的线段上找到点,然后确定它是否足够接近。由于到圆心的距离是凸函数,因此有三种可能性:段的两个端点和线上的最近点,假设它在段上。
为了找到线上最近的点,我们有一个超定线性系统
(1 - t) lineX1 + t lineX2 = circleX
(1 - t) lineY1 + t lineY2 = circleY,
表示为矩阵:
[lineX2 - lineX1] [t] = [circleX - lineX1]
[lineY2 - lineY1] [circleY - lineY1].
通过求解正规方程可以找到最近的点
[(lineX2 - lineX1) (lineY2 - lineY1)] [lineX2 - lineX1] [t] =
[lineY2 - lineY1]
[(lineX2 - lineX1) (lineY2 - lineY1)] [circleX - lineX1]
[circleY - lineY1],
表示为
((lineX2 - lineX1)^2 + (lineY2 - lineY1)^2) t =
(lineX2 - lineX1) (circleX - lineX1) + (lineY2 - lineY1) (circleY - lineY1),
并解决了t
:
(lineX2 - lineX1) (circleX - lineX1) + (lineY2 - lineY1) (circleY - lineY1)
t = ---------------------------------------------------------------------------.
((lineX2 - lineX1)^2 + (lineY2 - lineY1)^2)
假设t
介于0
和1
之间,我们可以将其插入并检查距离。当t
超出范围时,我们可以将其钳制并仅检查该端点。
未经测试的代码:
function lineCircleCollision(circleX, circleY, radius, lineX1, lineY1, lineX2, lineY2) {
circleX -= lineX1;
circleY -= lineY1;
lineX2 -= lineX1;
lineY2 -= lineY1;
var t = (lineX2 * circleX + lineY2 * circleY) / (lineX2 * lineX2 + lineY2 * lineY2);
if (t < 0) t = 0;
else if (t > 1) t = 1;
var deltaX = lineX2 * t - circleX;
var deltaY = lineY2 * t - circleY;
return deltaX * deltaX + deltaY * deltaY <= radius * radius;
}
答案 2 :(得分:0)
如果您不需要该点,只想知道线是否相交:
计算距圆心P0(x0,y0)和线端点P1(x1,y1),P2(x2,y2)的距离
double d1=|P1-P0|=sqrt((x1-x0)*(x1-x0)+(y1-x0)*(y1-x0));
double d2=|P2-P0|=sqrt((x2-x0)*(x2-x0)+(y2-x0)*(y2-x0));
订单d1,d2升序
if (d1>d2) { double d=d1; d1=d2; d2=d; }
检查交叉路口
if ((d1<=r)&&(d2>=r)) return true; else return false;
r
是圆半径[注释]
r*r
进行比较,而不是r