2014-09-12 101 views
0

我試圖檢測一條線在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

對於初學者來說,'k1'計算直線的斜率。但垂直線沒有斜坡;試圖計算它將需要除以零。而'k3',斜率的倒數,將水平線除以零。 – Kevin 2014-09-12 14:23:55

+0

@Kevin這是有道理的。在這種情況下我能做些什麼來檢查?你有什麼想法xx和yy代表什麼?謝謝。 – CountingStacks 2014-09-12 14:44:23

回答

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; 
} 
+0

這個伎倆!感謝那。 – CountingStacks 2014-09-12 19:15:28

0

如果您不需要點只是想知道,如果線相交,則:從圓心P0(X0,Y0)和線路端點P1

  1. 計算距離(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是圓半徑

[注意事項]

  • 你並不需要開方距離
  • ,如果你讓他們取消sqrted然後把它們比作r*r而不是r
+0

好主意,但是原始代碼檢查是否有碰撞,這樣一個完全包含在該圓圈中的線被視爲碰撞;該檢查應該只是'd1 2014-09-12 18:12:22

+0

@Meehm yep你說得對,對於p1,p2在圈外的線條,應該有P1的垂直距離,P2使用P0代替。當OP代碼無法正常工作時,該手柄處於邊緣情況 – Spektre 2014-09-12 21:21:32

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) 

假設t01之間,我們可以將其插入和檢查的距離。當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; 
} 
+0

非常好。可能是這個問題的最佳解決方案;它不需要迎合圈內兩個點的特殊情況。 – 2014-09-12 19:17:06