캔버스나 뷰에서 이런저런 장난을 치다 보면, 두 점 사이의 각도를 구해야 할 때가 있다. 이럴 때는 삼각함수 중 arctan을 이용해보자.
삼각함수중 tan을 이용하면 특정한 각도에서의 세로/가로의 비를 구할 수 있다. 이를 역으로 계산해주는 함수가 arctan이다.
그렇지만 그래프에서 알 수 있듯이, -90°~90° 사이의 각만 계산되므로 별도의처리를 좀 더 해주어야 -180°~180° 사이의 각을 얻을 수 있다.
위와 같은 컴퓨터 화면에서 θ(이하 a)를 계산하는 함수를 만들어보자. 윗쪽을 0° 라고 하고, 시계방향이 증가하는 방향이라고 정의하자. a주변의 작은 삼각형을 생각해 볼 때, 화살표 방향에 대해 높이는 x2-x1 이지만 밑변은 y1-y2 이다.
let a;
a = Math.atan((x2-x1)/(y1-y2));
그렇지만 대충 보아도 y1==y2일 경우 위 문장은 오류가 난다. -90°,90°,180°는 따로 처리해주자.
let a;
if(y1 == y2) {
if(x2<x1) {
a = -90;
} else {
a = 90;
}
} else if(x1==x2 && y2>y1) {
a = 180;
} else {
a = Math.atan((x2-x1)/(y1-y2));
}
이렇게 하면 각도의 범위중 -90°~90°, 180° 가 해결되었다.
나머지 범위에 해당하는 각 경우를 살펴보자.
+90°를 넘어가는 경우에, 가령 150°의 경우에 (x2-x1)/(y1-y2) 의 비는 -30° 일 때와 동일하고, 구분하는 방법은 y2>y1 인지만 확인하면 된다. 따라서 이 경우 +180° 에 arctan 값(-30°)을 더하면 된다.
마찬가지로 -90°를 넘어가는 경우에, 가령 -150°의 경우에 (x2-x1)/(y1-y2) 의 비는 +30° 일 때와 동일하고, 구분하는 방법은 y2>y1 인지만 확인하면 된다. 따라서 이 경우 -180° 에 arctan 값(+30°)을 더하면 된다.
함수는 다음과 같이 된다.
let a;
if(y1 == y2) {
if(x2<x1) {
a = -90;
} else {
a = 90;
}
} else if(x1==x2 && y2>y1) {
a = 180;
} else {
a = Math.atan((x2-x1)/(y1-y2));
if(y2>y1 && x2>x1) {
a = 180 + a;
} else if(y2>y1 && x2<x1) {
a = -180 + a;
}
}
아직 할 일이 남아 있다. 자바스크립트를 포함한 대부분의 프로그래밍 언어/라이브러리에서 삼각함수를 처리할 때 다루는 각도는 degree(°)단위가 아니라 rad 단위이다. 1rad는 반지름의 길이와 호의길이가 같은 부채꼴의 중심각 크기이다. (대략 57.3°)
180°크기의 반원 피자가 있다고 하자. 우리는 이 피자를 잘라서 위와 같은 피자 조각을 3개하고 조금 더 얻을 수 있을 것이다. 좀 더 정확히는 3개하고 0.14개 정도. 따라서 s rad와 t degree 사이에는 다음과 같은 식이 성립한다.
180° : 3.14 = t : s
t = s × 180 / 3.14
또는 t = s × 180 / Math.PI
함수형태로 완성시켜보자.
function getAngle(x1,y1,x2,y2) {
let a;
if(y1 == y2) {
if(x2<x1) {
a = -90;
} else {
a = 90;
}
} else if(x1==x2 && y2>y1) {
a = 180;
} else {
const rad = Math.atan((x2-x1)/(y1-y2));
a = rad * 180 / Math.PI;
if(y2>y1 && x2>x1) {
a = 180 + a;
} else if(y2>y1 && x2<x1) {
a = -180 + a;
}
}
return a;
}
간단한 테스터를 달아보자.
index.html
<!DOCTYPE html>
<html>
<head>
<script src="index.js"></script>
<style>
#test { border: 1px solid #000; width:400px; height:400px; }
</style>
</head>
<body>
<div id="test"></div>
</body>
</html>
index.js
function getAngle(x1,y1,x2,y2) {
let a;
if(y1 == y2) {
if(x2<x1) {
a = -90;
} else {
a = 90;
}
} else if(x1==x2 && y2>y1) {
a = 180;
} else {
const rad = Math.atan((x2-x1)/(y1-y2));
a = rad * 180 / Math.PI;
if(y2>y1 && x2>x1) {
a = 180 + a;
} else if(y2>y1 && x2<x1) {
a = -180 + a;
}
}
return a;
}
document.addEventListener("DOMContentLoaded", () => {
const testField = document.querySelector("#test");
testField.addEventListener('mousemove', e => {
const angle = getAngle(200, 200, e.offsetX, e.offsetY);
console.log(angle);
})
})
실행결과는 다음과 같다.