본문 바로가기
알고리즘

[기하] 다각형의 내부 외부 판별

by 박정률 2018. 5. 13.

다각형의 내부 외부 판별이란?



그림1 에는 다각형과 두개의 점 A,B 가 있습니다. 이 그림에서 눈으로 확인했을 때 각 점이 다각형 내부에 있는지, 외부에 있는 지 판별하는 것은 쉽습니다.

이렇게 주어진 점이 다각형 내부에 있는 지 외부에 있는 지 판별하는 것을 다각형의 내부 외부 판이라고 합니다.





아이디어


다각형의 내부에 위치하는 점의 특성


컴퓨터가 특정 점이 다각형 내부에 있는지 어떻게 판별할까요? 

오른쪽으로 반 직선을 그었을 때 다각형과 만나는 점의 개수가 홀수 개라면 내부에 있는 점입니다.



그림2를 보고 이 특징이 맞는지 확인해봅시다. 다각형 외부에 있는점  A는 다각형과의 교점 개수가 2개 즉 짝수개 입니다.

다각형 내부에 있는점 B는 오른쪽으로 그은 반직선과 다각형과의 교점 개수가 3개 즉 홀수 개 입니다. 


이 원칙은 눈으로 도저히 확인할 수 없을 만큼 복잡한 꼭지점이 백만 개 다각형에서도 내부 외부를 판별하는데 적용할 수 있는 강력한 규칙입니다.





구현


어떻게 반 직선과 다각형의 교점 개수를 구하지?


다각형의 내부 외부 판별 규칙은 간단하지만 반직선과 다각형의 교점의 개수를 구하는 것은 쉽지않아보입니다.


반직선과 다각형의 선분과의 교차점을 구한 후에 이 교차점이 선분 위에 있는지 조사하는 방법을 사용해야 할까요?

다행히 반직선은 x축에 항상 평행한 수평선이기 떄문에 비교적 간단하게 다각형의 선분과의 교차점을 구할 수 있습니다.


다각형의 모든 선분들에 대해 반직선과 교점이 존재하는지를 검사합니다. 

반직선과 선분 사이에 교점이 존재하기 위한 조건은 2가지 입니다.


- 점  B 의 y좌표가 두 선분 꼭지점의 y좌표 사이에 있다.

- 점  B를 지나는 수평선과 선분 사이의 교점의 x좌표가 점B의 x좌표보다 크다.




조건1을 만족하는 점B를 지나는 수평 직선과 선분 사이에 반드시 교점이 존재한다.

이 때 오른쪽 반직선과의 교점만 세야하므로 조건2를 추가로 만족해야 반직선과 선분 사이의 교점 존재여부를 올바르게 판별할 수 있습니다.





이제 그림 3과 같은 다각형이 주어졌을 때 점 B의 반직선과 다각형의 교점의 개수를 찾는 과정을 진행해봅시다.

다각형의 10개의 꼭지점은 p[0], p[1] ... p[9] 으로 주어졌습니다.





먼저 그림4처럼 꼭지점 p[0], p[1]을 연결하는 선분과 반직선과의 교차여부를 생각합시다.

 점 B는 선분 (p[0],p[1] ) 의 y좌표 사이에 있고 교점의 x좌표가 점 B보다 크기 떄문에 교차한다고 판단할 수 있습니다.






그림 5와같은 경우에는 조건2를 만족하지 않기 때문에, 교차하지 않는다고 판단해야합니다.



그림 6처럼 꼭지점 p[3],[4] 를 연결하는 선분과의 교차여부를 생각해봅시다. 

선분 (p[3],p[4])의 y좌표 사이에 있지 않기 때문에 교차하지 않는다고 판단합니다.





소스코드


1
2
3
4
5
6
7
/* 점을 구조체로 정의 함 */
strcut vector2{
int x, y;
}
/* STL vector를 이용해서 다각형 자료형을 정의 함 */
typedef vector<vector2> polygon;
 


cs

이제 점 B가 다각형 p 내부에 있는지를 반환하는 함수 isinside 를 정의합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool isInside(vector2 B, const polygon& p){
//crosses는 점q와 오른쪽 반직선과 다각형과의 교점의 개수
int crosses = 0;
for(int i = 0 ; i < p.size() ; i++){
int j = (i+1)%p.size();
//점 B가 선분 (p[i], p[j])의 y좌표 사이에 있음
if((p[i].y > B.y) != (p[j].y > B.y) ){
//atX는 점 B를 지나는 수평선과 선분 (p[i], p[j])의 교점
double atX = (p[j].x- p[i].x)*(B.y-p[i].y)/(p[j].y-p[i].y)+p[i].x;
//atX가 오른쪽 반직선과의 교점이 맞으면 교점의 개수를 증가시킨다.
if(B.x < atX)
crosses++;
}
}
return crosses % 2 > 0;
}
cs


'알고리즘' 카테고리의 다른 글

이항계수를 빠르게 구하는 알고리즘  (3) 2018.07.05
Usaco Gold) Fair Photography  (0) 2018.06.03
BOJ 6171) 땅따먹기  (0) 2018.05.13
DP최적화 - Convex Hull Trick  (0) 2018.05.06
Sqrt Decompositon(평방분할) & Mo's Algorithm  (0) 2018.04.05