垂直和水平线的圆线交点

时间:2014-09-12 13:58:13

标签: algorithm line geometry intersection

我试图检测一条线在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);
}

3 个答案:

答案 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²

用线方程替换xy,您将获得二次方程式:

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介于01之间,我们可以将其插入并检查距离。当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)

如果您不需要该点,只想知道线是否相交:

  1. 计算距圆心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));
  2. 订单d1,d2升序

    • if (d1>d2) { double d=d1; d1=d2; d2=d; }
  3. 检查交叉路口

    • if ((d1<=r)&&(d2>=r)) return true; else return false;
    • r是圆半径
  4. [注释]

    • 您不需要sqrt距离
    • 如果您将它们保留为非sqlica,则只需将它们与r*r进行比较,而不是r