Làm thế nào để bạn phát hiện nơi hai đoạn đường giao nhau? [đóng cửa]


518

Làm cách nào để xác định xem hai đường thẳng có giao nhau hay không và nếu chúng thực hiện, tại điểm x, y nào?


Có thể giúp nghĩ về các cạnh của hình chữ nhật là các đường riêng biệt thay vì đa giác hoàn chỉnh.
Ryan Graham

Người điều hành lưu ý : thảo luận về việc bài đăng này có thuộc chủ đề hay không thuộc về Meta Stack Overflow Nhận xét thêm về điều này ở đây sẽ bị xóa.
Martijn Pieters

Câu trả lời:


659

Có một cách tiếp cận tốt cho vấn đề này sử dụng các sản phẩm chéo vector. Xác định sản phẩm vectơ 2 chiều v  ×  wv x  w y  -  v y  w x .

Giả sử hai đoạn đường chạy từ p đến p  +  r và từ q đến q  +  s . Sau đó, bất kỳ điểm nào trên dòng đầu tiên đều có thể biểu diễn dưới dạng p  +  t  r (đối với tham số vô hướng  t ) và bất kỳ điểm nào trên dòng thứ hai là q  +  u  s (đối với tham số vô hướng  u ).

Hai đoạn đường giao nhau

Hai đường thẳng giao nhau nếu chúng ta có thể tìm thấy tu sao cho:

p + t  r = q + u  s

Công thức cho điểm giao nhau

Băng qua hai bên với s , nhận

( p + t  r ) × s = ( q + u  s ) × s

Và vì s  ×  s = 0, điều này có nghĩa là

t  ( r × s ) = ( q - p ) × s

Và do đó, giải quyết cho t :

t = ( q - p ) × s / ( r × s )

Theo cùng một cách, chúng ta có thể giải quyết cho u :

( p + t  r ) × r = ( q + u  s ) × r

u  ( s × r ) = ( p - q ) × r

u = ( p - q ) × r / ( s × r )

Để giảm số bước tính toán, thật thuận tiện để viết lại bước này như sau (hãy nhớ rằng s  ×  r = -  r  ×  s ):

u = ( q - p ) × r / ( r × s )

Bây giờ có bốn trường hợp:

  1. Nếu r  ×  s  = 0 và ( q  -  p ) ×  r  = 0, thì hai dòng thẳng hàng.

    Trong trường hợp này, biểu thị các điểm cuối của đoạn thứ hai ( qq  +  s ) theo phương trình của đoạn thứ nhất ( p + t r ):

    t 0 = ( q - p ) ·  r / ( r  ·  r )

    t 1 = ( q + s - p ) ·  r / ( r  ·  r ) = t 0 + s  ·  r / ( r  ·  r )

    Nếu khoảng giữa t 0t 1 giao nhau với khoảng [0, 1] thì các đoạn đường thẳng là đường thẳng và chồng chéo; nếu không thì chúng là collinear và disjoint.

    Lưu ý rằng nếu sr trỏ theo hai hướng ngược nhau thì s  ·  r <0 và do đó, khoảng thời gian cần kiểm tra là [ t 1 , t 0 ] thay vì [ t 0 , t 1 ].

  2. Nếu r  ×  s  = 0 và ( q  -  p ) ×  r  0, thì hai đường thẳng song song và không cắt nhau.

  3. Nếu r  ×  s  ≠ 0 và 0 ≤  t  ≤ 1 và 0  u  ≤ 1, hai đoạn thẳng gặp nhau tại điểm p + t  r = q + u  s .

  4. Mặt khác, hai đoạn đường không song song nhưng không giao nhau.

Tín dụng: phương pháp này là chuyên môn hóa 2 chiều của thuật toán giao cắt đường 3D từ bài viết "Giao nhau của hai dòng trong ba không gian" của Ronald Goldman, được xuất bản trong Đồ họa đá quý , trang 304. Trong ba chiều, trường hợp thông thường là các đường thẳng bị lệch (không song song cũng không giao nhau) trong trường hợp phương thức này đưa ra các điểm tiếp cận gần nhất của hai đường.


5
@myrkos: Không. Đoạn đầu tiên chạy "từ p đến p + r" vì vậy khi nó được biểu thị theo thuật ngữ tham số là "p + tr" thì đoạn đó tương ứng với 0 ≤ t ≤ 1. Tương tự cho đoạn khác.
Gareth Rees

7
Gareth, tôi cảm thấy mình phải thiếu thứ gì đó, nhưng làm thế nào để bạn chia (một vectơ) cho một vectơ? Giải pháp của bạn cho tu kết thúc với / (r × s), nhưng (r × s)là một vector, phải không? Một vectơ (0, 0, rx * sy - ry * sx). Và phía bên trái tương tự như một vectơ song song với trục z. Vậy ... tôi chỉ chia thành phần z cho thành phần z khác phải không? Là công thức cho t thực sự |(q − p) × s| / |(r × s)|?
LarsH

7
@LarsH: xem đoạn đầu tiên.
Gareth Rees

35
Đối với những người quan tâm, đây là cách triển khai C # đơn giản, lấy tọa độ bắt đầu và kết thúc của PointF cho các dòng, dường như đang hoạt động: ideone.com/PnPJgb
Matt

24
Tôi kết hợp triển khai JavaScript sau @Matt. Tôi đã sửa chữa các lỗi được chỉ ra bởi Tekito.
pgkelley

230

FWIW, chức năng sau (trong C) đều phát hiện các giao điểm đường và xác định điểm giao nhau. Nó dựa trên một thuật toán trong " Thủ thuật lập trình trò chơi Windows " của Andre LeMothe . Nó không giống với một số thuật toán trong các câu trả lời khác (ví dụ như của Gareth). LeMothe sau đó sử dụng Quy tắc của Cramer (đừng hỏi tôi) để tự giải các phương trình.

Tôi có thể chứng thực rằng nó hoạt động trong bản sao tiểu hành tinh yếu của tôi, và dường như xử lý chính xác các trường hợp cạnh được mô tả trong các câu trả lời khác của Elemental, Dan và Wodzu. Nó cũng có thể nhanh hơn mã được đăng bởi KingNestor vì tất cả đều nhân và chia, không có căn bậc hai!

Tôi đoán có một số tiềm năng để chia cho số 0 trong đó, mặc dù đó không phải là vấn đề trong trường hợp của tôi. Đủ dễ dàng để sửa đổi để tránh sự cố

// Returns 1 if the lines intersect, otherwise 0. In addition, if the lines 
// intersect the intersection point may be stored in the floats i_x and i_y.
char get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, 
    float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
    float s1_x, s1_y, s2_x, s2_y;
    s1_x = p1_x - p0_x;     s1_y = p1_y - p0_y;
    s2_x = p3_x - p2_x;     s2_y = p3_y - p2_y;

    float s, t;
    s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);

    if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
    {
        // Collision detected
        if (i_x != NULL)
            *i_x = p0_x + (t * s1_x);
        if (i_y != NULL)
            *i_y = p0_y + (t * s1_y);
        return 1;
    }

    return 0; // No collision
}

BTW, tôi phải nói rằng trong cuốn sách của LeMothe, mặc dù anh ta rõ ràng đã hiểu đúng thuật toán, ví dụ cụ thể mà anh ta cho thấy đã cắm sai số và tính toán sai. Ví dụ:

(4 * (4 - 1) + 12 * (7 - 1)) / (17 * 4 + 12 * 10)

= 844 / 0,88

= 0,44

Điều đó làm tôi bối rối hàng giờ . :


9
hàm getLineIntersection (p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) {var s1_x, s1_y, s2_x, s2_y; s1_x = p1_x - p0_x; s1_y = p1_y - p0_y; s2_x = p3_x - p2_x; s2_y = p3_y - p2_y; var s, t; s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y); t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
cortijon

5
if (s> = 0 && s <= 1 && t> = 0 && t <= 1) {// Phát hiện va chạm var intX = p0_x + (t * s1_x); var intY = p0_y + (t * s1_y); trở lại [intX, intY]; } trả về null; // Không va chạm}
cortijon

13
thuật toán tốt, tuy nhiên fyi nó không xử lý các trường hợp có định thức là 0. (-s2_x * s1_y + s1_x * s2_y ở trên). Nếu là 0 (hoặc gần 0), các đường thẳng song song hoặc thẳng hàng. Nếu nó thẳng hàng thì giao lộ có thể là một đoạn đường khác.
seand

16
Hai hoạt động phân chia có thể tránh được cho tốc độ (chi phí phân chia nhiều hơn nhân); nếu các đường giao nhau bạn cần một bộ phận, nếu chúng không giao nhau, bạn cần số không. Trước tiên, người ta phải tính toán mẫu số và dừng sớm nếu nó bằng 0 (có thể thêm mã để phát hiện colinearity.) Tiếp theo, thay vì tính toán sttrực tiếp, hãy kiểm tra mối quan hệ giữa hai tử số và mẫu số. Chỉ khi các dòng được xác nhận giao nhau, bạn thực sự cần phải tính giá trị của t(nhưng không s).
Qwertie

18
Tôi đã thực hiện kiểm tra hiệu suất trên tất cả các thuật toán được đăng ở đây, và thuật toán này nhanh hơn ít nhất gấp đôi so với các thuật toán khác. Cảm ơn vì đăng!
lajos

63

Vấn đề giảm cho câu hỏi này: Hai đường thẳng từ A đến B và từ C đến D có giao nhau không? Sau đó, bạn có thể hỏi nó bốn lần (giữa dòng và mỗi bốn cạnh của hình chữ nhật).

Đây là toán học vectơ để làm điều đó. Tôi giả sử dòng từ A đến B là dòng trong câu hỏi và dòng từ C đến D là một trong những dòng hình chữ nhật. Ký hiệu của tôi là Ax"tọa độ x của A" và Cylà "tọa độ y của C." Và " *" có nghĩa là sản phẩm chấm, vì vậy, ví dụ A*B = Ax*Bx + Ay*By.

E = B-A = ( Bx-Ax, By-Ay )
F = D-C = ( Dx-Cx, Dy-Cy ) 
P = ( -Ey, Ex )
h = ( (A-C) * P ) / ( F * P )

Con hsố này là chìa khóa. Nếu hnằm giữa 01, các đường giao nhau, nếu không thì không. Nếu F*Pbằng 0, tất nhiên bạn không thể thực hiện phép tính, nhưng trong trường hợp này, các đường thẳng song song và do đó chỉ giao nhau trong các trường hợp rõ ràng.

Điểm chính xác của giao lộ là C + F*h.

Vui hơn:

Nếu hchính xác 0 hoặc 1các dòng chạm vào điểm cuối. Bạn có thể coi đây là "giao lộ" hay không khi bạn thấy phù hợp.

Cụ thể, hlà bạn phải nhân bao nhiêu chiều dài của dòng để chạm chính xác vào dòng khác.

Do đó, If h<0, điều đó có nghĩa là đường hình chữ nhật nằm "phía sau" đường đã cho (với "hướng" là "từ A đến B") và nếu h>1đường hình chữ nhật là "phía trước" của đường đã cho.

Đạo hàm:

A và C là các vectơ chỉ đến điểm bắt đầu của dòng; E và F là các vectơ từ hai đầu A và C tạo thành đường thẳng.

Đối với hai đường thẳng không song song trong mặt phẳng, phải có chính xác một cặp vô hướng ghphương trình này giữ:

A + E*g = C + F*h

Tại sao? Bởi vì hai đường không song song phải giao nhau, có nghĩa là bạn có thể chia tỷ lệ cả hai dòng theo một số lượng và chạm vào nhau.

( Lúc đầu, nó trông giống như một phương trình đơn với hai ẩn số! Nhưng không phải khi bạn nghĩ rằng đây là phương trình vectơ 2D, có nghĩa đây thực sự là một cặp phương trình trong xy.)

Chúng ta phải loại bỏ một trong những biến này. Một cách dễ dàng là làm cho Esố không. Để làm điều đó, hãy lấy sản phẩm chấm của cả hai mặt của phương trình bằng cách sử dụng một vectơ sẽ chấm về 0 bằng E. Vectơ mà tôi đã gọi Pở trên và tôi đã thực hiện chuyển đổi rõ ràng của E.

Bây giờ bạn có:

A*P = C*P + F*P*h
(A-C)*P = (F*P)*h
( (A-C)*P ) / (F*P) = h

29
Thuật toán này là tốt đẹp. Nhưng có một lỗ hổng trong đó như được chỉ ra bởi Dan @ stackoverflow.com/questions/563198/ Khăn & Elemental @ stackoverflow.com/questions/563198/. Sẽ thật tuyệt nếu bạn cập nhật câu trả lời của mình để tham khảo trong tương lai. Cảm ơn.
Chantz

2
Thuật toán này có ổn định về số không? Tôi đã thử một chiếc similliar aproach và hóa ra nó cho kết quả kỳ lạ khi làm việc trên phao.
milosz

3
Dường như có một vấn đề khác với thuật toán này. Khi nó cho các điểm A = {1, 0} B = {2, 0} C = {0, 0} D = {1,0}, mặc dù các đoạn thẳng chạm rõ ràng ở cuối, F P (và cả E Q, phù hợp với cách khắc phục của người dùng bên dưới) đều bằng 0, do đó, chia cho 0 để tìm h và g. Vẫn đang nghiên cứu giải pháp cho vấn đề này, nhưng tôi nghĩ vấn đề này đáng để chỉ ra.
kẹo 27/211

12
Câu trả lời này đơn giản là không chính xác. Hãy thử A = {0,0}, B = {0,1}, C = {0,2} D = {2,0}
Tim Cooper

6
A + E*g = C + F*hHai đường thẳng giao nhau khi và chỉ khi giải pháp cho phương trình đó (giả sử chúng không song song) có cả hai gh giữa 0 và 1 (trong hoặc độc quyền, tùy thuộc vào việc bạn đếm chạm vào điểm cuối).
Daniel Fischer

46

Tôi đã cố gắng thực hiện thuật toán để Jason mô tả một cách tao nhã ở trên; Thật không may trong khi làm việc mặc dù toán học trong gỡ lỗi tôi đã tìm thấy nhiều trường hợp mà nó không hoạt động.

Ví dụ, hãy xem xét các điểm A (10,10) B (20,20) C (10,1) D (1,10) cho h = 0,5 và tuy nhiên rõ ràng bằng cách kiểm tra rằng các phân đoạn này không ở gần nhau khác

Vẽ đồ thị này cho thấy rõ rằng 0 <h <1 tiêu chí chỉ cho biết rằng điểm đánh chặn sẽ nằm trên CD nếu nó tồn tại nhưng không cho biết gì về việc điểm đó có nằm trên AB hay không. Để đảm bảo có điểm giao nhau, bạn phải thực hiện phép tính đối xứng cho biến g và yêu cầu đánh chặn là: 0 <g <1 VÀ 0 <h <1


2
Tôi đã nhổ tóc ra để tìm hiểu tại sao câu trả lời được chấp nhận không hiệu quả với tôi. Cám ơn rất nhiều!
Matt Bridges

1
Cũng đáng chú ý là các điều kiện biên hoạt động trong trường hợp này (ví dụ: h = 0 hoặc h = 1 hoặc g = 0 hoặc g = 1 các dòng 'chỉ' chạm
tố

Đối với những người gặp khó khăn khi hình dung kết quả, tôi đã thực hiện điều này trong Javascript: jsfiddle.net/ferrybig/eokwL9mp
Ferrybig

45

Đây là một cải tiến cho câu trả lời của Gavin. giải pháp của marcp cũng tương tự, nhưng không hoãn phân chia.

Đây thực sự cũng là một ứng dụng thực tế cho câu trả lời của Gareth Rees, bởi vì sản phẩm chéo tương đương trong 2D là sản phẩm perp-dot-sản phẩm, đây là thứ mà mã này sử dụng ba trong số đó. Chuyển sang 3D và sử dụng sản phẩm chéo, nội suy cả s và t ở cuối, dẫn đến hai điểm gần nhất giữa các dòng trong 3D. Dù sao, giải pháp 2D:

int get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, 
    float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
    float s02_x, s02_y, s10_x, s10_y, s32_x, s32_y, s_numer, t_numer, denom, t;
    s10_x = p1_x - p0_x;
    s10_y = p1_y - p0_y;
    s32_x = p3_x - p2_x;
    s32_y = p3_y - p2_y;

    denom = s10_x * s32_y - s32_x * s10_y;
    if (denom == 0)
        return 0; // Collinear
    bool denomPositive = denom > 0;

    s02_x = p0_x - p2_x;
    s02_y = p0_y - p2_y;
    s_numer = s10_x * s02_y - s10_y * s02_x;
    if ((s_numer < 0) == denomPositive)
        return 0; // No collision

    t_numer = s32_x * s02_y - s32_y * s02_x;
    if ((t_numer < 0) == denomPositive)
        return 0; // No collision

    if (((s_numer > denom) == denomPositive) || ((t_numer > denom) == denomPositive))
        return 0; // No collision
    // Collision detected
    t = t_numer / denom;
    if (i_x != NULL)
        *i_x = p0_x + (t * s10_x);
    if (i_y != NULL)
        *i_y = p0_y + (t * s10_y);

    return 1;
}

Về cơ bản, nó hoãn việc phân chia cho đến giây cuối cùng và di chuyển hầu hết các bài kiểm tra cho đến trước khi các tính toán nhất định được thực hiện, do đó thêm vào sớm. Cuối cùng, nó cũng tránh sự phân chia theo trường hợp 0 ​​xảy ra khi các đường thẳng song song.

Bạn cũng có thể muốn xem xét sử dụng thử nghiệm epsilon thay vì so sánh với không. Các đường cực gần song song có thể tạo ra kết quả hơi bị tắt. Đây không phải là một lỗi, nó là một hạn chế với toán học dấu phẩy động.


1
Thất bại nếu một số điểm có giá trị 0 .. điều đó không xảy ra phải không?
hfossli

1
Tôi đã sửa lỗi cho lỗi giới thiệu khi hoãn phân chia. t có thể dương khi số và mệnh giá đều âm.
iMalc

2
Không hoạt động nếu p0-p1 thẳng đứng và p2-p3 nằm ngang và hai đoạn giao nhau. (lần trở lại đầu tiên được thực hiện)
Fabio Dalla Libera

Trường hợp coolinear có hai possibilites: không onverlaps và chồng chéo. Shoul đầu tiên trả lại sai thứ hai đúng. Trong mã của bạn, điều này không được kiểm tra. nó luôn trả về sai như hầu hết các câu trả lời ở đây. Thật xấu hổ khi không có giải pháp nào thực sự có hiệu quả.
AlexWien

3
Bạn có thể khai sáng cho tôi tại sao tất cả những thứ này sử dụng các tên biến mơ hồ như vậy s32_ythay vì một cái gì đó mô tả nó là như thế point2YDifferencenào?
Supuhstar

40

Câu hỏi C: Làm thế nào để bạn phát hiện xem hai đoạn đường có giao nhau hay không?

Tôi đã tìm kiếm cùng một chủ đề, và tôi không hài lòng với câu trả lời. Vì vậy, tôi đã viết một bài viết giải thích rất chi tiết về cách kiểm tra xem hai đoạn đườnggiao nhau với nhiều hình ảnh hay không. Có mã Java hoàn chỉnh (và được thử nghiệm).

Đây là bài viết, được cắt thành các phần quan trọng nhất:

Thuật toán kiểm tra xem phân đoạn dòng a có giao với phân đoạn b không, trông giống như sau:

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

Các hộp giới hạn là gì? Đây là hai hộp giới hạn của hai phân đoạn dòng:

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

Nếu cả hai hộp giới hạn có một giao điểm, bạn di chuyển đoạn thẳng a để một điểm nằm tại (0 | 0). Bây giờ bạn có một dòng thông qua nguồn gốc được xác định bởi a. Bây giờ di chuyển đoạn đường b theo cùng một cách và kiểm tra xem các điểm mới của đoạn đường b có ở các cạnh khác nhau của đường a không. Nếu đây là trường hợp, kiểm tra nó theo cách khác xung quanh. Nếu đây cũng là trường hợp, các phân đoạn dòng giao nhau. Nếu không, họ không giao nhau.

Câu A: Trường hợp hai đoạn thẳng cắt nhau?

Bạn biết rằng hai đoạn thẳng a và b cắt nhau. Nếu bạn không biết điều đó, hãy kiểm tra nó bằng các công cụ tôi đã đưa cho bạn trong "Câu hỏi C".

Bây giờ bạn có thể trải qua một số trường hợp và có được giải pháp với toán lớp 7 (xem mã và ví dụ tương tác ).

Câu hỏi B: Làm thế nào để bạn phát hiện xem hai đường thẳng có giao nhau hay không?

Hãy nói rằng quan điểm của bạn A = (x1, y1), điểm B = (x2, y2), C = (x_3, y_3), D = (x_4, y_4). Dòng đầu tiên của bạn được xác định bởi AB (với A! = B) và dòng thứ hai của bạn bằng CD (với C! = D).

function doLinesIntersect(AB, CD) {
    if (x1 == x2) {
        return !(x3 == x4 && x1 != x3);
    } else if (x3 == x4) {
        return true;
    } else {
        // Both lines are not parallel to the y-axis
        m1 = (y1-y2)/(x1-x2);
        m2 = (y3-y4)/(x3-x4);
        return m1 != m2;
    }
}

Câu D: Hai đường thẳng giao nhau ở đâu?

Kiểm tra với Câu hỏi B nếu chúng giao nhau.

Các dòng a và b được xác định bởi hai điểm cho mỗi dòng. Về cơ bản, bạn có thể áp dụng logic tương tự đã được sử dụng trong Câu hỏi A.


15
Để rõ ràng, Câu hỏi B trong câu trả lời này thực sự là về hai dòng giao nhau, không phải là các phân đoạn dòng. Tôi không phàn nàn; nó không sai Chỉ không muốn bất cứ ai bị lừa.
phord

1
Không có "câu hỏi C". Và Câu hỏi D chỉ bị trả về Câu hỏi A.
Konrad Viltersten

21

Câu trả lời một khi được chấp nhận ở đây là không chính xác (từ đó đã không được chấp nhận, vì vậy rất vui!). Nó không loại bỏ chính xác tất cả các giao lộ. Về mặt quan trọng, nó có thể hoạt động nhưng có thể thất bại, đặc biệt trong trường hợp 0 ​​và 1 được coi là hợp lệ cho h.

Hãy xem xét trường hợp sau:

Các dòng tại (4,1) - (5,1) và (0,0) - (0,2)

Đây là những đường vuông góc rõ ràng không trùng nhau.

A = (4,1)
B = (5,1)
C = (0,0)
D = (0,2)
E = (5,1) - (4,1) = (- 1,0)
F = (0,2) - (0,0) = (0, -2)
P = (0,1)
h = ((4,1) - (0,0)) chấm (0,1) / ((0) , -2) chấm (0,1)) = 0

Theo câu trả lời trên, hai đoạn đường này gặp nhau tại điểm cuối (giá trị 0 và 1). Điểm cuối đó sẽ là:

(0,0) + (0, -2) * 0 = (0,0)

Vì vậy, rõ ràng hai phân đoạn dòng gặp nhau tại (0,0), nằm trên dòng CD, nhưng không phải trên dòng AB. Vậy điều gì đang xảy ra? Câu trả lời là các giá trị 0 và 1 không hợp lệ và đôi khi chỉ HAPPEN để dự đoán chính xác giao điểm cuối. Khi phần mở rộng của một dòng (nhưng không phải là dòng khác) sẽ đáp ứng phân đoạn dòng, thuật toán dự đoán một giao điểm của các phân đoạn dòng, nhưng điều này không chính xác. Tôi tưởng tượng rằng bằng cách thử nghiệm bắt đầu với AB vs CD và sau đó thử nghiệm với CD vs AB, vấn đề này sẽ được loại bỏ. Chỉ khi cả hai rơi từ 0 đến 1, họ mới có thể nói là giao nhau.

Tôi khuyên bạn nên sử dụng phương pháp sản phẩm chéo vector nếu bạn phải dự đoán điểm cuối.

-Dan


4
Câu trả lời "được chấp nhận" có thể thay đổi, vì vậy bạn nên gọi nó là một cái gì đó khác. (Trên thực tế, tôi nghĩ rằng nó đã thay đổi kể từ nhận xét của bạn)
Johannes Hoff

14

Phiên bản Python của câu trả lời của iMalc:

def find_intersection( p0, p1, p2, p3 ) :

    s10_x = p1[0] - p0[0]
    s10_y = p1[1] - p0[1]
    s32_x = p3[0] - p2[0]
    s32_y = p3[1] - p2[1]

    denom = s10_x * s32_y - s32_x * s10_y

    if denom == 0 : return None # collinear

    denom_is_positive = denom > 0

    s02_x = p0[0] - p2[0]
    s02_y = p0[1] - p2[1]

    s_numer = s10_x * s02_y - s10_y * s02_x

    if (s_numer < 0) == denom_is_positive : return None # no collision

    t_numer = s32_x * s02_y - s32_y * s02_x

    if (t_numer < 0) == denom_is_positive : return None # no collision

    if (s_numer > denom) == denom_is_positive or (t_numer > denom) == denom_is_positive : return None # no collision


    # collision detected

    t = t_numer / denom

    intersection_point = [ p0[0] + (t * s10_x), p0[1] + (t * s10_y) ]


    return intersection_point

Hãy nhớ rằng bạn cần làm cho số của mình nổi hoặc thay đổi dòng 8 để sử dụngdenom = float(...)
Jonno_FTW

11

Tìm giao điểm chính xác của hai đoạn đường là một nhiệm vụ không hề nhỏ với nhiều trường hợp cạnh. Đây là một giải pháp được làm tài liệu, làm việc và thử nghiệm tốt trong Java.

Về bản chất, có ba điều có thể xảy ra khi tìm giao điểm của hai đoạn đường:

  1. Các đoạn không giao nhau

  2. Có một điểm giao nhau duy nhất

  3. Giao lộ là một đoạn khác

LƯU Ý : Trong mã, tôi giả sử rằng một phân đoạn dòng (x1, y1), (x2, y2) với x1 = x2 và y1 = y2 là một phân đoạn dòng hợp lệ. Về mặt toán học, một phân đoạn dòng bao gồm các điểm riêng biệt, nhưng tôi cho phép các phân đoạn trở thành các điểm trong triển khai này để hoàn thiện.

Mã được lấy từ repo github của tôi

/**
 * This snippet finds the intersection of two line segments.
 * The intersection may either be empty, a single point or the
 * intersection is a subsegment there's an overlap.
 */

import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;

import java.util.ArrayList;
import java.util.List;

public class LineSegmentLineSegmentIntersection {

  // Small epsilon used for double value comparison.
  private static final double EPS = 1e-5;

  // 2D Point class.
  public static class Pt {
    double x, y;
    public Pt(double x, double y) {
      this.x = x; 
      this.y = y;
    }
    public boolean equals(Pt pt) {
      return abs(x - pt.x) < EPS && abs(y - pt.y) < EPS;
    }
  }

  // Finds the orientation of point 'c' relative to the line segment (a, b)
  // Returns  0 if all three points are collinear.
  // Returns -1 if 'c' is clockwise to segment (a, b), i.e right of line formed by the segment.
  // Returns +1 if 'c' is counter clockwise to segment (a, b), i.e left of line
  // formed by the segment.
  public static int orientation(Pt a, Pt b, Pt c) {
    double value = (b.y - a.y) * (c.x - b.x) - 
                   (b.x - a.x) * (c.y - b.y);
    if (abs(value) < EPS) return 0;
    return (value > 0) ? -1 : +1;
  }

  // Tests whether point 'c' is on the line segment (a, b).
  // Ensure first that point c is collinear to segment (a, b) and
  // then check whether c is within the rectangle formed by (a, b)
  public static boolean pointOnLine(Pt a, Pt b, Pt c) {
    return orientation(a, b, c) == 0 && 
           min(a.x, b.x) <= c.x && c.x <= max(a.x, b.x) && 
           min(a.y, b.y) <= c.y && c.y <= max(a.y, b.y);
  }

  // Determines whether two segments intersect.
  public static boolean segmentsIntersect(Pt p1, Pt p2, Pt p3, Pt p4) {

    // Get the orientation of points p3 and p4 in relation
    // to the line segment (p1, p2)
    int o1 = orientation(p1, p2, p3);
    int o2 = orientation(p1, p2, p4);
    int o3 = orientation(p3, p4, p1);
    int o4 = orientation(p3, p4, p2);

    // If the points p1, p2 are on opposite sides of the infinite
    // line formed by (p3, p4) and conversly p3, p4 are on opposite
    // sides of the infinite line formed by (p1, p2) then there is
    // an intersection.
    if (o1 != o2 && o3 != o4) return true;

    // Collinear special cases (perhaps these if checks can be simplified?)
    if (o1 == 0 && pointOnLine(p1, p2, p3)) return true;
    if (o2 == 0 && pointOnLine(p1, p2, p4)) return true;
    if (o3 == 0 && pointOnLine(p3, p4, p1)) return true;
    if (o4 == 0 && pointOnLine(p3, p4, p2)) return true;

    return false;
  }

  public static List<Pt> getCommonEndpoints(Pt p1, Pt p2, Pt p3, Pt p4) {

    List<Pt> points = new ArrayList<>();

    if (p1.equals(p3)) {
      points.add(p1);
      if (p2.equals(p4)) points.add(p2);

    } else if (p1.equals(p4)) {
      points.add(p1);
      if (p2.equals(p3)) points.add(p2);

    } else if (p2.equals(p3)) {
      points.add(p2);
      if (p1.equals(p4)) points.add(p1);

    } else if (p2.equals(p4)) {
      points.add(p2);
      if (p1.equals(p3)) points.add(p1);
    }

    return points;
  }

  // Finds the intersection point(s) of two line segments. Unlike regular line 
  // segments, segments which are points (x1 = x2 and y1 = y2) are allowed.
  public static Pt[] lineSegmentLineSegmentIntersection(Pt p1, Pt p2, Pt p3, Pt p4) {

    // No intersection.
    if (!segmentsIntersect(p1, p2, p3, p4)) return new Pt[]{};

    // Both segments are a single point.
    if (p1.equals(p2) && p2.equals(p3) && p3.equals(p4))
      return new Pt[]{p1};

    List<Pt> endpoints = getCommonEndpoints(p1, p2, p3, p4);
    int n = endpoints.size();

    // One of the line segments is an intersecting single point.
    // NOTE: checking only n == 1 is insufficient to return early
    // because the solution might be a sub segment.
    boolean singleton = p1.equals(p2) || p3.equals(p4);
    if (n == 1 && singleton) return new Pt[]{endpoints.get(0)};

    // Segments are equal.
    if (n == 2) return new Pt[]{endpoints.get(0), endpoints.get(1)};

    boolean collinearSegments = (orientation(p1, p2, p3) == 0) && 
                                (orientation(p1, p2, p4) == 0);

    // The intersection will be a sub-segment of the two
    // segments since they overlap each other.
    if (collinearSegments) {

      // Segment #2 is enclosed in segment #1
      if (pointOnLine(p1, p2, p3) && pointOnLine(p1, p2, p4))
        return new Pt[]{p3, p4};

      // Segment #1 is enclosed in segment #2
      if (pointOnLine(p3, p4, p1) && pointOnLine(p3, p4, p2))
        return new Pt[]{p1, p2};

      // The subsegment is part of segment #1 and part of segment #2.
      // Find the middle points which correspond to this segment.
      Pt midPoint1 = pointOnLine(p1, p2, p3) ? p3 : p4;
      Pt midPoint2 = pointOnLine(p3, p4, p1) ? p1 : p2;

      // There is actually only one middle point!
      if (midPoint1.equals(midPoint2)) return new Pt[]{midPoint1};

      return new Pt[]{midPoint1, midPoint2};
    }

    /* Beyond this point there is a unique intersection point. */

    // Segment #1 is a vertical line.
    if (abs(p1.x - p2.x) < EPS) {
      double m = (p4.y - p3.y) / (p4.x - p3.x);
      double b = p3.y - m * p3.x;
      return new Pt[]{new Pt(p1.x, m * p1.x + b)};
    }

    // Segment #2 is a vertical line.
    if (abs(p3.x - p4.x) < EPS) {
      double m = (p2.y - p1.y) / (p2.x - p1.x);
      double b = p1.y - m * p1.x;
      return new Pt[]{new Pt(p3.x, m * p3.x + b)};
    }

    double m1 = (p2.y - p1.y) / (p2.x - p1.x);
    double m2 = (p4.y - p3.y) / (p4.x - p3.x);
    double b1 = p1.y - m1 * p1.x;
    double b2 = p3.y - m2 * p3.x;
    double x = (b2 - b1) / (m1 - m2);
    double y = (m1 * b2 - m2 * b1) / (m1 - m2);

    return new Pt[]{new Pt(x, y)};
  }

}

Dưới đây là một ví dụ sử dụng đơn giản:

  public static void main(String[] args) {

    // Segment #1 is (p1, p2), segment #2 is (p3, p4)
    Pt p1, p2, p3, p4;

    p1 = new Pt(-2, 4); p2 = new Pt(3, 3);
    p3 = new Pt(0, 0);  p4 = new Pt(2, 4);
    Pt[] points = lineSegmentLineSegmentIntersection(p1, p2, p3, p4);
    Pt point = points[0];

    // Prints: (1.636, 3.273)
    System.out.printf("(%.3f, %.3f)\n", point.x, point.y);

    p1 = new Pt(-10, 0); p2 = new Pt(+10, 0);
    p3 = new Pt(-5, 0);  p4 = new Pt(+5, 0);
    points = lineSegmentLineSegmentIntersection(p1, p2, p3, p4);
    Pt point1 = points[0], point2 = points[1];

    // Prints: (-5.000, 0.000) (5.000, 0.000)
    System.out.printf("(%.3f, %.3f) (%.3f, %.3f)\n", point1.x, point1.y, point2.x, point2.y);
  }

nó làm việc cho hệ thống tọa độ địa lý của tôi! cảm ơn! Nhưng nó là cho giao điểm đường vô hạn, và tôi đang tìm kiếm giao điểm hữu hạn hơn.
M. Usman Khan

8

Chỉ muốn đề cập rằng một lời giải thích tốt và giải pháp rõ ràng có thể được tìm thấy trong loạt Công thức số. Tôi đã có phiên bản thứ 3 và câu trả lời nằm ở trang 1117, phần 21.4. Một giải pháp khác với một danh pháp khác nhau có thể được tìm thấy trong một bài báo của Thử nghiệm giao thoa đường dây đáng tin cậy của Marina Gavrilova . Giải pháp của cô ấy, theo tôi, đơn giản hơn một chút.

Tôi thực hiện dưới đây:

bool NuGeometry::IsBetween(const double& x0, const double& x, const double& x1){
   return (x >= x0) && (x <= x1);
}

bool NuGeometry::FindIntersection(const double& x0, const double& y0, 
     const double& x1, const double& y1,
     const double& a0, const double& b0, 
     const double& a1, const double& b1, 
     double& xy, double& ab) {
   // four endpoints are x0, y0 & x1,y1 & a0,b0 & a1,b1
   // returned values xy and ab are the fractional distance along xy and ab
   // and are only defined when the result is true

   bool partial = false;
   double denom = (b0 - b1) * (x0 - x1) - (y0 - y1) * (a0 - a1);
   if (denom == 0) {
      xy = -1;
      ab = -1;
   } else {
      xy = (a0 * (y1 - b1) + a1 * (b0 - y1) + x1 * (b1 - b0)) / denom;
      partial = NuGeometry::IsBetween(0, xy, 1);
      if (partial) {
         // no point calculating this unless xy is between 0 & 1
         ab = (y1 * (x0 - a1) + b1 * (x1 - x0) + y0 * (a1 - x1)) / denom; 
      }
   }
   if ( partial && NuGeometry::IsBetween(0, ab, 1)) {
      ab = 1-ab;
      xy = 1-xy;
      return true;
   }  else return false;
}

Không hoạt động cho p1 = (0,0), p2 = (10.0), p3 = (9.0), p4 = (20,0)
padmalcom

Tôi phụ thuộc vào định nghĩa của bạn về "không hoạt động" tôi đoán. Denom là 0 nên nó sẽ trả về false có vẻ đúng với tôi khi chúng không giao nhau. Colinear không giống như giao nhau.
marcp

8

Rất nhiều giải pháp có sẵn ở trên, nhưng tôi nghĩ giải pháp dưới đây khá đơn giản và dễ hiểu.

Hai đoạn Vector AB và Vector CD cắt nhau khi và chỉ khi

  1. Điểm cuối a và b nằm ở hai phía đối diện của CD phân đoạn.
  2. Điểm cuối c và d nằm đối diện với đoạn AB.

Cụ thể hơn a và b nằm đối diện với CD phân đoạn khi và chỉ khi chính xác một trong hai bộ ba a, c, d và b, c, d theo thứ tự ngược chiều kim đồng hồ.

Intersect(a, b, c, d)
 if CCW(a, c, d) == CCW(b, c, d)
    return false;
 else if CCW(a, b, c) == CCW(a, b, d)
    return false;
 else
    return true;

Ở đây CCW đại diện ngược chiều kim đồng hồ trả về đúng / sai dựa trên hướng của các điểm.

Nguồn: http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf Trang 2


2
Tôi nghĩ bạn nên cụ thể hơn một chút: CCWbài kiểm tra được xác định như thế nào? Với dấu hiệu của sản phẩm bên ngoài?
ocramz

Cảm ơn; mã giả này cho phép thực hiện rất đơn giản trong Scratch; xem dự án này: scratch.mit.edu/projects/129319027
Ruud Helderman 6/11/2016

8

C và Mục tiêu-C

Dựa trên câu trả lời của Gareth Rees

const AGKLine AGKLineZero = (AGKLine){(CGPoint){0.0, 0.0}, (CGPoint){0.0, 0.0}};

AGKLine AGKLineMake(CGPoint start, CGPoint end)
{
    return (AGKLine){start, end};
}

double AGKLineLength(AGKLine l)
{
    return CGPointLengthBetween_AGK(l.start, l.end);
}

BOOL AGKLineIntersection(AGKLine l1, AGKLine l2, CGPoint *out_pointOfIntersection)
{
    // http://stackoverflow.com/a/565282/202451

    CGPoint p = l1.start;
    CGPoint q = l2.start;
    CGPoint r = CGPointSubtract_AGK(l1.end, l1.start);
    CGPoint s = CGPointSubtract_AGK(l2.end, l2.start);

    double s_r_crossProduct = CGPointCrossProductZComponent_AGK(r, s);
    double t = CGPointCrossProductZComponent_AGK(CGPointSubtract_AGK(q, p), s) / s_r_crossProduct;
    double u = CGPointCrossProductZComponent_AGK(CGPointSubtract_AGK(q, p), r) / s_r_crossProduct;

    if(t < 0 || t > 1.0 || u < 0 || u > 1.0)
    {
        if(out_pointOfIntersection != NULL)
        {
            *out_pointOfIntersection = CGPointZero;
        }
        return NO;
    }
    else
    {
        if(out_pointOfIntersection != NULL)
        {
            CGPoint i = CGPointAdd_AGK(p, CGPointMultiply_AGK(r, t));
            *out_pointOfIntersection = i;
        }
        return YES;
    }
}

CGFloat CGPointCrossProductZComponent_AGK(CGPoint v1, CGPoint v2)
{
    return v1.x * v2.y - v1.y * v2.x;
}

CGPoint CGPointSubtract_AGK(CGPoint p1, CGPoint p2)
{
    return (CGPoint){p1.x - p2.x, p1.y - p2.y};
}

CGPoint CGPointAdd_AGK(CGPoint p1, CGPoint p2)
{
    return (CGPoint){p1.x + p2.x, p1.y + p2.y};
}

CGFloat CGPointCrossProductZComponent_AGK(CGPoint v1, CGPoint v2)
{
    return v1.x * v2.y - v1.y * v2.x;
}

CGPoint CGPointMultiply_AGK(CGPoint p1, CGFloat factor)
{
    return (CGPoint){p1.x * factor, p1.y * factor};
}

Nhiều chức năng và cấu trúc là riêng tư, nhưng bạn sẽ khá dễ dàng có thể biết chuyện gì đang xảy ra. Điều này được công khai trên repo này https://github.com/hfossli/AGGeometryKit/


AGPointZero đến từ đâu trong mã này?
seanicus

1
@seanicus đã cập nhật ví dụ để sử dụng CGPoint thay vào đó
hfossli

6

Điều này đang làm việc tốt cho tôi. Lấy từ đây .

 // calculates intersection and checks for parallel lines.  
 // also checks that the intersection point is actually on  
 // the line segment p1-p2  
 Point findIntersection(Point p1,Point p2,  
   Point p3,Point p4) {  
   float xD1,yD1,xD2,yD2,xD3,yD3;  
   float dot,deg,len1,len2;  
   float segmentLen1,segmentLen2;  
   float ua,ub,div;  

   // calculate differences  
   xD1=p2.x-p1.x;  
   xD2=p4.x-p3.x;  
   yD1=p2.y-p1.y;  
   yD2=p4.y-p3.y;  
   xD3=p1.x-p3.x;  
   yD3=p1.y-p3.y;    

   // calculate the lengths of the two lines  
   len1=sqrt(xD1*xD1+yD1*yD1);  
   len2=sqrt(xD2*xD2+yD2*yD2);  

   // calculate angle between the two lines.  
   dot=(xD1*xD2+yD1*yD2); // dot product  
   deg=dot/(len1*len2);  

   // if abs(angle)==1 then the lines are parallell,  
   // so no intersection is possible  
   if(abs(deg)==1) return null;  

   // find intersection Pt between two lines  
   Point pt=new Point(0,0);  
   div=yD2*xD1-xD2*yD1;  
   ua=(xD2*yD3-yD2*xD3)/div;  
   ub=(xD1*yD3-yD1*xD3)/div;  
   pt.x=p1.x+ua*xD1;  
   pt.y=p1.y+ua*yD1;  

   // calculate the combined length of the two segments  
   // between Pt-p1 and Pt-p2  
   xD1=pt.x-p1.x;  
   xD2=pt.x-p2.x;  
   yD1=pt.y-p1.y;  
   yD2=pt.y-p2.y;  
   segmentLen1=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);  

   // calculate the combined length of the two segments  
   // between Pt-p3 and Pt-p4  
   xD1=pt.x-p3.x;  
   xD2=pt.x-p4.x;  
   yD1=pt.y-p3.y;  
   yD2=pt.y-p4.y;  
   segmentLen2=sqrt(xD1*xD1+yD1*yD1)+sqrt(xD2*xD2+yD2*yD2);  

   // if the lengths of both sets of segments are the same as  
   // the lenghts of the two lines the point is actually  
   // on the line segment.  

   // if the point isn’t on the line, return null  
   if(abs(len1-segmentLen1)>0.01 || abs(len2-segmentLen2)>0.01)  
     return null;  

   // return the valid intersection  
   return pt;  
 }  

 class Point{  
   float x,y;  
   Point(float x, float y){  
     this.x = x;  
     this.y = y;  
   }  

   void set(float x, float y){  
     this.x = x;  
     this.y = y;  
   }  
 }  

8
Có một số vấn đề với mã này. Nó có thể đưa ra một ngoại lệ do chia cho 0; nó chậm vì nó mất căn bậc hai; và đôi khi nó trả về dương tính giả vì nó sử dụng yếu tố mờ nhạt. Bạn có thể làm tốt hơn thế này!
Gareth Rees

Được rồi như một giải pháp nhưng được Jason đưa ra chắc chắn là nhanh hơn về mặt tính toán và tránh được rất nhiều vấn đề với giải pháp này
Elemental

6

Tôi đã thử một vài trong số những câu trả lời này, nhưng chúng không có tác dụng với tôi (xin lỗi các bạn); Sau khi tìm kiếm thêm trên mạng, tôi tìm thấy cái này .

Với một chút sửa đổi mã của anh ta, giờ đây tôi có chức năng này sẽ trả về điểm giao nhau hoặc nếu không tìm thấy giao lộ nào, nó sẽ trả về -1, -1.

    Public Function intercetion(ByVal ax As Integer, ByVal ay As Integer, ByVal bx As Integer, ByVal by As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal dx As Integer, ByVal dy As Integer) As Point
    '//  Determines the intersection point of the line segment defined by points A and B
    '//  with the line segment defined by points C and D.
    '//
    '//  Returns YES if the intersection point was found, and stores that point in X,Y.
    '//  Returns NO if there is no determinable intersection point, in which case X,Y will
    '//  be unmodified.

    Dim distAB, theCos, theSin, newX, ABpos As Double

    '//  Fail if either line segment is zero-length.
    If ax = bx And ay = by Or cx = dx And cy = dy Then Return New Point(-1, -1)

    '//  Fail if the segments share an end-point.
    If ax = cx And ay = cy Or bx = cx And by = cy Or ax = dx And ay = dy Or bx = dx And by = dy Then Return New Point(-1, -1)

    '//  (1) Translate the system so that point A is on the origin.
    bx -= ax
    by -= ay
    cx -= ax
    cy -= ay
    dx -= ax
    dy -= ay

    '//  Discover the length of segment A-B.
    distAB = Math.Sqrt(bx * bx + by * by)

    '//  (2) Rotate the system so that point B is on the positive X axis.
    theCos = bx / distAB
    theSin = by / distAB
    newX = cx * theCos + cy * theSin
    cy = cy * theCos - cx * theSin
    cx = newX
    newX = dx * theCos + dy * theSin
    dy = dy * theCos - dx * theSin
    dx = newX

    '//  Fail if segment C-D doesn't cross line A-B.
    If cy < 0 And dy < 0 Or cy >= 0 And dy >= 0 Then Return New Point(-1, -1)

    '//  (3) Discover the position of the intersection point along line A-B.
    ABpos = dx + (cx - dx) * dy / (dy - cy)

    '//  Fail if segment C-D crosses line A-B outside of segment A-B.
    If ABpos < 0 Or ABpos > distAB Then Return New Point(-1, -1)

    '//  (4) Apply the discovered position to line A-B in the original coordinate system.
    '*X=Ax+ABpos*theCos
    '*Y=Ay+ABpos*theSin

    '//  Success.
    Return New Point(ax + ABpos * theCos, ay + ABpos * theSin)
End Function

6

Dường như có một số quan tâm đến câu trả lời của Gavin mà cortijon đã đề xuất một phiên bản javascript trong các bình luận và iMalc cung cấp một phiên bản với các tính toán ít hơn một chút . Một số đã chỉ ra những thiếu sót với các đề xuất mã khác nhau và những người khác đã nhận xét về hiệu quả của một số đề xuất mã.

Thuật toán được cung cấp bởi iMalc thông qua câu trả lời của Gavin là thuật toán mà tôi hiện đang sử dụng trong một dự án javascript và tôi chỉ muốn cung cấp một phiên bản đã được làm sạch ở đây nếu nó có thể giúp đỡ bất cứ ai.

// Some variables for reuse, others may do this differently
var p0x, p1x, p2x, p3x, ix,
    p0y, p1y, p2y, p3y, iy,
    collisionDetected;

// do stuff, call other functions, set endpoints...

// note: for my purpose I use |t| < |d| as opposed to
// |t| <= |d| which is equivalent to 0 <= t < 1 rather than
// 0 <= t <= 1 as in Gavin's answer - results may vary

var lineSegmentIntersection = function(){
    var d, dx1, dx2, dx3, dy1, dy2, dy3, s, t;

    dx1 = p1x - p0x;      dy1 = p1y - p0y;
    dx2 = p3x - p2x;      dy2 = p3y - p2y;
    dx3 = p0x - p2x;      dy3 = p0y - p2y;

    collisionDetected = 0;

    d = dx1 * dy2 - dx2 * dy1;

    if(d !== 0){
        s = dx1 * dy3 - dx3 * dy1;
        if((s <= 0 && d < 0 && s >= d) || (s >= 0 && d > 0 && s <= d)){
            t = dx2 * dy3 - dx3 * dy2;
            if((t <= 0 && d < 0 && t > d) || (t >= 0 && d > 0 && t < d)){
                t = t / d;
                collisionDetected = 1;
                ix = p0x + t * dx1;
                iy = p0y + t * dy1;
            }
        }
    }
};

Tôi không hiểu làm thế nào bạn có thể hiểu những gì đang diễn ra với các dòng như t = dx2 * dy3 - dx3 * dy2;...
Supuhstar

@Supuhstar Nó phải làm với toán vector và định nghĩa của sản phẩm chấm và sản phẩm chéo. Ví dụ mã bạn đã đăng đại diện cho một hoạt động sản phẩm chéo. Đó là một cách chiếu một đoạn đường lên một đoạn khác để xác định vị trí của nó nằm ở đoạn đường kia, trước điểm bắt đầu ở đâu đó ở giữa hoặc sau đường kẻ. Vậy t là một giá trị chuẩn hóa. Nếu nó nằm trong khoảng từ 0 đến 1 thì hai đoạn giao nhau. Nếu nó nhỏ hơn 0 hoặc lớn hơn một thì họ không làm thế.
Nolo

@Supuhstar Cũng lưu ý rằng để phép chiếu tìm điểm thực tế, kết quả phải được thu nhỏ. Đó là nơi t/dđến.
Nolo

1
Ý tôi là làm thế nào để bạn hiểu những gì đang diễn ra trong nháy mắt với các tên biến như thế? Tại sao không phải là một cái gì đó như crossProduct = (line1XDifference * line2YDifference) - (line2XDifference * line1YDifference)scaledResult = crossProduct / dotProduct?
Supuhstar

1
@Supuhstar À, tôi hiểu ý của bạn. Erm, tôi cho rằng thực sự không có lý do chính đáng nào để nói về việc ám ảnh về hiệu quả, nhưng đó không phải là lý do chính đáng bởi vì các trình biên dịch làm rất tốt việc lấy hầu hết mọi mã bạn đưa cho họ và làm cho nó hiệu quả như có thể trong khi không thay đổi những gì nên tính toán. Mặt khác, các tên, p1x, p1yv.v. có nghĩa là để mô tả các điểm theo giá trị x và y của chúng, do đó, p1xviết tắt cho point1x, tương tự d1x, trong tâm trí của tôi là viết tắt cho chữ Hy Lạp deltaXhoặc bạn có thể nói differenceInX. (còn nữa)
Nolo

5

Tôi nghĩ rằng có một giải pháp đơn giản hơn nhiều cho vấn đề này. Tôi đã nảy ra một ý tưởng khác ngày hôm nay và nó dường như chỉ hoạt động tốt (ít nhất là ở dạng 2D bây giờ). Tất cả những gì bạn phải làm là tính toán giao điểm giữa hai đường, sau đó kiểm tra xem điểm giao cắt được tính có nằm trong các hộp ràng buộc của cả hai đoạn đường không. Nếu có, các đoạn đường giao nhau. Đó là nó.

BIÊN TẬP:

Đây là cách tôi tính toán giao lộ (Tôi không biết nữa nơi tôi tìm thấy đoạn mã này)

Point3D

đến từ

System.Windows.Media.Media3D

public static Point3D? Intersection(Point3D start1, Point3D end1, Point3D start2, Point3D end2) {

        double a1 = end1.Y - start1.Y;
        double b1 = start1.X - end1.X;
        double c1 = a1 * start1.X + b1 * start1.Y;

        double a2 = end2.Y - start2.Y;
        double b2 = start2.X - end2.X;
        double c2 = a2 * start2.X + b2 * start2.Y;

        double det = a1 * b2 - a2 * b1;
        if (det == 0) { // lines are parallel
            return null;
        }

        double x = (b2 * c1 - b1 * c2) / det;
        double y = (a1 * c2 - a2 * c1) / det;

        return new Point3D(x, y, 0.0);
    }

và đây là của tôi (được đơn giản hóa cho mục đích của câu trả lời) Lớp BoundingBox:

public class BoundingBox {
    private Point3D min = new Point3D();
    private Point3D max = new Point3D();

    public BoundingBox(Point3D point) {
        min = point;
        max = point;
    }

    public Point3D Min {
        get { return min; }
        set { min = value; }
    }

    public Point3D Max {
        get { return max; }
        set { max = value; }
    }

    public bool Contains(BoundingBox box) {
        bool contains =
            min.X <= box.min.X && max.X >= box.max.X &&
            min.Y <= box.min.Y && max.Y >= box.max.Y &&
            min.Z <= box.min.Z && max.Z >= box.max.Z;
        return contains;
    }

    public bool Contains(Point3D point) {
        return Contains(new BoundingBox(point));
    }

}

3

Giải pháp này có thể giúp

public static float GetLineYIntesept(PointF p, float slope)
    {
        return p.Y - slope * p.X;
    }

    public static PointF FindIntersection(PointF line1Start, PointF line1End, PointF line2Start, PointF line2End)
    {

        float slope1 = (line1End.Y - line1Start.Y) / (line1End.X - line1Start.X);
        float slope2 = (line2End.Y - line2Start.Y) / (line2End.X - line2Start.X);

        float yinter1 = GetLineYIntesept(line1Start, slope1);
        float yinter2 = GetLineYIntesept(line2Start, slope2);

        if (slope1 == slope2 && yinter1 != yinter2)
            return PointF.Empty;

        float x = (yinter2 - yinter1) / (slope1 - slope2);

        float y = slope1 * x + yinter1;

        return new PointF(x, y);
    }

3

Tôi đã chuyển câu trả lời trên của Kris sang JavaScript. Sau khi thử nhiều câu trả lời khác nhau, ông đã cung cấp các điểm chính xác. Tôi nghĩ rằng tôi đang phát điên rằng tôi đã không đạt được số điểm tôi cần.

function getLineLineCollision(p0, p1, p2, p3) {
    var s1, s2;
    s1 = {x: p1.x - p0.x, y: p1.y - p0.y};
    s2 = {x: p3.x - p2.x, y: p3.y - p2.y};

    var s10_x = p1.x - p0.x;
    var s10_y = p1.y - p0.y;
    var s32_x = p3.x - p2.x;
    var s32_y = p3.y - p2.y;

    var denom = s10_x * s32_y - s32_x * s10_y;

    if(denom == 0) {
        return false;
    }

    var denom_positive = denom > 0;

    var s02_x = p0.x - p2.x;
    var s02_y = p0.y - p2.y;

    var s_numer = s10_x * s02_y - s10_y * s02_x;

    if((s_numer < 0) == denom_positive) {
        return false;
    }

    var t_numer = s32_x * s02_y - s32_y * s02_x;

    if((t_numer < 0) == denom_positive) {
        return false;
    }

    if((s_numer > denom) == denom_positive || (t_numer > denom) == denom_positive) {
        return false;
    }

    var t = t_numer / denom;

    var p = {x: p0.x + (t * s10_x), y: p0.y + (t * s10_y)};
    return p;
}

2

Tôi đã thử rất nhiều cách và sau đó tôi quyết định tự viết. Vì vậy, đây là:

bool IsBetween (float x, float b1, float b2)
{
   return ( ((x >= (b1 - 0.1f)) && 
        (x <= (b2 + 0.1f))) || 
        ((x >= (b2 - 0.1f)) &&
        (x <= (b1 + 0.1f))));
}

bool IsSegmentsColliding(   POINTFLOAT lineA,
                POINTFLOAT lineB,
                POINTFLOAT line2A,
                POINTFLOAT line2B)
{
    float deltaX1 = lineB.x - lineA.x;
    float deltaX2 = line2B.x - line2A.x;
    float deltaY1 = lineB.y - lineA.y;
    float deltaY2 = line2B.y - line2A.y;

    if (abs(deltaX1) < 0.01f && 
        abs(deltaX2) < 0.01f) // Both are vertical lines
        return false;
    if (abs((deltaY1 / deltaX1) -
        (deltaY2 / deltaX2)) < 0.001f) // Two parallel line
        return false;

    float xCol = (  (   (deltaX1 * deltaX2) * 
                        (line2A.y - lineA.y)) - 
                    (line2A.x * deltaY2 * deltaX1) + 
                    (lineA.x * deltaY1 * deltaX2)) / 
                 ((deltaY1 * deltaX2) - (deltaY2 * deltaX1));
    float yCol = 0;
    if (deltaX1 < 0.01f) // L1 is a vertical line
        yCol = ((xCol * deltaY2) + 
                (line2A.y * deltaX2) - 
                (line2A.x * deltaY2)) / deltaX2;
    else // L1 is acceptable
        yCol = ((xCol * deltaY1) +
                (lineA.y * deltaX1) -
                (lineA.x * deltaY1)) / deltaX1;

    bool isCol =    IsBetween(xCol, lineA.x, lineB.x) &&
            IsBetween(yCol, lineA.y, lineB.y) &&
            IsBetween(xCol, line2A.x, line2B.x) &&
            IsBetween(yCol, line2A.y, line2B.y);
    return isCol;
}

Dựa trên hai công thức sau: (Tôi đã đơn giản hóa chúng từ phương trình đường thẳng và các công thức khác)

công thức cho x

công thức cho y


Hoạt động nhưng cố gắng nhập tọa độ này (nếu nó là colinear / chồng chéo thì nó sẽ trả về kết quả sai): PointA1 = (0,0) PointA2 = (0,2) và PointB1 = (0,1) PointB2 = (0,5)
dns

@dns Vâng, đó là vì mã trả về sai cho các đường song song. Tôi thấy vấn đề, tuy nhiên, tôi vẫn không biết hàm nào sẽ trả về vì có vô số câu trả lời cho nó.
Soroush Falahati

2

Điều này dựa trên câu trả lời của Gareth Ree. Nó cũng trả về sự chồng chéo của các phân đoạn dòng nếu chúng làm. Được mã hóa trong C ++, V là một lớp vectơ đơn giản. Trong đó tích chéo của hai vectơ trong 2D trả về một vô hướng. Nó đã được kiểm tra và thông qua hệ thống kiểm tra tự động của trường tôi.

//Required input point must be colinear with the line
bool on_segment(const V& p, const LineSegment& l)
{
    //If a point is on the line, the sum of the vectors formed by the point to the line endpoints must be equal
    V va = p - l.pa;
    V vb = p - l.pb;
    R ma = va.magnitude();
    R mb = vb.magnitude();
    R ml = (l.pb - l.pa).magnitude();
    R s = ma + mb;
    bool r = s <= ml + epsilon;
    return r;
}

//Compute using vector math
// Returns 0 points if the lines do not intersect or overlap
// Returns 1 point if the lines intersect
//  Returns 2 points if the lines overlap, contain the points where overlapping start starts and stop
std::vector<V> intersect(const LineSegment& la, const LineSegment& lb)
{
    std::vector<V> r;

    //http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
    V oa, ob, da, db; //Origin and direction vectors
    R sa, sb; //Scalar values
    oa = la.pa;
    da = la.pb - la.pa;
    ob = lb.pa;
    db = lb.pb - lb.pa;

    if (da.cross(db) == 0 && (ob - oa).cross(da) == 0) //If colinear
    {
        if (on_segment(lb.pa, la) && on_segment(lb.pb, la))
        {
            r.push_back(lb.pa);
            r.push_back(lb.pb);
            dprintf("colinear, overlapping\n");
            return r;
        }

        if (on_segment(la.pa, lb) && on_segment(la.pb, lb))
        {
            r.push_back(la.pa);
            r.push_back(la.pb);
            dprintf("colinear, overlapping\n");
            return r;
        }

        if (on_segment(la.pa, lb))
            r.push_back(la.pa);

        if (on_segment(la.pb, lb))
            r.push_back(la.pb);

        if (on_segment(lb.pa, la))
            r.push_back(lb.pa);

        if (on_segment(lb.pb, la))
            r.push_back(lb.pb);

        if (r.size() == 0)
            dprintf("colinear, non-overlapping\n");
        else
            dprintf("colinear, overlapping\n");

        return r;
    }

    if (da.cross(db) == 0 && (ob - oa).cross(da) != 0)
    {
        dprintf("parallel non-intersecting\n");
        return r;
    }

    //Math trick db cross db == 0, which is a single scalar in 2D.
    //Crossing both sides with vector db gives:
    sa = (ob - oa).cross(db) / da.cross(db);

    //Crossing both sides with vector da gives
    sb = (oa - ob).cross(da) / db.cross(da);

    if (0 <= sa && sa <= 1 && 0 <= sb && sb <= 1)
    {
        dprintf("intersecting\n");
        r.push_back(oa + da * sa);
        return r;
    }

    dprintf("non-intersecting, non-parallel, non-colinear, non-overlapping\n");
    return r;
}

2

Đây là một triển khai cơ bản của một phân đoạn dòng trong C #, với mã phát hiện giao lộ tương ứng. Nó đòi hỏi một cấu trúc vectơ / điểm 2D được gọi Vector2f, mặc dù bạn có thể thay thế cấu trúc này bằng bất kỳ loại nào khác có thuộc tính X / Y. Bạn cũng có thể thay thế floatvới doublenếu phù hợp với những nhu cầu của bạn tốt hơn.

Mã này được sử dụng trong thư viện vật lý .NET của tôi, Boing .

public struct LineSegment2f
{
    public Vector2f From { get; }
    public Vector2f To { get; }

    public LineSegment2f(Vector2f @from, Vector2f to)
    {
        From = @from;
        To = to;
    }

    public Vector2f Delta => new Vector2f(To.X - From.X, To.Y - From.Y);

    /// <summary>
    /// Attempt to intersect two line segments.
    /// </summary>
    /// <remarks>
    /// Even if the line segments do not intersect, <paramref name="t"/> and <paramref name="u"/> will be set.
    /// If the lines are parallel, <paramref name="t"/> and <paramref name="u"/> are set to <see cref="float.NaN"/>.
    /// </remarks>
    /// <param name="other">The line to attempt intersection of this line with.</param>
    /// <param name="intersectionPoint">The point of intersection if within the line segments, or empty..</param>
    /// <param name="t">The distance along this line at which intersection would occur, or NaN if lines are collinear/parallel.</param>
    /// <param name="u">The distance along the other line at which intersection would occur, or NaN if lines are collinear/parallel.</param>
    /// <returns><c>true</c> if the line segments intersect, otherwise <c>false</c>.</returns>
    public bool TryIntersect(LineSegment2f other, out Vector2f intersectionPoint, out float t, out float u)
    {
        var p = From;
        var q = other.From;
        var r = Delta;
        var s = other.Delta;

        // t = (q − p) × s / (r × s)
        // u = (q − p) × r / (r × s)

        var denom = Fake2DCross(r, s);

        if (denom == 0)
        {
            // lines are collinear or parallel
            t = float.NaN;
            u = float.NaN;
            intersectionPoint = default(Vector2f);
            return false;
        }

        var tNumer = Fake2DCross(q - p, s);
        var uNumer = Fake2DCross(q - p, r);

        t = tNumer / denom;
        u = uNumer / denom;

        if (t < 0 || t > 1 || u < 0 || u > 1)
        {
            // line segments do not intersect within their ranges
            intersectionPoint = default(Vector2f);
            return false;
        }

        intersectionPoint = p + r * t;
        return true;
    }

    private static float Fake2DCross(Vector2f a, Vector2f b)
    {
        return a.X * b.Y - a.Y * b.X;
    }
}

1

Một chương trình C ++ để kiểm tra xem hai đoạn đường đã cho có giao nhau không

#include <iostream>
using namespace std;

struct Point
{
    int x;
    int y;
};

// Given three colinear points p, q, r, the function checks if
// point q lies on line segment 'pr'
bool onSegment(Point p, Point q, Point r)
{
    if (q.x <= max(p.x, r.x) && q.x >= min(p.x, r.x) &&
        q.y <= max(p.y, r.y) && q.y >= min(p.y, r.y))
       return true;

    return false;
}

// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are colinear
// 1 --> Clockwise
// 2 --> Counterclockwise
int orientation(Point p, Point q, Point r)
{
    // See 10th slides from following link for derivation of the formula
    // http://www.dcs.gla.ac.uk/~pat/52233/slides/Geometry1x1.pdf
    int val = (q.y - p.y) * (r.x - q.x) -
              (q.x - p.x) * (r.y - q.y);

    if (val == 0) return 0;  // colinear

    return (val > 0)? 1: 2; // clock or counterclock wise
}

// The main function that returns true if line segment 'p1q1'
// and 'p2q2' intersect.
bool doIntersect(Point p1, Point q1, Point p2, Point q2)
{
    // Find the four orientations needed for general and
    // special cases
    int o1 = orientation(p1, q1, p2);
    int o2 = orientation(p1, q1, q2);
    int o3 = orientation(p2, q2, p1);
    int o4 = orientation(p2, q2, q1);

    // General case
    if (o1 != o2 && o3 != o4)
        return true;

    // Special Cases
    // p1, q1 and p2 are colinear and p2 lies on segment p1q1
    if (o1 == 0 && onSegment(p1, p2, q1)) return true;

    // p1, q1 and p2 are colinear and q2 lies on segment p1q1
    if (o2 == 0 && onSegment(p1, q2, q1)) return true;

    // p2, q2 and p1 are colinear and p1 lies on segment p2q2
    if (o3 == 0 && onSegment(p2, p1, q2)) return true;

     // p2, q2 and q1 are colinear and q1 lies on segment p2q2
    if (o4 == 0 && onSegment(p2, q1, q2)) return true;

    return false; // Doesn't fall in any of the above cases
}

// Driver program to test above functions
int main()
{
    struct Point p1 = {1, 1}, q1 = {10, 1};
    struct Point p2 = {1, 2}, q2 = {10, 2};

    doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";

    p1 = {10, 0}, q1 = {0, 10};
    p2 = {0, 0}, q2 = {10, 10};
    doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";

    p1 = {-5, -5}, q1 = {0, 0};
    p2 = {1, 1}, q2 = {10, 10};
    doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n";

    return 0;
}

1

Dựa trên câu trả lời @Gareth Rees, phiên bản dành cho Python:

import numpy as np

def np_perp( a ) :
    b = np.empty_like(a)
    b[0] = a[1]
    b[1] = -a[0]
    return b

def np_cross_product(a, b):
    return np.dot(a, np_perp(b))

def np_seg_intersect(a, b, considerCollinearOverlapAsIntersect = False):
    # /programming/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282
    # http://www.codeproject.com/Tips/862988/Find-the-intersection-point-of-two-line-segments
    r = a[1] - a[0]
    s = b[1] - b[0]
    v = b[0] - a[0]
    num = np_cross_product(v, r)
    denom = np_cross_product(r, s)
    # If r x s = 0 and (q - p) x r = 0, then the two lines are collinear.
    if np.isclose(denom, 0) and np.isclose(num, 0):
        # 1. If either  0 <= (q - p) * r <= r * r or 0 <= (p - q) * s <= * s
        # then the two lines are overlapping,
        if(considerCollinearOverlapAsIntersect):
            vDotR = np.dot(v, r)
            aDotS = np.dot(-v, s)
            if (0 <= vDotR  and vDotR <= np.dot(r,r)) or (0 <= aDotS  and aDotS <= np.dot(s,s)):
                return True
        # 2. If neither 0 <= (q - p) * r = r * r nor 0 <= (p - q) * s <= s * s
        # then the two lines are collinear but disjoint.
        # No need to implement this expression, as it follows from the expression above.
        return None
    if np.isclose(denom, 0) and not np.isclose(num, 0):
        # Parallel and non intersecting
        return None
    u = num / denom
    t = np_cross_product(v, s) / denom
    if u >= 0 and u <= 1 and t >= 0 and t <= 1:
        res = b[0] + (s*u)
        return res
    # Otherwise, the two line segments are not parallel but do not intersect.
    return None

0

Nếu mỗi cạnh của hình chữ nhật là một đoạn đường và phần người dùng vẽ là một đoạn đường, thì bạn chỉ cần kiểm tra đoạn người dùng được vẽ để biết giao điểm với bốn đoạn đường bên. Đây phải là một bài tập khá đơn giản với các điểm bắt đầu và kết thúc của mỗi phân đoạn.


3
Lưu ý rằng đây là một câu trả lời hợp lý cho câu hỏi như được đóng khung ban đầu nhưng bây giờ câu hỏi đã được chỉnh sửa rất nhiều, nó không có ý nghĩa nhiều lắm.
GS - Xin lỗi đến Monica

0

Dựa trên câu trả lời của t3chb0t:

int intersezione_linee(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int& p_x, int& p_y)
{
   //L1: estremi (x1,y1)(x2,y2) L2: estremi (x3,y3)(x3,y3)
   int d;
   d = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
   if(!d)
       return 0;
   p_x = ((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4))/d;
   p_y = ((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4))/d;
   return 1;
}

int in_bounding_box(int x1, int y1, int x2, int y2, int p_x, int p_y)
{
    return p_x>=x1 && p_x<=x2 && p_y>=y1 && p_y<=y2;

}

int intersezione_segmenti(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int& p_x, int& p_y)
{
    if (!intersezione_linee(x1,y1,x2,y2,x3,y3,x4,y4,p_x,p_y))
        return 0;

    return in_bounding_box(x1,y1,x2,y2,p_x,p_y) && in_bounding_box(x3,y3,x4,y4,p_x,p_y);
}

0

Tôi đọc các thuật toán này từ cuốn sách "nhiều hình học xem"

văn bản sau sử dụng

'như dấu hiệu chuyển vị

* dưới dạng sản phẩm chấm

x là sản phẩm chéo, khi sử dụng làm toán tử

1. định nghĩa dòng

một điểm x_vec = (x, y) 'nằm trên đường thẳng ax + by + c = 0

chúng ta biểu thị L = (a, b, c) ', điểm dưới dạng (x, y, 1)' là tọa độ đồng nhất

phương trình đường thẳng có thể được viết là

(x, y, 1) (a, b, c) '= 0 hoặc x' * L = 0

2. giao điểm của đường

ta có hai dòng L1 = (a1, b1, c1) ', L2 = (a2, b2, c2)'

giả sử x là một điểm, một vectơ và x = L1 x L2 (sản phẩm chéo L1 L2).

hãy cẩn thận, x luôn là một điểm 2D, vui lòng đọc tọa độ đồng nhất nếu bạn nhầm lẫn về (L1xL2) là một vectơ ba phần tử và x là tọa độ 2D.

theo ba sản phẩm, chúng tôi biết rằng

L1 * (L1 x L2) = 0 và L2 * (L1 x L2) = 0, vì cùng mặt phẳng L1, L2

chúng ta thay thế (L1xL2) bằng vectơ x, khi đó ta có L1 * x = 0, L2 * x = 0, có nghĩa là x nằm trên cả L1 và L2, x là điểm giao nhau.

hãy cẩn thận, ở đây x là tọa độ đồng nhất, nếu phần tử cuối cùng của x bằng 0, có nghĩa là L1 và L2 song song.


0

Nhiều câu trả lời đã kết thúc tất cả các tính toán thành một hàm duy nhất. Nếu bạn cần tính toán độ dốc đường, chặn y hoặc x-chặn để sử dụng ở nơi khác trong mã của mình, bạn sẽ thực hiện các phép tính đó một cách dự phòng. Tôi đã tách ra các hàm tương ứng, sử dụng tên biến rõ ràng và nhận xét mã của tôi để dễ theo dõi hơn. Tôi cần phải biết nếu các dòng giao nhau vô hạn vượt quá điểm cuối của chúng, vì vậy trong JavaScript:

http://jsfiddle.net/skibulk/evmqq00u/

var point_a = {x:0, y:10},
    point_b = {x:12, y:12},
    point_c = {x:10, y:0},
    point_d = {x:0, y:0},
    slope_ab = slope(point_a, point_b),
    slope_bc = slope(point_b, point_c),
    slope_cd = slope(point_c, point_d),
    slope_da = slope(point_d, point_a),
    yint_ab = y_intercept(point_a, slope_ab),
    yint_bc = y_intercept(point_b, slope_bc),
    yint_cd = y_intercept(point_c, slope_cd),
    yint_da = y_intercept(point_d, slope_da),
    xint_ab = x_intercept(point_a, slope_ab, yint_ab),
    xint_bc = x_intercept(point_b, slope_bc, yint_bc),
    xint_cd = x_intercept(point_c, slope_cd, yint_cd),
    xint_da = x_intercept(point_d, slope_da, yint_da),
    point_aa = intersect(slope_da, yint_da, xint_da, slope_ab, yint_ab, xint_ab),
    point_bb = intersect(slope_ab, yint_ab, xint_ab, slope_bc, yint_bc, xint_bc),
    point_cc = intersect(slope_bc, yint_bc, xint_bc, slope_cd, yint_cd, xint_cd),
    point_dd = intersect(slope_cd, yint_cd, xint_cd, slope_da, yint_da, xint_da);

console.log(point_a, point_b, point_c, point_d);
console.log(slope_ab, slope_bc, slope_cd, slope_da);
console.log(yint_ab, yint_bc, yint_cd, yint_da);
console.log(xint_ab, xint_bc, xint_cd, xint_da);
console.log(point_aa, point_bb, point_cc, point_dd);

function slope(point_a, point_b) {
  var i = (point_b.y - point_a.y) / (point_b.x - point_a.x);
  if (i === -Infinity) return Infinity;
  if (i === -0) return 0;
  return i;
}

function y_intercept(point, slope) {
    // Horizontal Line
    if (slope == 0) return point.y;
  // Vertical Line
    if (slope == Infinity)
  {
    // THE Y-Axis
    if (point.x == 0) return Infinity;
    // No Intercept
    return null;
  }
  // Angled Line
  return point.y - (slope * point.x);
}

function x_intercept(point, slope, yint) {
    // Vertical Line
    if (slope == Infinity) return point.x;
  // Horizontal Line
    if (slope == 0)
  {
    // THE X-Axis
    if (point.y == 0) return Infinity;
    // No Intercept
    return null;
  }
  // Angled Line
  return -yint / slope;
}

// Intersection of two infinite lines
function intersect(slope_a, yint_a, xint_a, slope_b, yint_b, xint_b) {
  if (slope_a == slope_b)
  {
    // Equal Lines
    if (yint_a == yint_b && xint_a == xint_b) return Infinity;
    // Parallel Lines
    return null;
  }
  // First Line Vertical
    if (slope_a == Infinity)
  {
    return {
        x: xint_a,
      y: (slope_b * xint_a) + yint_b
    };
  }
  // Second Line Vertical
    if (slope_b == Infinity)
  {
    return {
        x: xint_b,
      y: (slope_a * xint_b) + yint_a
    };
  }
  // Not Equal, Not Parallel, Not Vertical
  var i = (yint_b - yint_a) / (slope_a - slope_b);
  return {
    x: i,
    y: (slope_a * i) + yint_a
  };
}
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.