Thuật toán phát hiện va chạm phân đoạn đường tròn?


195

Tôi có một đường thẳng từ A đến B và một đường tròn được định vị tại C với bán kính R.

Hình ảnh

Một thuật toán tốt để sử dụng để kiểm tra xem đường thẳng có giao nhau với vòng tròn không? Và tại tọa độ nào dọc theo các vòng tròn cạnh nó xảy ra?


4
Hừm. Một câu hỏi: bạn đang nói về dòng vô hạn qua A và B, hay đoạn thẳng hữu hạn từ A đến B?
Jason S

2
Trong trường hợp này, nó là phân khúc dòng hữu hạn. "Dòng" được gọi là cái gì khác tùy thuộc vào nó là hữu hạn hay vô hạn?
Mizipzor

1
Có một yêu cầu hiệu suất? Nó có nên là một phương pháp nhanh?
chmike

Tại thời điểm này, không, tất cả các thuật toán ở đây mà tôi đã thử không làm chậm ứng dụng một cách đáng chú ý.
Mizipzor

13
@Mizipzor vâng, chúng được gọi là cái gì khác: phân đoạn dòng . Nếu bạn chỉ nói "dòng" thì nó ngụ ý vô hạn.
MestreLion

Câu trả lời:


200

Đang lấy

  1. E là điểm bắt đầu của tia,
  2. L là điểm cuối của tia,
  3. C là trung tâm của hình cầu bạn đang thử nghiệm
  4. r là bán kính của hình cầu đó

Tính:
d = L - E (Vectơ chỉ hướng của tia, từ đầu đến cuối)
f = E - C (Vectơ từ tâm cầu đến tia bắt đầu)

Khi đó giao điểm được tìm thấy bởi ..
Cắm:
P = E + t * d
Đây là một phương trình tham số:
P x = E x + td x
P y = E y + td y
vào
(x - h) 2 + (y - k) 2 = r 2
(h, k) = tâm đường tròn.

Lưu ý: Chúng tôi đã đơn giản hóa vấn đề thành 2D ở đây, giải pháp chúng tôi nhận được cũng áp dụng trong 3D

để có được:

  1. Mở rộng
    x 2 - 2xh + h 2 + y 2 - 2yk + k 2 - r 2 = 0
  2. Cắm
    x = e x + td x
    y = e y + td y
    (e x + td x ) 2 - 2 (e x + td x ) h + h 2 + (e y + td y ) 2 - 2 (e y + td y ) k + k 2 - r 2 = 0
  3. Phát nổ
    e x 2 + 2e x td x + t 2 d x 2 - 2e x h - 2td x h + h 2 + e y 2 + 2e y td y + t 2 d y 2 - 2e y k - 2td y k + k 2 - r 2 = 0
  4. Nhóm
    t 2 (d x 2 + d y 2 ) + 2t (e x d x + e y d y - d x h - d y k) + e x 2 + e y 2 - 2e x h - 2e y k + h 2 + k 2 - r 2 = 0
  5. Cuối cùng,
    t 2 (_d * _d) + 2t (_e * _d - _d * _c) + _e * _e - 2 (_e * _c) + _c * _c - r 2 = 0
    * Trong đó _d là vectơ d và * là sản phẩm chấm. *
  6. Và sau đó,
    t 2 (_d * _d) + 2t (_d * (_e - _c)) + (_e - _c) * (_e - _c) - r 2 = 0
  7. Để _f = _e - _c
    t 2 (_d * _d) + 2t (_d * _f) + _f * _f - r 2 = 0

Vậy ta nhận được:
t 2 * (d DOT d) + 2t * (f DOT d) + (f DOT f - r 2 ) = 0
Vậy giải phương trình bậc hai:

float a = d.Dot( d ) ;
float b = 2*f.Dot( d ) ;
float c = f.Dot( f ) - r*r ;

float discriminant = b*b-4*a*c;
if( discriminant < 0 )
{
  // no intersection
}
else
{
  // ray didn't totally miss sphere,
  // so there is a solution to
  // the equation.

  discriminant = sqrt( discriminant );

  // either solution may be on or off the ray so need to test both
  // t1 is always the smaller value, because BOTH discriminant and
  // a are nonnegative.
  float t1 = (-b - discriminant)/(2*a);
  float t2 = (-b + discriminant)/(2*a);

  // 3x HIT cases:
  //          -o->             --|-->  |            |  --|->
  // Impale(t1 hit,t2 hit), Poke(t1 hit,t2>1), ExitWound(t1<0, t2 hit), 

  // 3x MISS cases:
  //       ->  o                     o ->              | -> |
  // FallShort (t1>1,t2>1), Past (t1<0,t2<0), CompletelyInside(t1<0, t2>1)

  if( t1 >= 0 && t1 <= 1 )
  {
    // t1 is the intersection, and it's closer than t2
    // (since t1 uses -b - discriminant)
    // Impale, Poke
    return true ;
  }

  // here t1 didn't intersect so we are either started
  // inside the sphere or completely past it
  if( t2 >= 0 && t2 <= 1 )
  {
    // ExitWound
    return true ;
  }

  // no intn: FallShort, Past, CompletelyInside
  return false ;
}

1
Có vẻ như sẽ hoạt động nếu tôi sao chép và dán thẳng, nhưng tôi đang tìm cách hiểu nó. Trong (xh) ^ 2 + (yk) ^ 2 = r ^ 2 h và k là gì? Là k đến hằng số mà dòng / tia tăng trên y trên x? Và t là gì? Nhìn vào mã có vẻ như bạn đã giả sử số 1 của nó (vì vậy nó chỉ "bị xóa"). Những công thức này có một tên hoặc một cái gì đó? Có lẽ tôi có thể tra cứu chúng chi tiết trên Wolfram.
Mizipzor

3
h và k là tâm của đường tròn mà bạn giao nhau. t là tham số của phương trình đường thẳng. Trong mã, t1 và t2 là các giải pháp. t1 và t2 cho bạn biết "khoảng cách dọc theo tia" giao lộ đã xảy ra.
bobobobo

1
OK đã nhận nó. Sản phẩm chấm được tính đơn giản trên ba vectơ (x, y, z). Tôi sẽ chuyển mã của tôi sang thuật toán này.
chmike

21
P = E + t * dt
Derek 朕 會

3
Không chắc chắn tại sao, nhưng mã dường như không hoạt động cho trường hợp Impale. Nó thực hiện khi tôi thêm nếu t1 <= 0 && t1> = -1 && t2 <= 0 && t2> = -1 là điều kiện đúng, nhưng sau đó nó cũng cho kết quả dương tính giả ở một bên của đường hữu hạn, khi đường tròn là phần "vô hạn". Tôi chưa hiểu toán, nhưng sao chép / dán, hãy cẩn thận.
Nicolas Mommaerts

141

Không ai có vẻ xem xét chiếu, tôi hoàn toàn không theo dõi ở đây?

Chiếu vector AClên AB. Các vectơ chiếu AD, cho điểm mới D.
Nếu khoảng cách giữa DCnhỏ hơn (hoặc bằng), Rchúng ta có một giao điểm.

Như thế này:
Hình ảnh của SchoolBoy


9
Có nhiều chi tiết cần xem xét: D có nằm giữa AB không? C có phải là khoảng cách vuông góc với đường thẳng lớn hơn bán kính không? Tất cả những điều này liên quan đến độ lớn của vectơ, tức là căn bậc hai.
ADB

15
Ý tưởng tốt, nhưng làm thế nào để bạn tính hai điểm giao nhau?
Ben

4
@Spider nó không thành vấn đề. Nói chung, vì đây là một biến thể của vấn đề giao cắt đường cầu, chiến lược của Mizipzor là hoàn toàn hợp lệ. CDlà một hình chiếu, nó vuông góc theo định nghĩa.

2
Đó là một câu hỏi cũ, nhưng có một nguồn tài nguyên tốt về điều này và các thuật toán có liên quan tại trang web này: paulbourke.net/geometry/pointlineplane
Andrew

1
Một lời giải thích tuyệt vời cho câu trả lời này: scratchapixel.com/lessons/3d-basic-rendering/,
ShawnFeatherly

50

Tôi sẽ sử dụng thuật toán để tính khoảng cách giữa một điểm (tâm đường tròn) và đường thẳng (đường AB). Điều này sau đó có thể được sử dụng để xác định các điểm giao nhau của đường thẳng với đường tròn.

Giả sử ta có các điểm A, B, C. Ax và Ay là thành phần x và y của các điểm A. Tương tự cho B và C. Vô hướng R là bán kính đường tròn.

Thuật toán này yêu cầu A, B và C là các điểm khác biệt và R không phải là 0.

Đây là thuật toán

// compute the euclidean distance between A and B
LAB = sqrt( (Bx-Ax)²+(By-Ay)² )

// compute the direction vector D from A to B
Dx = (Bx-Ax)/LAB
Dy = (By-Ay)/LAB

// the equation of the line AB is x = Dx*t + Ax, y = Dy*t + Ay with 0 <= t <= LAB.

// compute the distance between the points A and E, where
// E is the point of AB closest the circle center (Cx, Cy)
t = Dx*(Cx-Ax) + Dy*(Cy-Ay)    

// compute the coordinates of the point E
Ex = t*Dx+Ax
Ey = t*Dy+Ay

// compute the euclidean distance between E and C
LEC = sqrt((Ex-Cx)²+(Ey-Cy)²)

// test if the line intersects the circle
if( LEC < R )
{
    // compute distance from t to circle intersection point
    dt = sqrt( R² - LEC²)

    // compute first intersection point
    Fx = (t-dt)*Dx + Ax
    Fy = (t-dt)*Dy + Ay

    // compute second intersection point
    Gx = (t+dt)*Dx + Ax
    Gy = (t+dt)*Dy + Ay
}

// else test if the line is tangent to circle
else if( LEC == R )
    // tangent point to circle is E

else
    // line doesn't touch circle

nếu bất kỳ đường nào không giao nhau với đường tròn và cả hai điểm p1 và p2 của nó đều nằm trong vòng tròn. trong trường hợp này thuật toán của bạn hoạt động như thế nào ??
Prashant

1
Bạn phải kiểm tra t-dt và t + dt. Nếu t-dt <0, hơn p1 nằm trong vòng tròn. Nếu t + dt> 1, hơn p2 nằm trong vòng tròn. Điều này đúng nếu LEC <R tất nhiên.
chmike

Cảm ơn. Tôi thích lời bình luận pgm này như lời giải thích vì không sử dụng từ "chấm sản phẩm" vì toán học của tôi bị hoen gỉ. Tuy nhiên t và dt không nằm trong khoảng 0..1 nên trong khi thay đổi nó thành python, tôi đã thay đổi t để được chia cho LAB ** 2. Hiểu biết của tôi là phép chia đầu tiên của LAB là chiếu tâm của đường tròn vào đường thẳng AB và phép chia thứ hai theo LAB là bình thường hóa nó trong phạm vi 0..1. Ngoài ra, dt cần được chia cho LAB để nó cũng được chuẩn hóa. Do đó "if (t-dt> = 0.0)" tồn tại giao điểm thứ nhất "if (t + dt <= 1.0)" giao lộ thứ hai tồn tại. Điều này đã làm việc với thử nghiệm.
punchcard

2
Bởi vì điểm giao nhau với vòng tròn nằm ở "khoảng cách" t+dtt-dttrên đường thẳng. tlà điểm trên đường thẳng gần tâm của đường tròn. Các điểm giao nhau với đường tròn nằm ở khoảng cách đối xứng t. Các điểm giao nhau nằm ở "khoảng cách" t-dtt+dt. Tôi đã trích dẫn khoảng cách bởi vì nó không phải là khoảng cách eidianidian. Để có được khoảng cách eidianidian từ Ađâu t=0, bạn phải nhân giá trị với LAB.
chmike

1
@Matt W Bạn có nghĩa là "Làm thế nào để xác định xem giao lộ xảy ra bên ngoài các điểm cuối của đoạn đường AB"? Chỉ cần nghĩ về t như một thước đo khoảng cách dọc theo đường. Điểm A đang ở t=0. Điểm B tại t=LAB. Khi cả hai điểm giao nhau ( t1=t-tdt2=t+td) có giá trị âm hơn giao điểm nằm ngoài phần (phía sau điểm A nhìn từ phía phần của điểm). Khi t1 và t2 lớn hơn LAB thì chúng cũng ở bên ngoài (lần này nằm sau điểm B). Giao lộ t1 (hoặc t2) chỉ xảy ra giữa A và B khi t1 (hoặc t2) nằm trong khoảng từ 0 đến LAB.
Marconius

20

Được rồi, tôi sẽ không cung cấp cho bạn mã, nhưng vì bạn đã gắn thẻ này , Tôi không nghĩ rằng điều đó sẽ quan trọng với bạn. Đầu tiên, bạn phải có được một vectơ vuông góc với đường thẳng.

Bạn sẽ có một biến không xác định trong y = ax + c ( c sẽ không xác định )
Để giải quyết vấn đề đó, Tính giá trị của nó khi đường thẳng đi qua tâm của vòng tròn.

Đó là,
Cắm vị trí của tâm đường tròn vào phương trình đường thẳng và giải c.
Sau đó tính điểm giao nhau của đường ban đầu và bình thường của nó.

Điều này sẽ cung cấp cho bạn điểm gần nhất trên đường thẳng đến vòng tròn.
Tính khoảng cách giữa điểm này và tâm đường tròn (sử dụng độ lớn của vectơ).
Nếu giá trị này nhỏ hơn bán kính của vòng tròn - thì đấy, chúng ta có một giao điểm!


2
Trên thực tế, đó là những gì tôi muốn. Tôi muốn lý thuyết, một tìm kiếm google về thuật toán va chạm đường tròn chỉ bật mã theo như tôi có thể thấy.
Mizipzor

Ok, c không biết trong phương trình của bạn, nhưng "a" là gì? Các câu trả lời khác dường như đề cập đến biến đó là "alpha" và "t". Mặc dù, đây chỉ là một hàm tuyến tính (y = kx + m), toán học khá cơ bản, vì vậy tôi đột nhiên cảm thấy hơi gỉ. Có phải k cũng không biết? Hay bạn có nghĩa là chúng ta có thể giả sử m = 0 và giải k? Vậy thì m (nghĩa là c) luôn luôn bằng 0 đối với k đã giải của chúng ta?
Mizipzor

1
Ồ, xin lỗi - Tôi đang sử dụng phương trình đơn giản của một dòng với độ dốc và độ lệch (phương trình cartesian). Tôi giả sử rằng bạn đang lưu trữ đường thẳng như một phương trình - trong trường hợp đó bạn sử dụng âm của gradient cho k. Nếu bạn không có dòng được lưu trữ như thế này, bạn có thể tính k là (y2-y1) / (x2-x1)
a_m0d

1
Chúng tôi không cho rằng m bằng không; chúng ta tính toán độ dốc trước (để phương trình của đường thẳng trông giống như y = 2x + m làm ví dụ), và sau đó khi chúng ta có độ dốc, chúng ta có thể giải quyết cho m bằng cách cắm vào tâm của đường tròn cho y và x .
a_m0d

1
+1 lời giải thích tuyệt vời! Nhưng tôi nghĩ rằng điều này giả định một dòng, không phải là một phân khúc dòng. Vì vậy, nếu điểm gần nhất trên đường thẳng này đến tâm của vòng tròn không nằm giữa điểm A và B, thì nó vẫn sẽ được tính.
Hassan

12

Một phương pháp khác sử dụng công thức diện tích tam giác ABC. Phép thử giao cắt đơn giản và hiệu quả hơn phương pháp chiếu, nhưng việc tìm tọa độ của điểm giao nhau đòi hỏi nhiều công việc hơn. Ít nhất nó sẽ bị trì hoãn đến mức nó được yêu cầu.

Công thức tính diện tích tam giác là: area = bh / 2

Trong đó b là chiều dài cơ sở và h là chiều cao. Chúng tôi chọn đoạn AB làm cơ sở sao cho h là khoảng cách ngắn nhất từ ​​C, tâm đường tròn, đến đường thẳng.

Vì diện tích tam giác cũng có thể được tính bằng một sản phẩm chấm vector, chúng ta có thể xác định h.

// compute the triangle area times 2 (area = area2/2)
area2 = abs( (Bx-Ax)*(Cy-Ay) - (Cx-Ax)(By-Ay) )

// compute the AB segment length
LAB = sqrt( (Bx-Ax)² + (By-Ay)² )

// compute the triangle height
h = area2/LAB

// if the line intersects the circle
if( h < R )
{
    ...
}        

CẬP NHẬT 1:

Bạn có thể tối ưu hóa mã bằng cách sử dụng tính toán căn bậc hai nghịch đảo nhanh được mô tả ở đây để có được xấp xỉ 1 / LAB.

Tính toán điểm giao nhau không khó. Nó đi từ đây

// compute the line AB direction vector components
Dx = (Bx-Ax)/LAB
Dy = (By-Ay)/LAB

// compute the distance from A toward B of closest point to C
t = Dx*(Cx-Ax) + Dy*(Cy-Ay)

// t should be equal to sqrt( (Cx-Ax)² + (Cy-Ay)² - h² )

// compute the intersection point distance from t
dt = sqrt( R² - h² )

// compute first intersection point coordinate
Ex = Ax + (t-dt)*Dx
Ey = Ay + (t-dt)*Dy

// compute second intersection point coordinate
Fx = Ax + (t+dt)*Dx
Fy = Ay + (t+dt)*Dy

Nếu h = R thì đường thẳng AB tiếp tuyến với đường tròn và giá trị dt = 0 và E = F. Tọa độ điểm là của E và F.

Bạn nên kiểm tra xem A khác với B và độ dài phân đoạn không phải là null nếu điều này có thể xảy ra trong ứng dụng của bạn.


2
Tôi thích sự đơn giản trong phương pháp này. Có lẽ tôi có thể điều chỉnh một số mã xung quanh để không cần chính điểm va chạm thực tế, Ill xem điều gì xảy ra nếu tôi sử dụng A hoặc B thay vì điểm tính toán ở giữa.
Mizipzor

1
t = Dx * (Cx-Axe) + Dy * (Cy-Ax) nên đọc t = Dx * (Cx-Axe) + Dy * (Cy-Ay)
Stonetip

Đúng rồi đó. Cảm ơn vì chỉ ra điều ấy. Tôi đã sửa nó trong bài viết.
chmike

vừa chỉnh sửa - dòng đầu tiên tính diện tích tam giác bằng cách sử dụng sản phẩm chéo , không phải sản phẩm chấm. được xác minh bằng mã tại đây: stackoverflow.com/questions/2533011/ từ
ericsoco

4
cũng lưu ý, nửa đầu của câu trả lời này kiểm tra giao điểm với một dòng, không phải là một phân đoạn dòng (như được hỏi trong câu hỏi).
ericsoco

8

Tôi đã viết một đoạn script nhỏ để kiểm tra giao lộ bằng cách chiếu điểm trung tâm của vòng tròn trên đường thẳng.

vector distVector = centerPoint - projectedPoint;
if(distVector.length() < circle.radius)
{
    double distance = circle.radius - distVector.length();
    vector moveVector = distVector.normalize() * distance;
    circle.move(moveVector);
}

http://jsfiddle.net/ercang/ornh3594/1/

Nếu bạn cần kiểm tra xung đột với phân khúc, bạn cũng cần xem xét khoảng cách của tâm vòng tròn để bắt đầu và kết thúc điểm.

vector distVector = centerPoint - startPoint;
if(distVector.length() < circle.radius)
{
    double distance = circle.radius - distVector.length();
    vector moveVector = distVector.normalize() * distance;
    circle.move(moveVector);
}

https://jsfiddle.net/ercang/menp0991/


5

Giải pháp này tôi thấy có vẻ dễ dàng hơn một chút để theo dõi sau đó một số những người khác.

Đang lấy:

p1 and p2 as the points for the line, and
c as the center point for the circle and r for the radius

Tôi sẽ giải phương trình của đường thẳng ở dạng chặn dốc. Tuy nhiên, tôi không muốn phải xử lý các phương trình khó cnhư một điểm, vì vậy tôi chỉ cần chuyển hệ tọa độ sang để vòng tròn ở0,0

p3 = p1 - c
p4 = p2 - c

Nhân tiện, bất cứ khi nào tôi trừ điểm của nhau, tôi sẽ trừ đi xvà trừ đi y, và đưa chúng vào một điểm mới, trong trường hợp ai đó không biết.

Dù sao, bây giờ tôi giải phương trình của đường thẳng với p3p4:

m = (p4_y - p3_y) / (p4_x - p3) (the underscore is an attempt at subscript)
y = mx + b
y - mx = b (just put in a point for x and y, and insert the m we found)

Đồng ý. Bây giờ tôi cần đặt các phương trình bằng nhau. Đầu tiên tôi cần giải phương trình đường tròn chox

x^2 + y^2 = r^2
y^2 = r^2 - x^2
y = sqrt(r^2 - x^2)

Sau đó, tôi đặt chúng bằng nhau:

mx + b = sqrt(r^2 - x^2)

Và giải phương trình bậc hai ( 0 = ax^2 + bx + c):

(mx + b)^2 = r^2 - x^2
(mx)^2 + 2mbx + b^2 = r^2 - x^2
0 = m^2 * x^2 + x^2 + 2mbx + b^2 - r^2
0 = (m^2 + 1) * x^2 + 2mbx + b^2 - r^2

Bây giờ tôi có tôi a, bc.

a = m^2 + 1
b = 2mb
c = b^2 - r^2

Vì vậy, tôi đưa điều này vào công thức bậc hai:

(-b ± sqrt(b^2 - 4ac)) / 2a

Và thay thế bằng các giá trị sau đó đơn giản hóa càng nhiều càng tốt:

(-2mb ± sqrt(b^2 - 4ac)) / 2a
(-2mb ± sqrt((-2mb)^2 - 4(m^2 + 1)(b^2 - r^2))) / 2(m^2 + 1)
(-2mb ± sqrt(4m^2 * b^2 - 4(m^2 * b^2 - m^2 * r^2 + b^2 - r^2))) / 2m^2 + 2
(-2mb ± sqrt(4 * (m^2 * b^2 - (m^2 * b^2 - m^2 * r^2 + b^2 - r^2))))/ 2m^2 + 2
(-2mb ± sqrt(4 * (m^2 * b^2 - m^2 * b^2 + m^2 * r^2 - b^2 + r^2)))/ 2m^2 + 2
(-2mb ± sqrt(4 * (m^2 * r^2 - b^2 + r^2)))/ 2m^2 + 2
(-2mb ± sqrt(4) * sqrt(m^2 * r^2 - b^2 + r^2))/ 2m^2 + 2
(-2mb ± 2 * sqrt(m^2 * r^2 - b^2 + r^2))/ 2m^2 + 2
(-2mb ± 2 * sqrt(m^2 * r^2 + r^2 - b^2))/ 2m^2 + 2
(-2mb ± 2 * sqrt(r^2 * (m^2 + 1) - b^2))/ 2m^2 + 2

Điều này là gần như nó sẽ đơn giản hóa. Cuối cùng, tách ra các phương trình với giá trị ±:

(-2mb + 2 * sqrt(r^2 * (m^2 + 1) - b^2))/ 2m^2 + 2 or     
(-2mb - 2 * sqrt(r^2 * (m^2 + 1) - b^2))/ 2m^2 + 2 

Sau đó chỉ cần cắm là kết quả của cả hai trong những phương trình vào xtrong mx + b. Để rõ ràng, tôi đã viết một số mã JavaScript để hiển thị cách sử dụng mã này:

function interceptOnCircle(p1,p2,c,r){
    //p1 is the first line point
    //p2 is the second line point
    //c is the circle's center
    //r is the circle's radius

    var p3 = {x:p1.x - c.x, y:p1.y - c.y} //shifted line points
    var p4 = {x:p2.x - c.x, y:p2.y - c.y}

    var m = (p4.y - p3.y) / (p4.x - p3.x); //slope of the line
    var b = p3.y - m * p3.x; //y-intercept of line

    var underRadical = Math.pow((Math.pow(r,2)*(Math.pow(m,2)+1)),2)-Math.pow(b,2)); //the value under the square root sign 

    if (underRadical < 0){
    //line completely missed
        return false;
    } else {
        var t1 = (-2*m*b+2*Math.sqrt(underRadical))/(2 * Math.pow(m,2) + 2); //one of the intercept x's
        var t2 = (-2*m*b-2*Math.sqrt(underRadical))/(2 * Math.pow(m,2) + 2); //other intercept's x
        var i1 = {x:t1,y:m*t1+b} //intercept point 1
        var i2 = {x:t2,y:m*t2+b} //intercept point 2
        return [i1,i2];
    }
}

Tôi hi vọng cái này giúp được!

PS Nếu bất cứ ai tìm thấy bất kỳ lỗi hoặc có bất kỳ đề nghị, xin vui lòng bình luận. Tôi rất mới và hoan nghênh mọi sự giúp đỡ / gợi ý.


Nếu có thể, cũng đăng với một số giá trị mẫu để chúng tôi có thể nhanh chóng nắm bắt luồng.

underRadicalthêm ')'
byJeevan

4

Bạn có thể tìm thấy một điểm trên một đường thẳng vô hạn gần trung tâm đường tròn bằng cách chiếu vectơ AC lên vectơ AB. Tính khoảng cách giữa điểm đó và tâm đường tròn. Nếu R lớn hơn, không có giao điểm. Nếu khoảng cách bằng R, đường thẳng là tiếp tuyến của đường tròn và điểm gần trung tâm vòng tròn thực sự là điểm giao nhau. Nếu khoảng cách nhỏ hơn R thì có 2 điểm giao nhau. Chúng nằm ở cùng một khoảng cách từ điểm gần nhất đến tâm vòng tròn. Khoảng cách đó có thể dễ dàng được tính bằng định lý Pythagore. Đây là thuật toán trong mã giả:

{
dX = bX - aX;
dY = bY - aY;
if ((dX == 0) && (dY == 0))
  {
  // A and B are the same points, no way to calculate intersection
  return;
  }

dl = (dX * dX + dY * dY);
t = ((cX - aX) * dX + (cY - aY) * dY) / dl;

// point on a line nearest to circle center
nearestX = aX + t * dX;
nearestY = aY + t * dY;

dist = point_dist(nearestX, nearestY, cX, cY);

if (dist == R)
  {
  // line segment touches circle; one intersection point
  iX = nearestX;
  iY = nearestY;

  if (t < 0 || t > 1)
    {
    // intersection point is not actually within line segment
    }
  }
else if (dist < R)
  {
  // two possible intersection points

  dt = sqrt(R * R - dist * dist) / sqrt(dl);

  // intersection point nearest to A
  t1 = t - dt;
  i1X = aX + t1 * dX;
  i1Y = aY + t1 * dY;
  if (t1 < 0 || t1 > 1)
    {
    // intersection point is not actually within line segment
    }

  // intersection point farthest from A
  t2 = t + dt;
  i2X = aX + t2 * dX;
  i2Y = aY + t2 * dY;
  if (t2 < 0 || t2 > 1)
    {
    // intersection point is not actually within line segment
    }
  }
else
  {
  // no intersection
  }
}

EDIT: đã thêm mã để kiểm tra xem các điểm giao nhau có thực sự nằm trong đoạn đường không.


Bạn đã bỏ lỡ một trường hợp vì chúng ta đang nói về một đoạn đường: khi đoạn đó kết thúc trong vòng tròn.
ADB

@ADB thực sự thuật toán của tôi chỉ hoạt động cho các dòng vô hạn, không phải các phân đoạn dòng. Có nhiều trường hợp nó không xử lý với các phân đoạn dòng.
Juozas Kontvainis

Câu hỏi ban đầu là về các phân đoạn dòng, không phải giao lộ đường tròn, đây là một vấn đề dễ dàng hơn nhiều.
msumme

4

Thật kỳ lạ tôi có thể trả lời nhưng không bình luận ... Tôi thích cách tiếp cận của Multitaskpro là thay đổi mọi thứ để làm cho tâm của vòng tròn rơi vào điểm gốc. Thật không may, có hai vấn đề trong mã của mình. Đầu tiên trong phần dưới căn bậc hai, bạn cần loại bỏ công suất gấp đôi. Vậy thì không:

var underRadical = Math.pow((Math.pow(r,2)*(Math.pow(m,2)+1)),2)-Math.pow(b,2));

nhưng:

var underRadical = Math.pow(r,2)*(Math.pow(m,2)+1)) - Math.pow(b,2);

Trong tọa độ cuối cùng, anh ta quên chuyển giải pháp trở lại. Vậy thì không:

var i1 = {x:t1,y:m*t1+b}

nhưng:

var i1 = {x:t1+c.x, y:m*t1+b+c.y};

Toàn bộ chức năng sau đó trở thành:

function interceptOnCircle(p1, p2, c, r) {
    //p1 is the first line point
    //p2 is the second line point
    //c is the circle's center
    //r is the circle's radius

    var p3 = {x:p1.x - c.x, y:p1.y - c.y}; //shifted line points
    var p4 = {x:p2.x - c.x, y:p2.y - c.y};

    var m = (p4.y - p3.y) / (p4.x - p3.x); //slope of the line
    var b = p3.y - m * p3.x; //y-intercept of line

    var underRadical = Math.pow(r,2)*Math.pow(m,2) + Math.pow(r,2) - Math.pow(b,2); //the value under the square root sign 

    if (underRadical < 0) {
        //line completely missed
        return false;
    } else {
        var t1 = (-m*b + Math.sqrt(underRadical))/(Math.pow(m,2) + 1); //one of the intercept x's
        var t2 = (-m*b - Math.sqrt(underRadical))/(Math.pow(m,2) + 1); //other intercept's x
        var i1 = {x:t1+c.x, y:m*t1+b+c.y}; //intercept point 1
        var i2 = {x:t2+c.x, y:m*t2+b+c.y}; //intercept point 2
        return [i1, i2];
    }
}

1
đề xuất: Đầu tiên, yêu cầu nó xử lý trường hợp đoạn thẳng đứng (nghĩa là có độ dốc vô hạn). Thứ hai, nó chỉ trả về những điểm thực sự nằm trong phạm vi của phân khúc dòng ban đầu - tôi tin rằng thật vui khi trả lại tất cả các điểm nằm trên dòng vô hạn, ngay cả khi những điểm đó nằm ngoài phân khúc dòng.
Gino

Lưu ý: Điều này hoạt động tốt cho các dòng, nhưng không hoạt động cho các phân đoạn dòng.
Mike

3

Bạn sẽ cần một số toán học ở đây:

Giả sử A = (Xa, Ya), B = (Xb, Yb) và C = (Xc, Yc). Bất kỳ điểm nào trên đường thẳng từ A đến B đều có tọa độ (alpha * Xa + (1-alpha) Xb, alpha Ya + (1-alpha) * Yb) = P

Nếu điểm P có khoảng cách R đến C, nó phải nằm trên đường tròn. Điều bạn muốn là giải quyết

distance(P, C) = R

đó là

(alpha*Xa + (1-alpha)*Xb)^2 + (alpha*Ya + (1-alpha)*Yb)^2 = R^2
alpha^2*Xa^2 + alpha^2*Xb^2 - 2*alpha*Xb^2 + Xb^2 + alpha^2*Ya^2 + alpha^2*Yb^2 - 2*alpha*Yb^2 + Yb^2=R^2
(Xa^2 + Xb^2 + Ya^2 + Yb^2)*alpha^2 - 2*(Xb^2 + Yb^2)*alpha + (Xb^2 + Yb^2 - R^2) = 0

nếu bạn áp dụng công thức ABC cho phương trình này để giải nó cho alpha và tính tọa độ của P bằng cách sử dụng (các) giải pháp cho alpha, bạn sẽ có được các điểm giao nhau, nếu có tồn tại.


3

Nếu bạn tìm thấy khoảng cách giữa tâm của hình cầu (vì đó là 3D tôi giả sử bạn có nghĩa là hình cầu và không phải hình tròn) và đường thẳng, sau đó kiểm tra xem khoảng cách đó có nhỏ hơn bán kính sẽ thực hiện thủ thuật không.

Điểm va chạm rõ ràng là điểm gần nhất giữa đường và hình cầu (sẽ được tính khi bạn tính khoảng cách giữa hình cầu và đường)

Khoảng cách giữa một điểm và một đường:
http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html


1
Đó là 2D, không phải 3D; như bạn nói, điều này không thực sự quan trọng
Martijn

Tôi không phải là nhà toán học nên tôi nghĩ rằng tôi nên phác thảo một cách tiếp cận chung và để nó cho những người khác tìm ra những phép toán cụ thể (mặc dù tôi trông khá tầm thường)
Martin

2
+1 với một upvote mạnh mẽ. (mặc dù tôi đã liên kết đến một trang web khác, trang web pbourke có vẻ khó hiểu) Tất cả các câu trả lời khác cho đến nay đều quá phức tạp. Mặc dù nhận xét của bạn "Điểm đó cũng là điểm giao nhau trên đường thẳng" là không chính xác, không có điểm nào được xây dựng trong quá trình tính toán.
Jason S


Tôi đã giải thích rõ hơn một chút về điểm gần nhất và liên kết với thế giới toán học thay vì pbourke :)
Martin

3

Đây là một triển khai trong Javascript. Cách tiếp cận của tôi là trước tiên chuyển đổi phân đoạn dòng thành một dòng vô hạn sau đó tìm (các) điểm giao nhau. Từ đó tôi kiểm tra xem (các) điểm được tìm thấy có nằm trên đoạn thẳng không. Mã này được ghi chép tốt, bạn sẽ có thể làm theo.

Bạn có thể thử mã ở đây trên bản demo trực tiếp này . Mã được lấy từ repo thuật toán của tôi .

nhập mô tả hình ảnh ở đây

// Small epsilon value
var EPS = 0.0000001;

// point (x, y)
function Point(x, y) {
  this.x = x;
  this.y = y;
}

// Circle with center at (x,y) and radius r
function Circle(x, y, r) {
  this.x = x;
  this.y = y;
  this.r = r;
}

// A line segment (x1, y1), (x2, y2)
function LineSegment(x1, y1, x2, y2) {
  var d = Math.sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
  if (d < EPS) throw 'A point is not a line segment';
  this.x1 = x1; this.y1 = y1;
  this.x2 = x2; this.y2 = y2;
}

// An infinite line defined as: ax + by = c
function Line(a, b, c) {
  this.a = a; this.b = b; this.c = c;
  // Normalize line for good measure
  if (Math.abs(b) < EPS) {
    c /= a; a = 1; b = 0;
  } else { 
    a = (Math.abs(a) < EPS) ? 0 : a / b;
    c /= b; b = 1; 
  }
}

// Given a line in standard form: ax + by = c and a circle with 
// a center at (x,y) with radius r this method finds the intersection
// of the line and the circle (if any). 
function circleLineIntersection(circle, line) {

  var a = line.a, b = line.b, c = line.c;
  var x = circle.x, y = circle.y, r = circle.r;

  // Solve for the variable x with the formulas: ax + by = c (equation of line)
  // and (x-X)^2 + (y-Y)^2 = r^2 (equation of circle where X,Y are known) and expand to obtain quadratic:
  // (a^2 + b^2)x^2 + (2abY - 2ac + - 2b^2X)x + (b^2X^2 + b^2Y^2 - 2bcY + c^2 - b^2r^2) = 0
  // Then use quadratic formula X = (-b +- sqrt(a^2 - 4ac))/2a to find the 
  // roots of the equation (if they exist) and this will tell us the intersection points

  // In general a quadratic is written as: Ax^2 + Bx + C = 0
  // (a^2 + b^2)x^2 + (2abY - 2ac + - 2b^2X)x + (b^2X^2 + b^2Y^2 - 2bcY + c^2 - b^2r^2) = 0
  var A = a*a + b*b;
  var B = 2*a*b*y - 2*a*c - 2*b*b*x;
  var C = b*b*x*x + b*b*y*y - 2*b*c*y + c*c - b*b*r*r;

  // Use quadratic formula x = (-b +- sqrt(a^2 - 4ac))/2a to find the 
  // roots of the equation (if they exist).

  var D = B*B - 4*A*C;
  var x1,y1,x2,y2;

  // Handle vertical line case with b = 0
  if (Math.abs(b) < EPS) {

    // Line equation is ax + by = c, but b = 0, so x = c/a
    x1 = c/a;

    // No intersection
    if (Math.abs(x-x1) > r) return [];

    // Vertical line is tangent to circle
    if (Math.abs((x1-r)-x) < EPS || Math.abs((x1+r)-x) < EPS)
      return [new Point(x1, y)];

    var dx = Math.abs(x1 - x);
    var dy = Math.sqrt(r*r-dx*dx);

    // Vertical line cuts through circle
    return [
      new Point(x1,y+dy),
      new Point(x1,y-dy)
    ];

  // Line is tangent to circle
  } else if (Math.abs(D) < EPS) {

    x1 = -B/(2*A);
    y1 = (c - a*x1)/b;

    return [new Point(x1,y1)];

  // No intersection
  } else if (D < 0) {

    return [];

  } else {

    D = Math.sqrt(D);

    x1 = (-B+D)/(2*A);
    y1 = (c - a*x1)/b;

    x2 = (-B-D)/(2*A);
    y2 = (c - a*x2)/b;

    return [
      new Point(x1, y1),
      new Point(x2, y2)
    ];

  }

}

// Converts a line segment to a line in general form
function segmentToGeneralForm(x1,y1,x2,y2) {
  var a = y1 - y2;
  var b = x2 - x1;
  var c = x2*y1 - x1*y2;
  return new Line(a,b,c);
}

// Checks if a point 'pt' is inside the rect defined by (x1,y1), (x2,y2)
function pointInRectangle(pt,x1,y1,x2,y2) {
  var x = Math.min(x1,x2), X = Math.max(x1,x2);
  var y = Math.min(y1,y2), Y = Math.max(y1,y2);
  return x - EPS <= pt.x && pt.x <= X + EPS &&
         y - EPS <= pt.y && pt.y <= Y + EPS;
}

// Finds the intersection(s) of a line segment and a circle
function lineSegmentCircleIntersection(segment, circle) {

  var x1 = segment.x1, y1 = segment.y1, x2 = segment.x2, y2 = segment.y2;
  var line = segmentToGeneralForm(x1,y1,x2,y2);
  var pts = circleLineIntersection(circle, line);

  // No intersection
  if (pts.length === 0) return [];

  var pt1 = pts[0];
  var includePt1 = pointInRectangle(pt1,x1,y1,x2,y2);

  // Check for unique intersection
  if (pts.length === 1) {
    if (includePt1) return [pt1];
    return [];
  }

  var pt2 = pts[1];
  var includePt2 = pointInRectangle(pt2,x1,y1,x2,y2);

  // Check for remaining intersections
  if (includePt1 && includePt2) return [pt1, pt2];
  if (includePt1) return [pt1];
  if (includePt2) return [pt2];
  return [];

}

3

Trong bài đăng này, xung đột đường tròn sẽ được kiểm tra bằng cách kiểm tra khoảng cách giữa tâm vòng tròn và điểm trên đoạn đường (Ipoint) thể hiện điểm giao nhau giữa N (Hình 2) bình thường từ tâm vòng tròn đến đoạn đường.

( https://i.stack.imgur.com/3o6do.png )Hình 1. Tìm vectơ E và D

Trên hình ảnh 1 một hình tròn và một dòng được hiển thị, vectơ A điểm bắt đầu điểm, vectơ B điểm đến điểm kết thúc dòng, vectơ C điểm đến tâm vòng tròn. Bây giờ chúng ta phải tìm vectơ E (từ điểm bắt đầu dòng đến tâm vòng tròn) và vectơ D (từ điểm bắt đầu dòng đến điểm cuối dòng) phép tính này được hiển thị trên hình ảnh 1.

( https://i.stack.imgur.com/7098a.png )Hình 2. Tìm vectơ X

Ở hình 2, chúng ta có thể thấy rằng vectơ E được chiếu trên Vector D bởi "sản phẩm chấm" của vectơ E và vectơ đơn vị D, kết quả của sản phẩm chấm là vô hướng Xp biểu thị khoảng cách giữa điểm bắt đầu dòng và điểm giao nhau (Ipoint) của vectơ N và vectơ D. Vectơ X tiếp theo được tìm thấy bằng cách nhân vectơ đơn vị D và vô hướng Xp.

Bây giờ chúng ta cần tìm vectơ Z (vectơ đến Ipoint), dễ dàng thêm vectơ đơn giản của vectơ A (điểm bắt đầu trên dòng) và vectơ X. Tiếp theo chúng ta cần xử lý các trường hợp đặc biệt, chúng ta phải kiểm tra là Ipoint trên đoạn thẳng, nếu Không phải chúng ta phải tìm ra nó là bên trái của nó hay bên phải của nó, chúng ta sẽ sử dụng vectơ gần nhất để xác định điểm nào gần nhất với đường tròn.

( https://i.stack.imgur.com/p9WIr.png )Hình 3. Tìm điểm gần nhất

Khi phép chiếu Xp là Ipoint âm bên trái của đoạn thẳng, vectơ gần nhất bằng vectơ của điểm bắt đầu dòng, khi phép chiếu Xp lớn hơn độ lớn của vectơ D thì Ipoint nằm bên phải đoạn thẳng, vectơ gần nhất bằng vectơ cuối dòng điểm trong bất kỳ trường hợp nào khác vectơ gần nhất bằng vectơ Z.

Bây giờ khi chúng ta có vectơ gần nhất, chúng ta cần tìm vectơ từ tâm vòng tròn đến Ipoint (vectơ dist), đơn giản chúng ta chỉ cần trừ vectơ gần nhất khỏi vectơ trung tâm. Tiếp theo, chỉ cần kiểm tra xem cường độ của vectơ có nhỏ hơn bán kính vòng tròn hay không nếu đó là va chạm, nếu nó không có va chạm.

( https://i.stack.imgur.com/QJ63q.png )Hình ảnh 4. Kiểm tra va chạm

Để kết thúc, chúng ta có thể trả về một số giá trị để giải quyết va chạm, cách dễ nhất là trả lại sự chồng chéo của va chạm (trừ bán kính từ cường độ vectơ) và trục quay trở lại, vectơ của nó D. Ngoài ra điểm giao nhau là vectơ Z nếu cần.


2

Nếu tọa độ của dòng là Ax, Ay và Bx, By và tâm đường tròn là Cx, Cy thì các công thức của dòng là:

x = Ax * t + Bx * (1 - t)

y = Ay * t + By * (1 - t)

trong đó 0 <= t <= 1

và vòng tròn là

(Cx - x) ^ 2 + (Cy - y) ^ 2 = R ^ 2

nếu bạn thay thế các công thức x và y của dòng vào công thức đường tròn, bạn sẽ có phương trình bậc hai của t và các giải pháp của nó là các điểm giao nhau (nếu có). Nếu bạn nhận được tại đó nhỏ hơn 0 hoặc lớn hơn 1 thì đó không phải là giải pháp nhưng nó cho thấy đường thẳng đang 'chỉ' theo hướng của vòng tròn.


2

Chỉ là một bổ sung cho chủ đề này ... Dưới đây là một phiên bản mã được đăng bởi pahlevan, nhưng đối với C # / XNA và được dọn dẹp một chút:

    /// <summary>
    /// Intersects a line and a circle.
    /// </summary>
    /// <param name="location">the location of the circle</param>
    /// <param name="radius">the radius of the circle</param>
    /// <param name="lineFrom">the starting point of the line</param>
    /// <param name="lineTo">the ending point of the line</param>
    /// <returns>true if the line and circle intersect each other</returns>
    public static bool IntersectLineCircle(Vector2 location, float radius, Vector2 lineFrom, Vector2 lineTo)
    {
        float ab2, acab, h2;
        Vector2 ac = location - lineFrom;
        Vector2 ab = lineTo - lineFrom;
        Vector2.Dot(ref ab, ref ab, out ab2);
        Vector2.Dot(ref ac, ref ab, out acab);
        float t = acab / ab2;

        if (t < 0)
            t = 0;
        else if (t > 1)
            t = 1;

        Vector2 h = ((ab * t) + lineFrom) - location;
        Vector2.Dot(ref h, ref h, out h2);

        return (h2 <= (radius * radius));
    }

Trong C # / XNA, bạn có thể sử dụngRay.Intersects(BoundingSphere)
bobobobo

2

nhập mô tả hình ảnh ở đây

' VB.NET - Code

Function CheckLineSegmentCircleIntersection(x1 As Double, y1 As Double, x2 As Double, y2 As Double, xc As Double, yc As Double, r As Double) As Boolean
    Static xd As Double = 0.0F
    Static yd As Double = 0.0F
    Static t As Double = 0.0F
    Static d As Double = 0.0F
    Static dx_2_1 As Double = 0.0F
    Static dy_2_1 As Double = 0.0F

    dx_2_1 = x2 - x1
    dy_2_1 = y2 - y1

    t = ((yc - y1) * dy_2_1 + (xc - x1) * dx_2_1) / (dy_2_1 * dy_2_1 + dx_2_1 * dx_2_1)

    If 0 <= t And t <= 1 Then
        xd = x1 + t * dx_2_1
        yd = y1 + t * dy_2_1

        d = Math.Sqrt((xd - xc) * (xd - xc) + (yd - yc) * (yd - yc))
        Return d <= r
    Else
        d = Math.Sqrt((xc - x1) * (xc - x1) + (yc - y1) * (yc - y1))
        If d <= r Then
            Return True
        Else
            d = Math.Sqrt((xc - x2) * (xc - x2) + (yc - y2) * (yc - y2))
            If d <= r Then
                Return True
            Else
                Return False
            End If
        End If
    End If
End Function

2

Tôi đã tạo chức năng này cho iOS theo câu trả lời được đưa ra bởi chmike

+ (NSArray *)intersectionPointsOfCircleWithCenter:(CGPoint)center withRadius:(float)radius toLinePoint1:(CGPoint)p1 andLinePoint2:(CGPoint)p2
{
    NSMutableArray *intersectionPoints = [NSMutableArray array];

    float Ax = p1.x;
    float Ay = p1.y;
    float Bx = p2.x;
    float By = p2.y;
    float Cx = center.x;
    float Cy = center.y;
    float R = radius;


    // compute the euclidean distance between A and B
    float LAB = sqrt( pow(Bx-Ax, 2)+pow(By-Ay, 2) );

    // compute the direction vector D from A to B
    float Dx = (Bx-Ax)/LAB;
    float Dy = (By-Ay)/LAB;

    // Now the line equation is x = Dx*t + Ax, y = Dy*t + Ay with 0 <= t <= 1.

    // compute the value t of the closest point to the circle center (Cx, Cy)
    float t = Dx*(Cx-Ax) + Dy*(Cy-Ay);

    // This is the projection of C on the line from A to B.

    // compute the coordinates of the point E on line and closest to C
    float Ex = t*Dx+Ax;
    float Ey = t*Dy+Ay;

    // compute the euclidean distance from E to C
    float LEC = sqrt( pow(Ex-Cx, 2)+ pow(Ey-Cy, 2) );

    // test if the line intersects the circle
    if( LEC < R )
    {
        // compute distance from t to circle intersection point
        float dt = sqrt( pow(R, 2) - pow(LEC,2) );

        // compute first intersection point
        float Fx = (t-dt)*Dx + Ax;
        float Fy = (t-dt)*Dy + Ay;

        // compute second intersection point
        float Gx = (t+dt)*Dx + Ax;
        float Gy = (t+dt)*Dy + Ay;

        [intersectionPoints addObject:[NSValue valueWithCGPoint:CGPointMake(Fx, Fy)]];
        [intersectionPoints addObject:[NSValue valueWithCGPoint:CGPointMake(Gx, Gy)]];
    }

    // else test if the line is tangent to circle
    else if( LEC == R ) {
        // tangent point to circle is E
        [intersectionPoints addObject:[NSValue valueWithCGPoint:CGPointMake(Ex, Ey)]];
    }
    else {
        // line doesn't touch circle
    }

    return intersectionPoints;
}

2

Một số khác trong c # (lớp Circle một phần). Đã thử nghiệm và hoạt động như một lá bùa.

public class Circle : IEquatable<Circle>
{
    // ******************************************************************
    // The center of a circle
    private Point _center;
    // The radius of a circle
    private double _radius;

   // ******************************************************************
    /// <summary>
    /// Find all intersections (0, 1, 2) of the circle with a line defined by its 2 points.
    /// Using: http://math.stackexchange.com/questions/228841/how-do-i-calculate-the-intersections-of-a-straight-line-and-a-circle
    /// Note: p is the Center.X and q is Center.Y
    /// </summary>
    /// <param name="linePoint1"></param>
    /// <param name="linePoint2"></param>
    /// <returns></returns>
    public List<Point> GetIntersections(Point linePoint1, Point linePoint2)
    {
        List<Point> intersections = new List<Point>();

        double dx = linePoint2.X - linePoint1.X;

        if (dx.AboutEquals(0)) // Straight vertical line
        {
            if (linePoint1.X.AboutEquals(Center.X - Radius) || linePoint1.X.AboutEquals(Center.X + Radius))
            {
                Point pt = new Point(linePoint1.X, Center.Y);
                intersections.Add(pt);
            }
            else if (linePoint1.X > Center.X - Radius && linePoint1.X < Center.X + Radius)
            {
                double x = linePoint1.X - Center.X;

                Point pt = new Point(linePoint1.X, Center.Y + Math.Sqrt(Radius * Radius - (x * x)));
                intersections.Add(pt);

                pt = new Point(linePoint1.X, Center.Y - Math.Sqrt(Radius * Radius - (x * x)));
                intersections.Add(pt);
            }

            return intersections;
        }

        // Line function (y = mx + b)
        double dy = linePoint2.Y - linePoint1.Y;
        double m = dy / dx;
        double b = linePoint1.Y - m * linePoint1.X;

        double A = m * m + 1;
        double B = 2 * (m * b - m * _center.Y - Center.X);
        double C = Center.X * Center.X + Center.Y * Center.Y - Radius * Radius - 2 * b * Center.Y + b * b;

        double discriminant = B * B - 4 * A * C;

        if (discriminant < 0)
        {
            return intersections; // there is no intersections
        }

        if (discriminant.AboutEquals(0)) // Tangeante (touch on 1 point only)
        {
            double x = -B / (2 * A);
            double y = m * x + b;

            intersections.Add(new Point(x, y));
        }
        else // Secant (touch on 2 points)
        {
            double x = (-B + Math.Sqrt(discriminant)) / (2 * A);
            double y = m * x + b;
            intersections.Add(new Point(x, y));

            x = (-B - Math.Sqrt(discriminant)) / (2 * A);
            y = m * x + b;
            intersections.Add(new Point(x, y));
        }

        return intersections;
    }

    // ******************************************************************
    // Get the center
    [XmlElement("Center")]
    public Point Center
    {
        get { return _center; }
        set
        {
            _center = value;
        }
    }

    // ******************************************************************
    // Get the radius
    [XmlElement]
    public double Radius
    {
        get { return _radius; }
        set { _radius = value; }
    }

    //// ******************************************************************
    //[XmlArrayItemAttribute("DoublePoint")]
    //public List<Point> Coordinates
    //{
    //    get { return _coordinates; }
    //}

    // ******************************************************************
    // Construct a circle without any specification
    public Circle()
    {
        _center.X = 0;
        _center.Y = 0;
        _radius = 0;
    }

    // ******************************************************************
    // Construct a circle without any specification
    public Circle(double radius)
    {
        _center.X = 0;
        _center.Y = 0;
        _radius = radius;
    }

    // ******************************************************************
    // Construct a circle with the specified circle
    public Circle(Circle circle)
    {
        _center = circle._center;
        _radius = circle._radius;
    }

    // ******************************************************************
    // Construct a circle with the specified center and radius
    public Circle(Point center, double radius)
    {
        _center = center;
        _radius = radius;
    }

    // ******************************************************************
    // Construct a circle based on one point
    public Circle(Point center)
    {
        _center = center;
        _radius = 0;
    }

    // ******************************************************************
    // Construct a circle based on two points
    public Circle(Point p1, Point p2)
    {
        Circle2Points(p1, p2);
    }

Cần thiết:

using System;

namespace Mathematic
{
    public static class DoubleExtension
    {
        // ******************************************************************
        // Base on Hans Passant Answer on:
        // http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre

        /// <summary>
        /// Compare two double taking in account the double precision potential error.
        /// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
        public static bool AboutEquals(this double value1, double value2)
        {
            if (double.IsPositiveInfinity(value1))
                return double.IsPositiveInfinity(value2);

            if (double.IsNegativeInfinity(value1))
                return double.IsNegativeInfinity(value2);

            if (double.IsNaN(value1))
                return double.IsNaN(value2);

            double epsilon = Math.Max(Math.Abs(value1), Math.Abs(value2)) * 1E-15;
            return Math.Abs(value1 - value2) <= epsilon;
        }

        // ******************************************************************
        // Base on Hans Passant Answer on:
        // http://stackoverflow.com/questions/2411392/double-epsilon-for-equality-greater-than-less-than-less-than-or-equal-to-gre

        /// <summary>
        /// Compare two double taking in account the double precision potential error.
        /// Take care: truncation errors accumulate on calculation. More you do, more you should increase the epsilon.
        /// You get really better performance when you can determine the contextual epsilon first.
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <param name="precalculatedContextualEpsilon"></param>
        /// <returns></returns>
        public static bool AboutEquals(this double value1, double value2, double precalculatedContextualEpsilon)
        {
            if (double.IsPositiveInfinity(value1))
                return double.IsPositiveInfinity(value2);

            if (double.IsNegativeInfinity(value1))
                return double.IsNegativeInfinity(value2);

            if (double.IsNaN(value1))
                return double.IsNaN(value2);

            return Math.Abs(value1 - value2) <= precalculatedContextualEpsilon;
        }

        // ******************************************************************
        public static double GetContextualEpsilon(this double biggestPossibleContextualValue)
        {
            return biggestPossibleContextualValue * 1E-15;
        }

        // ******************************************************************
        /// <summary>
        /// Mathlab equivalent
        /// </summary>
        /// <param name="dividend"></param>
        /// <param name="divisor"></param>
        /// <returns></returns>
        public static double Mod(this double dividend, double divisor)
        {
            return dividend - System.Math.Floor(dividend / divisor) * divisor;
        }

        // ******************************************************************
    }
}


2

Circle thực sự là một kẻ xấu :) Vì vậy, một cách tốt là tránh vòng tròn thực sự, nếu bạn có thể. Nếu bạn đang thực hiện kiểm tra va chạm cho các trò chơi, bạn có thể thực hiện một số đơn giản hóa và chỉ có 3 sản phẩm chấm, và một vài so sánh.

Tôi gọi đây là "điểm béo" hay "vòng tròn mỏng". nó là một hình elip có bán kính bằng 0 theo hướng song song với một đoạn. nhưng bán kính đầy đủ theo hướng vuông góc với một đoạn

Đầu tiên, tôi sẽ xem xét đổi tên và chuyển đổi hệ tọa độ để tránh dữ liệu quá mức:

s0s1 = B-A;
s0qp = C-A;
rSqr = r*r;

Thứ hai, chỉ số h trong hvec2f có nghĩa là vectơ phải ưu tiên các hoạt động horisontal, như dot () / det (). Điều đó có nghĩa là các thành phần của nó sẽ được đặt trong một thanh ghi xmm riêng biệt, để tránh xáo trộn / hadd'ing / hsub'ing. Và ở đây chúng tôi đi, với phiên bản hiệu quả nhất của phát hiện va chạm đơn giản nhất cho trò chơi 2D:

bool fat_point_collides_segment(const hvec2f& s0qp, const hvec2f& s0s1, const float& rSqr) {
    auto a = dot(s0s1, s0s1);
    //if( a != 0 ) // if you haven't zero-length segments omit this, as it would save you 1 _mm_comineq_ss() instruction and 1 memory fetch
    {
        auto b = dot(s0s1, s0qp);
        auto t = b / a; // length of projection of s0qp onto s0s1
        //std::cout << "t = " << t << "\n";
        if ((t >= 0) && (t <= 1)) // 
        {
            auto c = dot(s0qp, s0qp);
            auto r2 = c - a * t * t;
            return (r2 <= rSqr); // true if collides
        }
    }   
    return false;
}

Tôi nghi ngờ bạn có thể tối ưu hóa nó hơn nữa. Tôi đang sử dụng nó để phát hiện va chạm xe đua điều khiển mạng thần kinh, để xử lý hàng triệu triệu bước lặp.


Nếu đoạn đường cắt ngang vòng tròn nhưng chỉ một chút để nó không vượt qua điểm trung tâm của nó, chức năng này sẽ không trả về sai khi nó trở về đúng? Giá trị t có thể nằm ngoài phạm vi 0..1.
Chris

1

Hàm Java này trả về một đối tượng DVec2. Nó lấy một DVec2 cho tâm của vòng tròn, bán kính của vòng tròn và Đường thẳng.

public static DVec2 CircLine(DVec2 C, double r, Line line)
{
    DVec2 A = line.p1;
    DVec2 B = line.p2;
    DVec2 P;
    DVec2 AC = new DVec2( C );
    AC.sub(A);
    DVec2 AB = new DVec2( B );
    AB.sub(A);
    double ab2 = AB.dot(AB);
    double acab = AC.dot(AB);
    double t = acab / ab2;

    if (t < 0.0) 
        t = 0.0;
    else if (t > 1.0) 
        t = 1.0;

    //P = A + t * AB;
    P = new DVec2( AB );
    P.mul( t );
    P.add( A );

    DVec2 H = new DVec2( P );
    H.sub( C );
    double h2 = H.dot(H);
    double r2 = r * r;

    if(h2 > r2) 
        return null;
    else
        return P;
}

1

Đây là giải pháp của tôi trong TypeScript, theo ý tưởng mà @Mizipzor đã đề xuất (sử dụng phép chiếu):

/**
 * Determines whether a line segment defined by a start and end point intersects with a sphere defined by a center point and a radius
 * @param a the start point of the line segment
 * @param b the end point of the line segment
 * @param c the center point of the sphere
 * @param r the radius of the sphere
 */
export function lineSphereIntersects(
  a: IPoint,
  b: IPoint,
  c: IPoint,
  r: number
): boolean {
  // find the three sides of the triangle formed by the three points
  const ab: number = distance(a, b);
  const ac: number = distance(a, c);
  const bc: number = distance(b, c);

  // check to see if either ends of the line segment are inside of the sphere
  if (ac < r || bc < r) {
    return true;
  }

  // find the angle between the line segment and the center of the sphere
  const numerator: number = Math.pow(ac, 2) + Math.pow(ab, 2) - Math.pow(bc, 2);
  const denominator: number = 2 * ac * ab;
  const cab: number = Math.acos(numerator / denominator);

  // find the distance from the center of the sphere and the line segment
  const cd: number = Math.sin(cab) * ac;

  // if the radius is at least as long as the distance between the center and the line
  if (r >= cd) {
    // find the distance between the line start and the point on the line closest to
    // the center of the sphere
    const ad: number = Math.cos(cab) * ac;
    // intersection occurs when the point on the line closest to the sphere center is
    // no further away than the end of the line
    return ad <= ab;
  }
  return false;
}

export function distance(a: IPoint, b: IPoint): number {
  return Math.sqrt(
    Math.pow(b.z - a.z, 2) + Math.pow(b.y - a.y, 2) + Math.pow(b.x - a.x, 2)
  );
}

export interface IPoint {
  x: number;
  y: number;
  z: number;
}

1

Tôi biết đã được một lúc kể từ khi chủ đề này được mở. Từ câu trả lời được đưa ra bởi chmike và được cải thiện bởi Aqib Mumtaz. Họ đưa ra một câu trả lời tốt nhưng chỉ hoạt động cho một dòng vô hạn như Aqib nói. Vì vậy, tôi thêm một số so sánh để biết nếu đoạn đường chạm vào vòng tròn, tôi viết nó bằng Python.

def LineIntersectCircle(c, r, p1, p2):
    #p1 is the first line point
    #p2 is the second line point
    #c is the circle's center
    #r is the circle's radius

    p3 = [p1[0]-c[0], p1[1]-c[1]]
    p4 = [p2[0]-c[0], p2[1]-c[1]]

    m = (p4[1] - p3[1]) / (p4[0] - p3[0])
    b = p3[1] - m * p3[0]

    underRadical = math.pow(r,2)*math.pow(m,2) + math.pow(r,2) - math.pow(b,2)

    if (underRadical < 0):
        print("NOT")
    else:
        t1 = (-2*m*b+2*math.sqrt(underRadical)) / (2 * math.pow(m,2) + 2)
        t2 = (-2*m*b-2*math.sqrt(underRadical)) / (2 * math.pow(m,2) + 2)
        i1 = [t1+c[0], m * t1 + b + c[1]]
        i2 = [t2+c[0], m * t2 + b + c[1]]

        if p1[0] > p2[0]:                                           #Si el punto 1 es mayor al 2 en X
            if (i1[0] < p1[0]) and (i1[0] > p2[0]):                 #Si el punto iX esta entre 2 y 1 en X
                if p1[1] > p2[1]:                                   #Si el punto 1 es mayor al 2 en Y
                    if (i1[1] < p1[1]) and (i1[1] > p2[1]):         #Si el punto iy esta entre 2 y 1
                        print("Intersection")
                if p1[1] < p2[1]:                                   #Si el punto 2 es mayo al 2 en Y
                    if (i1[1] > p1[1]) and (i1[1] < p2[1]):         #Si el punto iy esta entre 1 y 2
                        print("Intersection")

        if p1[0] < p2[0]:                                           #Si el punto 2 es mayor al 1 en X
            if (i1[0] > p1[0]) and (i1[0] < p2[0]):                 #Si el punto iX esta entre 1 y 2 en X
                if p1[1] > p2[1]:                                   #Si el punto 1 es mayor al 2 en Y
                    if (i1[1] < p1[1]) and (i1[1] > p2[1]):         #Si el punto iy esta entre 2 y 1
                        print("Intersection")
                if p1[1] < p2[1]:                                   #Si el punto 2 es mayo al 2 en Y
                    if (i1[1] > p1[1]) and (i1[1] < p2[1]):         #Si el punto iy esta entre 1 y 2
                        print("Intersection")

        if p1[0] > p2[0]:                                           #Si el punto 1 es mayor al 2 en X
            if (i2[0] < p1[0]) and (i2[0] > p2[0]):                 #Si el punto iX esta entre 2 y 1 en X
                if p1[1] > p2[1]:                                   #Si el punto 1 es mayor al 2 en Y
                    if (i2[1] < p1[1]) and (i2[1] > p2[1]):         #Si el punto iy esta entre 2 y 1
                        print("Intersection")
                if p1[1] < p2[1]:                                   #Si el punto 2 es mayo al 2 en Y
                    if (i2[1] > p1[1]) and (i2[1] < p2[1]):         #Si el punto iy esta entre 1 y 2
                        print("Intersection")

        if p1[0] < p2[0]:                                           #Si el punto 2 es mayor al 1 en X
            if (i2[0] > p1[0]) and (i2[0] < p2[0]):                 #Si el punto iX esta entre 1 y 2 en X
                if p1[1] > p2[1]:                                   #Si el punto 1 es mayor al 2 en Y
                    if (i2[1] < p1[1]) and (i2[1] > p2[1]):         #Si el punto iy esta entre 2 y 1
                        print("Intersection")
                if p1[1] < p2[1]:                                   #Si el punto 2 es mayo al 2 en Y
                    if (i2[1] > p1[1]) and (i2[1] < p2[1]):         #Si el punto iy esta entre 1 y 2
                        print("Intersection")

0

Đây là một giải pháp được viết bằng golang. Phương pháp này tương tự như một số câu trả lời khác được đăng ở đây, nhưng không hoàn toàn giống nhau. Nó rất dễ thực hiện, và đã được thử nghiệm. Dưới đây là các bước:

  1. Dịch tọa độ sao cho vòng tròn ở gốc.
  2. Biểu thị đoạn thẳng dưới dạng hàm tham số của t cho cả tọa độ x và y. Nếu t là 0, các giá trị của hàm là một điểm cuối của đoạn và nếu t là 1, các giá trị của hàm là điểm cuối khác.
  3. Nếu có thể, phương trình bậc hai xuất phát từ các giá trị ràng buộc của t tạo ra tọa độ x, y với khoảng cách từ điểm gốc bằng bán kính của đường tròn.
  4. Loại bỏ các giải pháp trong đó t là <0 hoặc> 1 (<= 0 hoặc> = 1 cho một phân đoạn mở). Những điểm đó không có trong phân khúc.
  5. Dịch trở lại tọa độ ban đầu.

Các giá trị cho A, B và C cho bậc hai được lấy ở đây, trong đó (n-et) và (m-dt) lần lượt là các phương trình cho tọa độ x và y của dòng. r là bán kính của đường tròn.

(n-et)(n-et) + (m-dt)(m-dt) = rr
nn - 2etn + etet + mm - 2mdt + dtdt = rr
(ee+dd)tt - 2(en + dm)t + nn + mm - rr = 0

Do đó A = ee + dd, B = - 2 (en + dm) và C = nn + mm - rr.

Đây là mã golang cho chức năng:

package geom

import (
    "math"
)

// SegmentCircleIntersection return points of intersection between a circle and
// a line segment. The Boolean intersects returns true if one or
// more solutions exist. If only one solution exists, 
// x1 == x2 and y1 == y2.
// s1x and s1y are coordinates for one end point of the segment, and
// s2x and s2y are coordinates for the other end of the segment.
// cx and cy are the coordinates of the center of the circle and
// r is the radius of the circle.
func SegmentCircleIntersection(s1x, s1y, s2x, s2y, cx, cy, r float64) (x1, y1, x2, y2 float64, intersects bool) {
    // (n-et) and (m-dt) are expressions for the x and y coordinates
    // of a parameterized line in coordinates whose origin is the
    // center of the circle.
    // When t = 0, (n-et) == s1x - cx and (m-dt) == s1y - cy
    // When t = 1, (n-et) == s2x - cx and (m-dt) == s2y - cy.
    n := s2x - cx
    m := s2y - cy

    e := s2x - s1x
    d := s2y - s1y

    // lineFunc checks if the  t parameter is in the segment and if so
    // calculates the line point in the unshifted coordinates (adds back
    // cx and cy.
    lineFunc := func(t float64) (x, y float64, inBounds bool) {
        inBounds = t >= 0 && t <= 1 // Check bounds on closed segment
        // To check bounds for an open segment use t > 0 && t < 1
        if inBounds { // Calc coords for point in segment
            x = n - e*t + cx
            y = m - d*t + cy
        }
        return
    }

    // Since we want the points on the line distance r from the origin,
    // (n-et)(n-et) + (m-dt)(m-dt) = rr.
    // Expanding and collecting terms yeilds the following quadratic equation:
    A, B, C := e*e+d*d, -2*(e*n+m*d), n*n+m*m-r*r

    D := B*B - 4*A*C // discriminant of quadratic
    if D < 0 {
        return // No solution
    }
    D = math.Sqrt(D)

    var p1In, p2In bool
    x1, y1, p1In = lineFunc((-B + D) / (2 * A)) // First root
    if D == 0.0 {
        intersects = p1In
        x2, y2 = x1, y1
        return // Only possible solution, quadratic has one root.
    }

    x2, y2, p2In = lineFunc((-B - D) / (2 * A)) // Second root

    intersects = p1In || p2In
    if p1In == false { // Only x2, y2 may be valid solutions
        x1, y1 = x2, y2
    } else if p2In == false { // Only x1, y1 are valid solutions
        x2, y2 = x1, y1
    }
    return
}

Tôi đã thử nghiệm nó với chức năng này, xác nhận rằng các điểm giải pháp nằm trong đoạn đường và trên vòng tròn. Nó tạo một phân đoạn thử nghiệm và quét nó xung quanh vòng tròn đã cho:

package geom_test

import (
    "testing"

    . "**put your package path here**"
)

func CheckEpsilon(t *testing.T, v, epsilon float64, message string) {
    if v > epsilon || v < -epsilon {
        t.Error(message, v, epsilon)
        t.FailNow()
    }
}

func TestSegmentCircleIntersection(t *testing.T) {
    epsilon := 1e-10      // Something smallish
    x1, y1 := 5.0, 2.0    // segment end point 1
    x2, y2 := 50.0, 30.0  // segment end point 2
    cx, cy := 100.0, 90.0 // center of circle
    r := 80.0

    segx, segy := x2-x1, y2-y1

    testCntr, solutionCntr := 0, 0

    for i := -100; i < 100; i++ {
        for j := -100; j < 100; j++ {
            testCntr++
            s1x, s2x := x1+float64(i), x2+float64(i)
            s1y, s2y := y1+float64(j), y2+float64(j)

            sc1x, sc1y := s1x-cx, s1y-cy
            seg1Inside := sc1x*sc1x+sc1y*sc1y < r*r
            sc2x, sc2y := s2x-cx, s2y-cy
            seg2Inside := sc2x*sc2x+sc2y*sc2y < r*r

            p1x, p1y, p2x, p2y, intersects := SegmentCircleIntersection(s1x, s1y, s2x, s2y, cx, cy, r)

            if intersects {
                solutionCntr++
                //Check if points are on circle
                c1x, c1y := p1x-cx, p1y-cy
                deltaLen1 := (c1x*c1x + c1y*c1y) - r*r
                CheckEpsilon(t, deltaLen1, epsilon, "p1 not on circle")

                c2x, c2y := p2x-cx, p2y-cy
                deltaLen2 := (c2x*c2x + c2y*c2y) - r*r
                CheckEpsilon(t, deltaLen2, epsilon, "p2 not on circle")

                // Check if points are on the line through the line segment
                // "cross product" of vector from a segment point to the point
                // and the vector for the segment should be near zero
                vp1x, vp1y := p1x-s1x, p1y-s1y
                crossProd1 := vp1x*segy - vp1y*segx
                CheckEpsilon(t, crossProd1, epsilon, "p1 not on line ")

                vp2x, vp2y := p2x-s1x, p2y-s1y
                crossProd2 := vp2x*segy - vp2y*segx
                CheckEpsilon(t, crossProd2, epsilon, "p2 not on line ")

                // Check if point is between points s1 and s2 on line
                // This means the sign of the dot prod of the segment vector
                // and point to segment end point vectors are opposite for
                // either end.
                wp1x, wp1y := p1x-s2x, p1y-s2y
                dp1v := vp1x*segx + vp1y*segy
                dp1w := wp1x*segx + wp1y*segy
                if (dp1v < 0 && dp1w < 0) || (dp1v > 0 && dp1w > 0) {
                    t.Error("point not contained in segment ", dp1v, dp1w)
                    t.FailNow()
                }

                wp2x, wp2y := p2x-s2x, p2y-s2y
                dp2v := vp2x*segx + vp2y*segy
                dp2w := wp2x*segx + wp2y*segy
                if (dp2v < 0 && dp2w < 0) || (dp2v > 0 && dp2w > 0) {
                    t.Error("point not contained in segment ", dp2v, dp2w)
                    t.FailNow()
                }

                if s1x == s2x && s2y == s1y { //Only one solution
                    // Test that one end of the segment is withing the radius of the circle
                    // and one is not
                    if seg1Inside && seg2Inside {
                        t.Error("Only one solution but both line segment ends inside")
                        t.FailNow()
                    }
                    if !seg1Inside && !seg2Inside {
                        t.Error("Only one solution but both line segment ends outside")
                        t.FailNow()
                    }

                }
            } else { // No intersection, check if both points outside or inside
                if (seg1Inside && !seg2Inside) || (!seg1Inside && seg2Inside) {
                    t.Error("No solution but only one point in radius of circle")
                    t.FailNow()
                }
            }
        }
    }
    t.Log("Tested ", testCntr, " examples and found ", solutionCntr, " solutions.")
}

Đây là đầu ra của bài kiểm tra:

=== RUN   TestSegmentCircleIntersection
--- PASS: TestSegmentCircleIntersection (0.00s)
    geom_test.go:105: Tested  40000  examples and found  7343  solutions.

Cuối cùng, phương pháp có thể dễ dàng mở rộng đối với trường hợp tia bắt đầu tại một điểm, đi qua điểm kia và kéo dài đến vô tận, chỉ bằng cách kiểm tra nếu t> 0 hoặc t <1 chứ không phải cả hai.


0

Tôi chỉ cần điều đó, vì vậy tôi đã đưa ra giải pháp này. Ngôn ngữ là maxscript, nhưng nó nên dễ dàng dịch sang bất kỳ ngôn ngữ nào khác. sideA, sideB và CircleRadius là vô hướng, phần còn lại của các biến là các điểm như [x, y, z]. Tôi giả sử z = 0 để giải trên mặt phẳng XY

fn projectPoint p1 p2 p3 = --project  p1 perpendicular to the line p2-p3
(
    local v= normalize (p3-p2)
    local p= (p1-p2)
    p2+((dot v p)*v)
)
fn findIntersectionLineCircle CircleCenter CircleRadius LineP1 LineP2=
(
    pp=projectPoint CircleCenter LineP1 LineP2
    sideA=distance pp CircleCenter
    --use pythagoras to solve the third side
    sideB=sqrt(CircleRadius^2-sideA^2) -- this will return NaN if they don't intersect
    IntersectV=normalize (pp-CircleCenter)
    perpV=[IntersectV.y,-IntersectV.x,IntersectV.z]
    --project the point to both sides to find the solutions
    solution1=pp+(sideB*perpV)
    solution2=pp-(sideB*perpV)
    return #(solution1,solution2)
)

0

Giải pháp trong python, dựa trên @Joe Skeen

def check_line_segment_circle_intersection(line, point, radious):
    """ Checks whether a point intersects with a line defined by two points.

    A `point` is list with two values: [2, 3]

    A `line` is list with two points: [point1, point2]

    """
    line_distance = distance(line[0], line[1])
    distance_start_to_point = distance(line[0], point)
    distance_end_to_point = distance(line[1], point)

    if (distance_start_to_point <= radious or distance_end_to_point <= radious):
        return True

    # angle between line and point with law of cosines
    numerator = (math.pow(distance_start_to_point, 2)
                 + math.pow(line_distance, 2)
                 - math.pow(distance_end_to_point, 2))
    denominator = 2 * distance_start_to_point * line_distance
    ratio = numerator / denominator
    ratio = ratio if ratio <= 1 else 1  # To account for float errors
    ratio = ratio if ratio >= -1 else -1  # To account for float errors
    angle = math.acos(ratio)

    # distance from the point to the line with sin projection
    distance_line_to_point = math.sin(angle) * distance_start_to_point

    if distance_line_to_point <= radious:
        point_projection_in_line = math.cos(angle) * distance_start_to_point
        # Intersection occurs whent the point projection in the line is less
        # than the line distance and positive
        return point_projection_in_line <= line_distance and point_projection_in_line >= 0
    return False

def distance(point1, point2):
    return math.sqrt(
        math.pow(point1[1] - point2[1], 2) +
        math.pow(point1[0] - point2[0], 2)
    )

0
Function lineCircleCollision(p1,p2,c,r,precision){
Let dx = (p2.x-p1.x)/precision
Let dy = (p2.y-p1.y)/precision
Let collision=false
For(let i = 0;i<precision:i++){
If(Math.sqrt((p1.x+dx*i-c.x)**2+(p1.y+dy*i-c.y)**2).<r {
Collision=true
}
}

Bạn có thể lấy X điểm cách đều nhau từ đường kẻ và nếu có bất kỳ bên trong vòng tròn, có va chạm

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.