사용자 도구

사이트 도구


ps:선분_교차

선분 교차

  • 선분의 교차 여부는 세 점의 방향성을 이용해서, 실수 연산 없이 구하는 것이 가능하다.

세 점의 위치 관계

  • 세 점 p1, p2, p3의 위치 관계는 벡터의 외적을 이용해서 구할수 있다.
  • (p2-p1)벡터와 (p3-p1)벡터의 외적을 구한 값이, 양수이면 시계방향(CW), 음수이면 반시계방향(CCW), 0이면 일직선상(collinear)으로 놓여져있는 것이다.

두 선분의 위치 관계

  • p1,p2를 양 끝점으로 갖는 선분 l1과 p3,p4를 양 끝점으로 갖는 선분 l2가 있을때의 두 선분의 위치관계를 생각하자.
  • 먼저 용어부터 정리하자.
    • 두 선분이 공통으로 포함하는 점이 존재한다면, intersect 한다고 한다. 이것은 crossing, touching, overlapping을 모두 포함한다.
  • 우선 간단하게, 4점이 모두 non-collinear 할 경우를 생각하자. 이때에 가능한 위치관계는 교차하던가(crossing) 떨어져있던가(disjoint) 두가지 밖에 없다.
    • CCW 연산을 4번 사용해서 위치 관계를 구할수 있다.
      • l1선분을 기준으로 p3과 p4의 방향성이 반대이고 l2선분을 기준으로 p1과 p2의 방향성이 반대이면 교차하고, 그 외에는 교차하지 않는다.
  • 이제, collinear한 점들이 있을 경우를 생각하자.
    • 똑같이 4번의 CCW 연산의 결과를 이용해서 위치 관계를 구할수 있다.
    • 그러나 한 끝점이 다른 선분에 터칭하는 경우, 두 끝점이 같은 경우, 선분의 일부가 겹치는 경우 등등을 처리해야 한다. 막 어려운건 아닌데, 코드가 길어지고 실수하기 쉽다.
      • 다행하게도.. 모든 점이 non-collinear하다는 조건을 주는 문제가 많이 있어서.. 이것들을 실제로 전부 처리할 경우가 많지는 않다.
    • 관련문제: 17387
  • 구현
    • 가독성면에서는 cross 프로덕트, 또는 is_ccw 함수를 따로 빼는것이 좋지만, python에서는 함수 호출을 4번이나 하는것이 속도면에서 너무 손해가 크다.. 그냥 인라이닝하자.
    • 모든 점이 non-collinear하다는 조건이 없으면 intersecting 여부를 체크하기 위해 teflib.geometry.is_intersecting를 사용한다
    • 모든 점이 non-collinear하다면 crossing하는지만 체크하면 된다. teflib.geometry.is_crossing을 써서 좀더 컴팩트하게 쓸수 있게 하자.
    • 가능한 위치 관계를 전부 enum으로 만들어서 리턴해주는 함수도 고민했지만, 거기까지는 그다지 필요없어서 제외했다. 이 외의 케이스를 체크해야 한다면.. (overlapping 여부를 찾는다든가..) 그때그때 구현해서 쓰자.

두 선분의 교점

  • 어쩔수 없이 실수 연산이 필요하다. 처리할 케이스도 엄청 많아서 지저분하다.
  • 우선 CCW를 이용해서 교차 여부를 체크하고, 교차하는 경우에만 계산하는것이 그나마 좀 간단하다.
  • 계산은 공식대로 하면 된다만.. 아래 코드가 https://cp-algorithms.com/geometry/segments-intersection.html보다 더 간단하다.
  • 코드는 이런식..
    • DISJOINT = 0
      OVERWRAPPING = 1
      
      
      def intersect_point(x1, y1, x2, y2, x3, y3, x4, y4):
          if not geometry.is_intersecting(x1, y1, x2, y2, x3, y3, x4, y4):
              return DISJOINT
      
          denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
          if denom:
              t1, t2 = x1 * y2 - y1 * x2, x3 * y4 - y3 * x4
              x = ((x3 - x4) * t1 - (x1 - x2) * t2) / denom
              y = ((y3 - y4) * t1 - (y1 - y2) * t2) / denom
              return (x, y)
          else:
              p1, p2, p3, p4 = sorted([(x1, y1), (x2, y2), (x3, y3), (x4, y4)])
              return p2 if p2 == p3 else OVERWRAPPING
  • 관련 문제: 선분 교차 3

토론

댓글을 입력하세요:
X O A᠎ L᠎ J
 
ps/선분_교차.txt · 마지막으로 수정됨: 2022/12/08 12:12 저자 teferi