[python] 두 세그먼트가 교차하는지 어떻게 확인할 수 있습니까?

2 개의 세그먼트가 교차하는지 어떻게 확인할 수 있습니까?

다음 데이터가 있습니다.

Segment1 [ {x1,y1}, {x2,y2} ]
Segment2 [ {x1,y1}, {x2,y2} ]

두 줄이 교차하는지 감지하기 위해 Python으로 작은 알고리즘을 작성해야합니다.

대체 텍스트



답변

선의 방정식은 다음과 같습니다.

f(x) = A*x + b = y

세그먼트의 경우 x가 구간 I에 포함된다는 점을 제외하면 정확히 동일합니다.

두 개의 세그먼트가있는 경우 다음과 같이 정의됩니다.

Segment1 = {(X1, Y1), (X2, Y2)}
Segment2 = {(X3, Y3), (X4, Y4)}

잠재적 교차점 (Xa, Ya)의 abcisse Xa는 다음과 같이 정의 된 간격 I1 및 I2 모두에 포함되어야합니다.

I1 = [min(X1,X2), max(X1,X2)]
I2 = [min(X3,X4), max(X3,X4)]

그리고 우리는 Xa가 다음에 포함되어 있다고 말할 수 있습니다.

Ia = [max( min(X1,X2), min(X3,X4) ),
      min( max(X1,X2), max(X3,X4) )]

이제이 간격 Ia가 존재하는지 확인해야합니다.

if (max(X1,X2) < min(X3,X4)):
    return False  # There is no mutual abcisses

그래서, 우리는 두 줄 공식과 상호 간격을 가지고 있습니다. 라인 공식은 다음과 같습니다.

f1(x) = A1*x + b1 = y
f2(x) = A2*x + b2 = y

세그먼트별로 두 점을 얻었으므로 A1, A2, b1 및 b2를 결정할 수 있습니다.

A1 = (Y1-Y2)/(X1-X2)  # Pay attention to not dividing by zero
A2 = (Y3-Y4)/(X3-X4)  # Pay attention to not dividing by zero
b1 = Y1-A1*X1 = Y2-A1*X2
b2 = Y3-A2*X3 = Y4-A2*X4

세그먼트가 평행 한 경우 A1 == A2 :

if (A1 == A2):
    return False  # Parallel segments

두 줄에있는 점 (Xa, Ya)은 두 공식 f1과 f2를 모두 확인해야합니다.

Ya = A1 * Xa + b1
Ya = A2 * Xa + b2
A1 * Xa + b1 = A2 * Xa + b2
Xa = (b2 - b1) / (A1 - A2)   # Once again, pay attention to not dividing by zero

마지막으로 할 일은 Xa가 Ia에 포함되어 있는지 확인하는 것입니다.

if ( (Xa < max( min(X1,X2), min(X3,X4) )) or
     (Xa > min( max(X1,X2), max(X3,X4) )) ):
    return False  # intersection is out of bound
else:
    return True

이 외에도 시작시 제공된 4 개의 포인트 중 2 개가 모든 테스트를 피하기 위해 동일하지 않은지 확인할 수 있습니다.


답변

사용자 @ i_4_got 은 Python에서 매우 효율적인 솔루션 으로이 페이지 를 가리 킵니다 . 편의를 위해 여기에서 재현합니다 (여기에 있으면 행복하게 만들었 기 때문에).

def ccw(A,B,C):
    return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x)

# Return true if line segments AB and CD intersect
def intersect(A,B,C,D):
    return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)


답변

정확하게 계산할 필요는 없습니다. 세그먼트가 교차 위치를 교차 하는지 여부 만 이해 하면됩니다. 이것은 솔루션을 단순화합니다.

아이디어는 하나의 세그먼트를 “앵커”로 취급하고 두 번째 세그먼트를 2 개의 점으로 분리하는 것입니다.
이제 “고정 된”세그먼트 (OnLeft, OnRight 또는 동일 선상)에 대한 각 지점의 상대적 위치를 찾아야합니다.
두 점에 대해 이렇게 한 후 점 중 하나가 OnLeft이고 다른 점이 OnRight인지 확인합니다 (또는 부적절한 교차점도 포함하려는 경우 동일 선상 위치를 포함 ).

그런 다음 앵커 및 분리 된 세그먼트의 역할로 프로세스를 반복해야합니다.

교차점은 점 중 하나가 OnLeft이고 다른 하나가 OnRight 인 경우에만 존재합니다. 가능한 각 사례에 대한 예제 이미지와 함께 자세한 설명 은 이 링크 를 참조하십시오 .

이러한 방법을 구현하는 것은 교차점을 찾는 방법을 실제로 구현하는 것보다 훨씬 쉽습니다 (여러분이 처리해야하는 많은 코너 케이스를 감안할 때).

최신 정보

다음 함수는 아이디어를 설명해야합니다 (출처 : Computational Geometry in C ).
비고 : 이 샘플에서는 정수 사용을 가정합니다. 대신 부동 소수점 표현을 사용하는 경우 (분명히 복잡 할 수 있음) 일부 엡실론 값을 결정하여 “동등”을 나타내야합니다 (대부분 IsCollinear평가 용).

// points "a" and "b" forms the anchored segment.
// point "c" is the evaluated point
bool IsOnLeft(Point a, Point b, Point c)
{
     return Area2(a, b, c) > 0;
}

bool IsOnRight(Point a, Point b, Point c)
{
     return Area2(a, b, c) < 0;
}

bool IsCollinear(Point a, Point b, Point c)
{
     return Area2(a, b, c) == 0;
}

// calculates the triangle's size (formed by the "anchor" segment and additional point)
int Area2(Point a, Point b, Point c)
{
     return (b.X - a.X) * (c.Y - a.Y) -
            (c.X - a.X) * (b.Y - a.Y);
}

물론 이러한 기능을 사용할 때 각 세그먼트가 다른 세그먼트 “사이”에 있는지 확인해야합니다 (이들은 무한 선이 아니라 유한 세그먼트이기 때문입니다).

또한 이러한 기능을 사용하면 교차로 가 적절 하거나 부적절한 지 여부를 이해할 수 있습니다 .

  • Proper : 동일 선상의 점이 없습니다. 세그먼트는 “옆으로”서로 교차합니다.
  • 부적절 : 한 세그먼트가 다른 세그먼트에만 “접촉”합니다 (하나 이상의 점이 고정 된 세그먼트와 동일 선상에 있음).


답변

두 세그먼트에 끝점 A, B 및 C, D가 있다고 가정합니다. 교차를 결정하는 수치 적으로 강력한 방법은 네 가지 행렬식의 부호를 확인하는 것입니다.

| Ax-Cx  Bx-Cx |    | Ax-Dx  Bx-Dx |
| Ay-Cy  By-Cy |    | Ay-Dy  By-Dy |

| Cx-Ax  Dx-Ax |    | Cx-Bx  Dx-Bx |
| Cy-Ay  Dy-Ay |    | Cy-By  Dy-By |

교차의 경우 왼쪽에있는 각 행렬식은 오른쪽에있는 것과 반대되는 부호를 가져야하지만 두 선 사이에 관계가있을 필요는 없습니다. 기본적으로 세그먼트의 각 점이 다른 세그먼트에 의해 정의 된 선의 반대편에 있는지 확인하기 위해 다른 세그먼트에 대해 확인합니다.

여기를 참조하십시오 : http://www.cs.cmu.edu/~quake/robust.html


답변

방법을 사용하여 Shapely 라이브러리를 사용 하면 선 세그먼트가 교차하는지 확인하는 것이 매우 쉽습니다 intersects.

from shapely.geometry import LineString

line = LineString([(0, 0), (1, 1)])
other = LineString([(0, 1), (1, 0)])
print(line.intersects(other))
# True

여기에 이미지 설명 입력

line = LineString([(0, 0), (1, 1)])
other = LineString([(0, 1), (1, 2)])
print(line.intersects(other))
# False

여기에 이미지 설명 입력


답변

LiranGrumdrig의 훌륭한 답변을 바탕으로 닫힌 세그먼트가 교차 하는지 확인하는 완전한 Python 코드가 있습니다. 동일 선상 세그먼트, Y 축에 평행 한 세그먼트, 세그먼트 퇴화 (악마가 자세히 설명 됨)에 대해 작동합니다. 정수 좌표를 가정합니다. 부동 소수점 좌표는 점 동등성 테스트를 수정해야합니다.

def side(a,b,c):
    """ Returns a position of the point c relative to the line going through a and b
        Points a, b are expected to be different
    """
    d = (c[1]-a[1])*(b[0]-a[0]) - (b[1]-a[1])*(c[0]-a[0])
    return 1 if d > 0 else (-1 if d < 0 else 0)

def is_point_in_closed_segment(a, b, c):
    """ Returns True if c is inside closed segment, False otherwise.
        a, b, c are expected to be collinear
    """
    if a[0] < b[0]:
        return a[0] <= c[0] and c[0] <= b[0]
    if b[0] < a[0]:
        return b[0] <= c[0] and c[0] <= a[0]

    if a[1] < b[1]:
        return a[1] <= c[1] and c[1] <= b[1]
    if b[1] < a[1]:
        return b[1] <= c[1] and c[1] <= a[1]

    return a[0] == c[0] and a[1] == c[1]

#
def closed_segment_intersect(a,b,c,d):
    """ Verifies if closed segments a, b, c, d do intersect.
    """
    if a == b:
        return a == c or a == d
    if c == d:
        return c == a or c == b

    s1 = side(a,b,c)
    s2 = side(a,b,d)

    # All points are collinear
    if s1 == 0 and s2 == 0:
        return \
            is_point_in_closed_segment(a, b, c) or is_point_in_closed_segment(a, b, d) or \
            is_point_in_closed_segment(c, d, a) or is_point_in_closed_segment(c, d, b)

    # No touching and on the same side
    if s1 and s1 == s2:
        return False

    s1 = side(c,d,a)
    s2 = side(c,d,b)

    # No touching and on the same side
    if s1 and s1 == s2:
        return False

    return True


답변

다음은 내적을 사용하는 솔루션입니다.

# assumes line segments are stored in the format [(x0,y0),(x1,y1)]
def intersects(s0,s1):
    dx0 = s0[1][0]-s0[0][0]
    dx1 = s1[1][0]-s1[0][0]
    dy0 = s0[1][1]-s0[0][1]
    dy1 = s1[1][1]-s1[0][1]
    p0 = dy1*(s1[1][0]-s0[0][0]) - dx1*(s1[1][1]-s0[0][1])
    p1 = dy1*(s1[1][0]-s0[1][0]) - dx1*(s1[1][1]-s0[1][1])
    p2 = dy0*(s0[1][0]-s1[0][0]) - dx0*(s0[1][1]-s1[0][1])
    p3 = dy0*(s0[1][0]-s1[1][0]) - dx0*(s0[1][1]-s1[1][1])
    return (p0*p1<=0) & (p2*p3<=0)

다음은 Desmos의 시각화입니다 : Line Segment Intersection