충돌처리-원점에서 두 점을 잇는 선사이의 각도, 즉 두 벡터 사이의 각도 0~360범위로 구하기.
- 프로그래밍/물리 & 수학
- 2011. 6. 15. 13:49
제목 그대로 그림에서처럼 두 벡터 사이의 각도를 구해보죠.
이건 원운동을 하기 위해 필요한 작업이에요.
사실 이건 고등학교 수학책에도 나올만한 문제이지요. 백터의 내적을 이용하면 되거든요.
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;
}
이 글을 공유하기