[geometry] 두 선분이 교차하는 곳을 어떻게 감지합니까? [닫은]

두 선이 서로 교차하는지 여부를 확인하려면 어떻게해야합니까?



답변

벡터 교차 곱을 사용하는이 문제에 대한 좋은 접근 방식이 있습니다. 2 차원 벡터 교차 곱 v  ×  wv x  w y  −  v y  w x로 정의 합니다.

두 개의 선분이 p 에서 p  +  r로 , q 에서 q  +  s로 실행된다고 가정합니다 . 그러면 첫 번째 행에있는 모든 포인트로서 표현할 수있다 P  +  t  R (스칼라 파라미터에 대한  t )와 같은 두번째 라인상의 임의의 지점 (Q)  +  U  S (스칼라 파라미터에 대한  U ).

교차하는 두 선분

두 줄 은 다음과 같이 tu를 찾을 수 있으면 교차 합니다.

p + t  r = q + u  s

교차점에 대한 공식

s로 양쪽을 가로 지르십시오.

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

그리고 이후  ×  S = 0,이 수단

t  ( r × s ) = ( qp ) × s

따라서 t에 대한 해결 :

t = ( qp ) × s / ( r × s )

같은 방식으로 u를 풀 수 있습니다 .

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

u  ( s × r ) = ( pq ) × r

u = ( pq ) × r / ( s × r )

계산 단계 수를 줄이려면 다음과 같이 다시 작성하는 것이 편리합니다 ( s  ×  r = −  r  ×  s 기억 ).

u = ( qp ) × r / ( r × s )

이제 네 가지 경우가 있습니다.

  1. 경우 R  ×  S  = 0 ( Q  –  P ) ×  R  = 0, 다음 두 라인을 동일 선상.

    이 경우 첫 번째 선분 ( p + t r ) 의 방정식으로 두 번째 선분 ( qq  +  s ) 의 끝점을 표현하십시오 .

    t 0 = ( qp ) ·  r / ( r  ·  r )

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

    t 0t 1 사이의 간격 이 간격 [0, 1]과 교차하면 선분은 동일 선상에 있고 겹칩니다. 그렇지 않으면 공선적이고 분리됩니다.

    sr 이 반대 방향을 가리키는 경우 , s  ·  r <0이므로 확인할 간격은 [ t 0 , t 1 ]이 아니라 [ t 1 , t 0 ]입니다.

  2. 경우 R  ×  S  = 0 ( Q  –  P ) ×  R  ≠ 0, 다음 두 라인이 평행이 아닌 교차한다.

  3. 만약 R  ×  S  ≠ 0, 0 ≤  t  ≤ 1, 0 ≤  U  ≤ 1, 두 선분이 만나는 지점에서 P + t  R = Q + U  S .

  4. 그렇지 않으면 두 선분이 평행하지 않지만 교차하지는 않습니다.

크레딧 :이 방법은 304 페이지 그래픽 젬에 출판 된 Ronald Goldman의 기사 “3 공간에서 2 개의 선의 교차점”에서 3D 선 교차 알고리즘의 2 차원 특수화입니다 . 3 차원에서 일반적인 경우는 다음과 같습니다. 선이 기울어 지거나 (병렬 또는 교차하지 않음),이 경우 방법은 두 선에 가장 가까운 접근 점을 제공합니다.


답변

FWIW에서 다음 함수 (C)는 선 교차를 감지하고 교차점을 결정합니다. Andre LeMothe의 ” Tricks of the Windows Game Programming Gurus ” 알고리즘을 기반으로합니다 . 다른 답변 (예 : Gareth)의 일부 알고리즘과 다르지 않습니다. 그런 다음 LeMothe는 Cramer ‘s Rule (나에게 묻지 않음)을 사용하여 방정식 자체를 해결합니다.

나는 그것이 연약한 소행성 클론에서 작동한다는 것을 증명할 수 있으며 Elemental, Dan 및 Wodzu의 다른 답변에 설명 된 가장자리 사례를 올바르게 처리하는 것으로 보입니다. KingNestor가 게시 한 코드보다 아마 빠를 수도 있습니다. 왜냐하면 그것은 모두 곱셈과 나눗셈이기 때문입니다.

내 경우에는 문제가되지 않았지만 0으로 나눌 가능성이 있다고 생각합니다. 어쨌든 충돌을 피하기 위해 쉽게 수정할 수 있습니다.

// 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, 나는 LeMothe의 책에서 분명히 알고리즘을 올바르게 얻지 만, 구체적인 숫자는 플러그를 잘못된 숫자로 표시하고 계산을 잘못한다고 말합니다. 예를 들면 다음과 같습니다.

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

= 844 / 0.88

= 0.44

그것은 몇 시간 동안 나를 혼란스럽게했다 . 🙁


답변

문제는이 질문으로 줄어 듭니다. A에서 B로, C에서 D로 두 줄이 교차합니까? 그런 다음 네 번 요청할 수 있습니다 (직사각형의 선과 네면 사이).

여기에 대한 벡터 수학이 있습니다. A에서 B 로의 선이 문제의 선이고 C에서 D 로의 선이 직사각형 선 중 하나라고 가정합니다. 내 표기법은 Ax“A의 x 좌표”이고 Cy“C의 y 좌표”입니다. 그리고 ” *“는 내적을 의미 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 )

h숫자가 열쇠입니다. 경우 h사이 01, 선이 교차하는, 그렇지 않으면하지 않습니다. 경우는 F*P물론이 계산을 할 수 없습니다의 제로이지만,이 경우 라인은 평행 따라서 만 교차 명백한 경우입니다.

정확한 교차점은 C + F*h입니다.

더 재미있는:

경우 h입니다 정확히 0 또는 1라인은 엔드 포인트에서 누릅니다. 당신은 이것을 “교차”라고 생각할 수 있습니다.

특히, h다른 선을 정확하게 터치하기 위해 선의 길이를 얼마나 곱해야 하는가입니다.

따라서이면 h<0사각형 라인이 주어진 라인의 “뒤에”있고 ( “방향”은 “A에서 B로”), h>1사각형 라인이 주어진 라인의 “앞에”있다는 것을 의미합니다.

유도:

A와 C는 선의 시작을 가리키는 벡터입니다. E와 F는 선을 형성하는 A와 C의 끝에서 나온 벡터입니다.

평면에서 임의의 두 개의 비 평행선, 정확히 하나의 스칼라 한쌍이 있어야 g하고 h,이 방정식은 유지되도록 :

A + E*g = C + F*h

왜? 두 개의 비평 행 선이 교차해야하므로 두 선을 일정량 씩 조정하고 서로 접촉 할 수 있습니다.

( 두 개의 미지수를 가진 하나의 방정식처럼 먼저이 외모에서! 그러나 당신이이 정말로에서 방정식의 한 쌍의 의미 차원 벡터 방정식이라고 생각하면 그렇지 않다 xy.)

이러한 변수 중 하나를 제거해야합니다. 쉬운 방법은 E용어를 0 으로 만드는 것 입니다. 그렇게하려면 E로 0으로 점이 찍히는 벡터를 사용하여 방정식의 양변에있는 내적을 구하십시오. P위에서 언급 한 벡터 는 E의 명백한 변형을 수행했습니다.

당신은 지금 :

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


답변

위의 Jason이 우아하게 설명한 알고리즘을 구현하려고했습니다. 불행히도 디버깅의 수학을 통해 작업하는 동안 작동하지 않는 많은 사례를 발견했습니다.

예를 들어 점 A (10,10) B (20,20) C (10,1) D (1,10)은 h = .5를 나타내지 만 이러한 세그먼트가 각각의 근처에 있지 않다는 점을 분명히 알 수 있습니다 다른.

이를 그래프로 나타내면 0 <h <1 기준이 인터셉트 포인트가 CD에있을 경우에만 CD에 있다는 것을 나타내지 만 해당 포인트가 AB에 있는지 여부는 아무 것도 알려주지 않습니다. 교차점이 있는지 확인하려면 변수 g에 대해 대칭 계산을 수행해야하고 가로 채기 요구 사항은 다음과 같습니다. 0 <g <1 AND 0 <h <1


답변

Gavin의 답변이 개선되었습니다. marcp의 솔루션도 비슷하지만 부서를 연기하지는 않습니다.

이것은 실제로 Gareth Rees의 답변을 실제로 적용하는 것으로 밝혀졌습니다. 2D에서 교차 제품의 동등 물은 perp-dot-product이므로이 코드는 3 개를 사용합니다. 3D로 전환하고 교차 곱을 사용하여 끝에 s와 t를 보간하면 3D 선 사이에서 가장 가까운 두 지점이 생깁니다. 어쨌든 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;
}

기본적으로 마지막 순간까지 나누기를 연기하고 특정 계산이 완료되기 전까지 대부분의 테스트를 이동하여 조기 종료를 추가합니다. 마지막으로, 선이 평행 일 때 발생하는 0으로 나누지 않아도됩니다.

0과 비교하는 것이 아니라 엡실론 테스트를 사용하는 것이 좋습니다. 평행에 매우 가까운 선은 약간 벗어난 결과를 생성 할 수 있습니다. 이것은 버그가 아니며 부동 소수점 연산의 제한 사항입니다.


답변

질문 C : 두 선분이 교차하는지 여부를 어떻게 감지합니까?

나는 같은 주제를 검색했으며 그 대답에 만족하지 않았습니다. 그래서 두 선분 이 많은 이미지와 교차하는지 확인하는 방법을 매우 자세히 설명하는 기사를 작성했습니다 . 완전한 Java 코드가 있습니다.

다음은 가장 중요한 부분으로 잘린 기사입니다.

선분 a가 선분 b와 교차하는지 확인하는 알고리즘은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

경계 상자 란 무엇입니까? 다음은 두 선분으로 된 두 개의 경계 상자입니다.

여기에 이미지 설명을 입력하십시오

두 경계 상자에 교차점이 있으면 한 점이 (0 | 0)이되도록 선분 a를 이동합니다. 이제 a로 정의 된 원점을 통과하는 선이 생겼습니다. 이제 선분 b를 같은 방식으로 이동하고 선분 b의 새 점이 선 a의 다른쪽에 있는지 확인합니다. 이 경우 다른 방법으로 확인하십시오. 이 경우에도 선분이 교차합니다. 그렇지 않으면 교차하지 않습니다.

질문 A : 두 선분은 어디에 교차합니까?

두 선분 a와 b가 교차한다는 것을 알고 있습니다. 모르는 경우 “질문 C”에서 제공 한 도구를 사용하여 확인하십시오.

이제 몇 가지 사례를 살펴보고 7 학년 수학으로 솔루션을 얻을 수 있습니다 ( 코드 및 대화식 예제 참조 ).

질문 B : 두 선이 교차하는지 여부를 어떻게 감지합니까?

의이 점 가정 해 봅시다 A = (x1, y1), 포인트를 B = (x2, y2), C = (x_3, y_3), D = (x_4, y_4). 첫 번째 줄은 AB (A! = B)로, 두 번째 줄은 CD (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;
    }
}

질문 D : 두 선은 어디에 교차합니까?

질문 B와 전혀 교차하는지 확인하십시오.

선 a와 b는 각 선에 대해 두 점으로 정의됩니다. 기본적으로 질문 A에서 사용한 것과 동일한 논리를 적용 할 수 있습니다.


답변

한 번 여기에 수락 된 답변은 올바르지 않습니다 (허용되지 않았으므로 hooray!). 교차로가 아닌 모든 부분을 올바르게 제거하지는 않습니다. 사소하게 작동하는 것처럼 보일 수 있지만 특히 0과 1이 h에 유효한 것으로 간주되는 경우 실패 할 수 있습니다.

다음과 같은 경우를 고려하십시오.

(4,1)-(5,1) 및 (0,0)-(0,2)의 줄

이들은 겹치지 않는 수직선입니다.

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)) 도트 (0,1) / ((0 , -2) 도트 (0,1)) = 0

위의 답변에 따르면,이 두 선분은 끝점 (값 0과 1)에서 만나게됩니다. 엔드 포인트는 다음과 같습니다.

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

따라서, 두 개의 선분은 (0,0)에서 만나지 만, 선 CD에는 있지만 AB에는 없습니다. 무슨 일이야? 대답은 0과 1의 값이 유효하지 않으며 종점 교차점을 정확하게 예측하기 위해 때때로 발생한다는 것입니다. 한 선 (다른 선이 아닌)의 연장이 선 세그먼트를 충족 할 때 알고리즘은 선 세그먼트의 교차점을 예측하지만 올바르지 않습니다. AB vs CD로 테스트 한 다음 CD vs AB로 테스트하면이 문제가 해결 될 것이라고 생각합니다. 둘 다 0과 1 사이에있는 경우에만 교차 할 수 있습니다.

엔드 포인트를 예측해야하는 경우 벡터 교차 곱 방법을 사용하는 것이 좋습니다.

-단