Html5 Canvas
Web 에서 일반적인 언어의 Graphics 를 사용하는 것과 유사한 작업을 가능하게 해주는 모듈이 추가된지도 꽤 오랜 시간이 흘렀습니다.
돌이켜 보면 SVG 라는 도형을 그리는 표준이 d3.js 라는 뛰어난 라이브러리로, Chart 등에서 맹위를 떨치던 시간이 그리 오래 전이
아닌데 벌써, 트랜드의 한축이 넘어가는 느낌이 듭니다.
현재 많은 차트 라이브러리에서 SVG 에서 점차 CANVAS 로 옮겨가는 추세 입니다.
svg는 web 에서 느린 자원이 아닙니다. 게다가 각 도형별, shape 별 event 처리가 canvas 에 비해 훨씬 수월 합니다.
canvas 는 일반적인 언어의 Graphics 객체를 직접 다루는 것과 유사하게 접근하여야 하기 때문에 모든 형태를 직접 그려 주고, 그에 따른
Event 처리를 하나 하나 처리해 주어야 합니다.
이런 불편함에도, 빠른 속도와, 상상할 수 있는 대부분의 작업이 Web 에서 가능하다는 장점으로, Web Chart 등의 Application 과 유사한
영역에서 조금씩 자리를 넓혀가는 것 같습니다. ( 물론 WebGL 도 포함 해서요 …. )
간단한 Canvas 예제
아래의 그림은 canvas 를 활용하여 그려본 삼각형입니다.
Canvas 에 대한 기초적인 정리가 가능하고, 삼각함수에 대한 정리도 가능할 것 같아서 간단히 작성하여 보았습니다.
function makeCanvas(width, height) {
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
return canvas;
}
function makeTriangleUI( width, height) {
const gaps = 80;
const tSize = width - gaps*2;
const lineLen = Math.round(Math.sqrt(tSize*tSize*2));
const degrees = 45;
const radian = (Math.PI*degrees/180);
const points = [];
const lineNames = [{name:'a',x:0,y:0},{name:'b',x:0,y:0},{name:'c',x:0,y:0}];
let tx = 0;
let ty = 0;
points.push([tx,ty]);
lineNames[0].x = Math.floor(tSize/2);
lineNames[0].y = 20;
tx = Math.cos(radian)*lineLen;
ty = 0;
points.push([tx,ty]);
tx = Math.cos(radian)*lineLen;
ty = Math.sin(radian)*lineLen;
lineNames[1].x = tSize + 15;
lineNames[1].y = -Math.round(ty/2);
lineNames[2].x = Math.floor(Math.cos(radian)*lineLen/2) - 30;
lineNames[2].y = -Math.round(ty/2);
points.push([tx,-ty]);
const canvas = makeCanvas(width, height);
const ctx = canvas.getContext("2d");
ctx.clearRect(0,0,width,height);
ctx.save();
ctx.translate(gaps,tSize+gaps); //new 0,0 base
ctx.beginPath();
ctx.fillStyle = "#EEEE22";
ctx.strokeStyle = "#000080";
ctx.lineWidth = 2;
ctx.moveTo(0,0);
ctx.arc(0,0, 50, 0, -radian, true);
ctx.lineTo(0,0);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineJoin = "round";
ctx.strokeStyle = "#000080";
ctx.font = "bold 18px consolas";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// theta
ctx.fillStyle = "#000080";
ctx.fillText("θ", Math.cos(radian/2)*60, -Math.sin(radian/2)*60 );
const sizeRect = 30;
ctx.strokeRect(points[1][0]-sizeRect,points[1][1]-sizeRect,sizeRect,sizeRect);
ctx.stroke();
for ( let i = 0; i < 3; i++ ) {
if ( i == 0 ) {
ctx.moveTo(points[0][0], points[0][1]);
} else {
ctx.lineTo(points[i][0], points[i][1]);
}
ctx.save();
ctx.fillStyle = "#EE22FE";
ctx.fillText(lineNames[i].name, lineNames[i].x, lineNames[i].y );
ctx.restore();
}
ctx.closePath();
ctx.stroke();
ctx.restore();
document.body.appendChild(canvas);
console.log(canvas.toDataURL());
}
Canvas 를 그리는 부분은 잠시 놓아 두고 보면, 그림을 그릴 때 사용한 부분이 degree 에서 radian 으로 변환하여 사용하였고, Math.sin, Math.cos 함수를 그리는 용도로 사용하고 있습니다.
javascript 에서는 sine, cosine, tangent 와 그 역함수 등을 api 로 제공하고 있습니다. 물론 대부분의 언어에서 언급한 함수는 제공하고 있습니다.
프로그램에서 삼각함수의 입력값은 모두 radian 입니다. 그렇기 때문에 degree 에서 radian, radian 에서 degree 로의 변환이 필요합니다.
아래는 그 결과 입니다. 이 예시를 보면서 간단하게 삼각함수를 정리해 보고자 합니다.
삼각함수
가능한 이런저런 수식 없이 말로 풀어서 정리해 보려고 합니다.
몇가지 기본적인 공식은 함수를 이해하기 위해 필요하기 때문에 먼저 언급하고 진행하겠습니다.
제가 아는 중학교 수학 수준에서 정리해 보려고 합니다.
선행 공식
중학교때 배우는 피타고라스 정의 입니다.
다른 하나는 삼각형의 비율, 삼각비를 지칭하는 명칭 입니다. (sine, cosine, tangent )
$$ 직각삼각형 \quad c = 빗변, a = 밑변, b = 높이, h = 높이, ( 직각삼각형의 경우 b = h ), \theta = 각도(radian) \\ 피타고라스 공식 : c=\sqrt{a^2+b^2}, \quad c^2 = a^2 + b^2 \\ sin\theta = \frac{b}{c} , \quad cos\theta = \frac{a}{c} , \quad tan\theta = \frac {b}{a} \\ 삼각형의 면적 = a \times h \times \frac{1}{2} = a \times b \times \frac{1}{2} ( 직각삼각형 ) \\ 1 (radian) = 52.2957… (도) \quad \pi(radian) = 180 (도) \quad 1 (radian) = \frac{1 \times 180} {\pi} \\ $$
sin, cos, tan 이 길이를 기준으로 한 비율이라는 것, 삼각형의 면적구하기 공식, 피타고라스 정의 정도가 앞으로 나올 이야기를 이해하는 기준이 될 것 같습니다.
생각정리
- 직각삼각형의 각도가 0 에서 증가하여 90 도까지 변경한다고 가정해 보겠습니다.
- theta 가 0도 이면, 가로와 빗변이 같습니다. 삼각형이 아닌 직선이 되겠네요 …. 이때 a = c 이니, a/c = 1 이 됩니다. cos 의 값이 1로 가장 큽니다. sin 은 b = 0 이니 b/c = 0 입니다.
- theta 가 90 도면 위와 반대로 b = c 이고 a = 0 입니다. cos 의 값은 0이고, sin 은 1 입니다.
- 위의 내용을 참조로 0 ~ 90 도 까지 빗변(c) 를 각 sine 과 cosine 의 값에 곱해 주면 어떤 현상이 나타날까요? 앞서의 정의에서 삼각함수를 비율로 표현했습니다. 모두 분모가 빗변(c) 이기 때문에 분모가 약분되어 없어지면 sine 높이와 cosine 가로 의 위치를 알 수 있습니다.
- 좌표값을 x, y 라고 할 때 빗변(c) - 원의 반지름 을 곱해주면 , sin theta 는 y, cos theta 는 x 위치가 됩니다.
- 0도 일때 sine 이 0, cosine 이 1 인것을 상기하면, 각도가 증가할 때 x, y 의 위치는 오른쪽 수평선에서 위로 왼쪽으로 다시 아래로 도는 시계 반대 방향을 생각해 볼 수 있습니다.
- 여기까지의 내용을 정리하면, sine 은 y 좌표와 연결되고, cosine 은 x 좌표와 매칭되어 오른쪽 수평선에서 시계 반대방향으로 원을 그릴 수 있다는 것을 예상할 수 있습니다.
- 앞서 canvas 로 삼각형을 그린 소스에서 살펴 보아도 Math.sin(theta) = y 값과, Math.cos(theta) = x 값과 매핑되는 것을 확인 할 수 있습니다.
canvas 의 좌표계가 왼쪽 상단이 0, 0 이고 x는 오른쪽으로 증가하는 정상적인 흐름이지만, y 는 아래로 증가하는 구조로 되어 있습니다. 그래서 y 좌표는 뒤집어서 처리하였습니다.
이미 많이 접해 보신 분들은 너무 당연한 부분이겠지만, 기본적인 내용 부터 정리하고자 간단히 작성해 보았습니다.
위의 코드에서 0 ~ 360 도를 radian 으로 변경해서 cos, sin 을 활용하면 , x, y 좌표를 얻고 이를 연결하면 원이 됩니다.
다음에 시간이 될 때 Canvas 를 활용하여 원, 회전 등을 정리해 보려 합니다. ^^