각도, 라디안, atan2
이번에는 각도입니다. 그래픽 에디터에서 각도는 회전 핸들, 방향 표시, path tangent, 축 계산까지 여기저기 얼굴을 내밉니다.
각도는 사실 방향 벡터를 숫자 하나로 압축한 것입니다.
“중심에서 봤을 때 포인터가 어느 방향에 있나요?”
이 질문에 답하는 순간 회전 도구가 시작됩니다. 자, 이제 원을 한 바퀴 돌아봅시다. 어지러우면 잠깐 쉬어도 됩니다.
브라우저 수학 함수는 라디안을 좋아한다
CSS에서는 45deg, 0.25turn, 0.785rad처럼 여러 단위를 쓸 수 있습니다. 그런데 JavaScript의 Math.sin, Math.cos, Math.atan2는 라디안을 씁니다.
degree = radian * 180 / PI
radian = degree * PI / 180
초보자 입장에서는 “왜 둘이 통일을 안 했나요?” 싶습니다. 맞습니다. 우리도 마음이 편하진 않습니다. 하지만 변환식은 짧으니 너무 미워하진 맙시다.
atan2는 방향을 각도로 바꾼다
중심점이 (cx, cy)이고 포인터가 (x, y)에 있다면, 중심에서 포인터로 향하는 벡터는 이렇습니다.
v = (x - cx, y - cy)
이 벡터의 각도는 atan2로 구합니다.
theta = atan2(y - cy, x - cx)
atan2가 좋은 이유는 사분면을 보존한다는 점입니다. 그냥 atan(y / x)로 계산하면 오른쪽 위와 왼쪽 아래를 헷갈릴 수 있습니다. 회전 핸들이 갑자기 반대로 튀면 사용자는 도구를 의심하고, 우리는 수학을 의심하게 됩니다. 그러니 atan2를 씁시다.
회전은 각도 차이다
회전 핸들을 잡는 순간의 각도를 startAngle, 현재 포인터의 각도를 currentAngle이라고 하겠습니다.
deltaAngle = currentAngle - startAngle
newRotation = originalRotation + deltaAngle
편집기의 회전 도구는 거의 이 구조입니다. 물론 실제 구현에서는 360도 경계 처리가 필요합니다. 359deg에서 1deg로 넘어갈 때 차이를 -358deg로 보면 안 됩니다. 사용자는 2도 움직였는데 오브젝트가 갑자기 큰절을 하면 곤란하니까요.
회전 행렬도 같은 이야기다
점을 원점 기준으로 theta만큼 회전하면 이렇게 됩니다.
rotated = (
x * cos(theta) - y * sin(theta),
x * sin(theta) + y * cos(theta)
)
지금 당장은 외우지 않아도 됩니다. 중요한 것은 cos와 sin이 회전된 x축과 y축을 만들어낸다는 감각입니다. 뒤에서 CSS matrix를 볼 때 이 공식이 다시 정장을 입고 등장합니다.
각도는 나중에 여러 곳에서 다시 나온다
지금은 CSS 속성보다 수학 구조에 집중하겠습니다. 중심에서 포인터로 향하는 벡터를 만들고, 그 벡터를 atan2로 각도로 바꾸는 구조만 잡아두면 됩니다.
이 구조는 뒤에서 회전 핸들, path tangent, 그리고 각도를 이용하는 CSS 그래픽 속성을 볼 때 다시 등장합니다. 수학 개념이 먼저, 응용은 나중입니다. 오늘은 욕심내지 맙시다.
데모에서 볼 것
데모에서는 회전 핸들을 움직이며 atan2로 얻은 각도와 CSS rotate() 값이 같은 의미인지 비교합니다.
오늘의 요지는 단순합니다. 중심을 정하고, 중심에서 포인터로 가는 벡터를 만들고, 그 벡터를 각도로 바꿉니다. 회전 도구가 괜히 신비로운 척하지만, 속은 꽤 솔직합니다.