CCW (Counter Clock Wise)
CCW (Counter Clock Wise)
이 글은 제 개인적인 공부를 위해 작성한 글입니다.
틀린 내용이 있을 수 있고, 피드백은 환영합니다.
개요
CCW는 Counter Clock Wise(반시계 방향)의 약자로, 평면 위에 세 점이 주어졌을 때 세 점의 방향성이 어느 쪽인가를 판별하는 알고리즘이다.
2차원 평면에 세 점 p1, p2, p3가 순서대로 주어졌을 때, 이 점들을 이은 경로의 방향은 딱 3가지 경우만 존재한다.
- 반시계 방향 : p1 → p2 → p3로 이동할 때 왼쪽으로 꺾이는 경우
- 시계 방향 : p1 → p2 → p3로 이동할 때 오른쪽으로 꺾이는 경우
- 일직선 : 꺽이지 않고 곧게 뻗거나 왔던 길로 되돌아가는 경우
이 세 점이 어느 방향으로 꺾였는지 알 수 있는 방법은 벡터의 외적(Cross Product)이다.
세 점 p1(x1, y1), p2(x2, y2), p3(x3, y3)가 있을 때, p1을 시작점으로 하는 두 개의 벡터를 만들자.
- u = p1p2 = (x2 - x1, y2 - y1)
- v = p1p3 = (x3 - x1, y3 - y1)
두 2차원 벡터의 외적 수식은 행렬식으로 계산할 수 있다.
- u * v = (x2 - x1)(y3 - y1) - (y2 - y1)(x3 - x1)
결과가 양수(> 0)인 경우, 세 점은 반시계 방향으로 꺾여있다.
결과가 음수(< 0)인 경우, 세 점은 시계 방향으로 꺾여있다.
결과가 0인 경우, 세 점은 일직선 상에 놓여있다.
C++ 코드 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
struct Point
{
long long x, y;
};
int ccw(Point p1, Point p2, Point p3)
{
long long result = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
if (result > 0) return 1; // 반시계
if (result < 0) return -1; // 시계
return 0; // 일직선
}
int main()
{
Point p1(0, 0), p2(5, 0), p3(5, 5);
std::cout << ccw(p1, p2, p3); // 1
return 0;
}
CCW를 응용한 선분 교차 판정
두 개의 선분 (A,B), (C,D)가 주어질 때, 이 선분들이 서로 교차하는지 판정하는 문제는 CCW 알고리즘을 활용하여 해결할 수 있다. 한 선분을 기준으로 다른 선분의 양 끝점이 서로 다른 방향에 있는지 확인하면 된다.
- (A,B) 선분을 기준으로 (C,D)의 양 끝점이 서로 다른 방향에 있는지 확인한다. (CCW(A,B,C)와 CCW(A,B,D) 계산)
- (C,D) 선분을 기준으로 (A,B)의 양 끝점이 서로 다른 방향에 있는지 확인한다. (CCW(C,D,A)와 CCW(C,D,B) 계산)
- 두 선분 모두 점검했을 때, 두 선분 모두 양 끝점이 반대 방향에 있다면 교차하는 것이다.
- 한 선분이라도 양 끝점이 한 쪽에 몰려 있다면, 두 선분은 교차하지 않는다.
단, 선분이 일직선 상에 놓여있는 경우(ccw의 결과가 0)에는 추가적인 처리가 필요하다. 이 경우, 두 선분이 겹치는지 확인해야 한다.
- 두 선분이 서로 겹치지 않음.
- 두 선분이 한 점에 닿음.
- 두 선분이 겹쳐짐.
- 한 선분이 다른 선분 안에 포함됨.
선분을 x축위로 투영해서 x 좌표만 비교하자.
- A > B1 + B2라면, 만나지 않는다.
- A = B1 + B2라면, 한 점에서 만난다.
- A < B1 + B2라면, 겹쳐진다.
This post is licensed under CC BY 4.0 by the author.



