충돌처리-원점에서 두 점을 잇는 선사이의 각도, 즉 두 벡터 사이의 각도 0~360범위로 구하기.



제목 그대로 그림에서처럼 두 벡터 사이의 각도를 구해보죠.

이건 원운동을 하기 위해 필요한 작업이에요.

사실 이건 고등학교 수학책에도 나올만한 문제이지요. 백터의 내적을 이용하면 되거든요.

a점의 x1, y1이고 b점이 x2, y2면 아시다시피 두 백터의 내적은 다음과 같아요.

a*b = |a||b|*cos(c) -- 앞의 * 는 내적을 표해요. 표현하기가 마땅치 않군요.

이걸 풀어쓰고 정리하면(루트는 sqrt()로 쓸게요)

cos(c) = { x1 * y1 + x2 * y2 } / { sqrt( x1^2 + y1^2 ) * sqrt( x2^2 + y2^2 ) }

이제 cos의 역함수인 acos()함수를 쓰면 c를 구할 수 있어요. 보통은.

그런데 여기서 math.h 에 들어있는 acos의 한계가 나와요. 이놈은 리턴을 0~180까지의 범위만으로 하거든요.

다시 설명하자면 다음과 같이 점이 있을때.



나는 a에서 b까지 시계방향으로 측정한 각도를 구하고 싶은데 위 방법대로 하면

반시계방향의 각도가 나와 버린다는 소리죠.

그래서 내적을 이용해 구하기전에 약간의 사전작업을 해야 해요. 다시 그림을 보자면.



일단 a를 90도 회전시킨 a' 을 구해요. 이건 매우 쉬워요.

주의해야할껀 좌표계의 방향이에요. 보통 윈도우를 만들면 아래쪽으로 갈수록

y값이 커지는건 알고 있죠. 그걸 기준으로 설명할게요.

a 가 x1, y1 이고 a' 이 x1', y1' 일때 시계방향으로 회전시키고 싶으면 

x1' = -y1, y1' = x1이 되요.

반시계방향이라면. x1' = y1, y1' = -x1이 되죠. 조금만 생각해보면 답나와요.

이제 a'점과 b점사이의 각도를 위의 방식으로 내적을 이용해서 구해요.

이제 거의 다 됬어요. 방금구한 각도를 c' 라고 할때.

방금구한 각도가 90 보다 크면 a에서 b까지의 각도는 180도 보다 크다는 이야기에요.

그렇다면 간단하죠.

c'이 90보다 작을때에는 a와 b사이의 각도는 내적을 이용해서 바로 구할 수 있고.

c'이 90보다 클 때에는 360에서 내적을 이용해 구한 a와 b사이의 각도를 빼면 되지요.

아까 설명했다시피 c'이 90보다 클때는 a와 b사이의 각도가 반시계방향의 각도로 나오잖아요.

0~180의 범위로.


이런식으로 시계방향 혹은 반시계방향으로 0~360범위의 각도를 구할 수 있답니다.

아래 코드는 그걸 짜본거에요.

세번째 인자인 isClockWise는 true로 하면 시계방향, false로 하면 반시계방향의 각도를 구합니다.

fPOINT 는 전에 말했다시피 float x, y로 구성된 구조체입니다.

----------------------------------------------------------------------------------------

float CPath::CalAngleBetweenTwoPoint(fPOINT ptPoint1, fPOINT ptPoint2, BOOL isClockWise)
{
 //acos()나 asin()함수로 구하는 각도를 degree로 나타냈을때
 //그 범위는 0~180이다.

 //각도를 0~360으로 나타내기 위한 사전작업으로
 //기준점(ptPoint1)을 90도 회전시킨 점을 구한다.
 fPOINT ptRotated90Point1 = {0,0};

 if(isClockWise)
 {
  ptRotated90Point1.x= -ptPoint1.y;
  ptRotated90Point1.y = ptPoint1.x;
 }
 else
 {
  ptRotated90Point1.x =  ptPoint1.y;
  ptRotated90Point1.y = -ptPoint1.x;
 }

 //앞서 계산한 점과 두번째 인자로 받은 점간의 각도를 구함.
 //이 각도가 90도보다 크다면 첫번째인자로 받은 기준점과 두번째 점간의 각도가
 //180도보다 크다는 것을 의미한다.

 float fAng = acos( (ptRotated90Point1.x * ptPoint2.x + ptRotated90Point1.y * ptPoint2.y) / 
  (sqrt( ptRotated90Point1.x * ptRotated90Point1.x + ptRotated90Point1.y * ptRotated90Point1.y ) * 
  sqrt(ptPoint2.x * ptPoint2.x + ptPoint2.y * ptPoint2.y) ) )
  * 360 / TWOPI; 

 //fAng의 크기에 따라 두점사이의 각도를 다른 방식으로 구함.
 if(fAng > 90)
  return 360 - acos( (ptPoint1.x * ptPoint2.x + ptPoint1.y * ptPoint2.y) / 
  (sqrt( ptPoint1.x * ptPoint1.x + ptPoint1.y * ptPoint1.y ) * 
  sqrt(ptPoint2.x * ptPoint2.x + ptPoint2.y * ptPoint2.y) ) )
  * 360 / TWOPI;

 else
  return acos( (ptPoint1.x * ptPoint2.x + ptPoint1.y * ptPoint2.y) / 
  (sqrt( ptPoint1.x * ptPoint1.x + ptPoint1.y * ptPoint1.y ) * 
  sqrt(ptPoint2.x * ptPoint2.x + ptPoint2.y * ptPoint2.y) ) )
  * 360 / TWOPI; 
}

이 글을 공유하기

댓글

Designed by JB FACTORY