[TypeScript] 제스처로 대화하기. #15 - 핀치(with. 기준점) 편

Table of contents

이번 편은 이전 편으로부터 이어집니다.
이번에는 기준점에 대해 한번 생각해 봅시다. 포인터 이벤트는 하나의 포인터에 대해 개별적으로 발생합니다. 두 포인터의 이동이 거의 동일하더라도 말이지요. 따라서 두 포인터에 대해 움직이는 포인터와 고정된 포인터로 생각할 수 있습니다. 그렇다면 고정된 포인터가 기준점이 되겠네요. 이를 수행하기 위한 변환 행렬은 아래와 같습니다.
$$\begin{aligned} \begin{pmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} s & 0 & 0 \\ 0 & s & 0 \\ 0 & 0 & 1 \end{pmatrix} = \begin{pmatrix} s & 0 & t_x \\ 0 & s & t_y \\ 0 & 0 & 1 \end{pmatrix} \end{aligned}$$
크기를 수정하고 기준점이 고정될 수 있도록 각 축에 대해 일정 수치만큼 이동합니다. 그렇다면 얼마만큼 이동해야 할까요? 기준점이 될 고정 포인터의 좌표를 \((P_x, P_y)\)라 하면 핀치 후 새로운 좌표는 아래와 같습니다.
$$\begin{aligned} \begin{pmatrix} P_x' \\ P_y' \\ 1 \end{pmatrix} & = \begin{pmatrix} s & 0 & t_x \\ 0 & s & t_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} P_x \\ P_y \\ 1 \end{pmatrix} \\ & = \begin{pmatrix} sP_x + t_x \\ sP_y + t_y \\ 1 \end{pmatrix} \end{aligned}$$
앞서 언급한 것처럼 기준점은 고정되어야 하므로
$$\begin{aligned} & x = sP_x + t_x \\ & \begin{aligned} t_x & = P_x - sP_x \\ & = (1 - s)P_x \end{aligned} \\ \\ & y = sP_y + t_y \\ & \begin{aligned} t_y & = P_y - sP_y \\ & = (1 - s)P_y \end{aligned} \end{aligned}$$
따라서 기본 핀치 행렬은 아래와 같습니다.
$$\begin{aligned} \begin{pmatrix} s & 0 & t_x \\ 0 & s & t_y \\ 0 & 0 & 1 \end{pmatrix} = \begin{pmatrix} s & 0 & (1 - s)P_x \\ 0 & s & (1 - s)P_y \\ 0 & 0 & 1 \end{pmatrix} \end{aligned}$$
이제 핀치 전 이미 적용된 변환 행렬에 대해 살펴봅시다.
$$\begin{aligned} \begin{pmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{pmatrix} \end{aligned}$$
현재의 위치 변환을 무시할 수 없지만 이것이 기준점이 되기를 원하지는 않습니다. 따라서 이를 분해하여 좌측으로 이동할 필요가 있습니다. 이 이유에 대해서는 본 블로그의 지난 포스트에 설명되어 있으니 참고하시길 바랍니다.
$$\begin{aligned} \begin{pmatrix} 1 & 0 & e \\ 0 & 1 & f \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} a & c & 0 \\ b & d & 0 \\ 0 & 0 & 1 \end{pmatrix} \end{aligned}$$
이제 이 사이에 구성한 핀치 행렬을 끼워 넣으면 완성입니다.
$$\begin{aligned} M & = \begin{pmatrix} 1 & 0 & e \\ 0 & 1 & f \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} s & 0 & (1 - s)P_x \\ 0 & s & (1 - s)P_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} a & c & 0 \\ b & d & 0 \\ 0 & 0 & 1 \end{pmatrix} \\ & = \begin{pmatrix} s & 0 & e + (1 - s)P_x \\ 0 & s & f + (1 - s)P_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} a & c & 0 \\ b & d & 0 \\ 0 & 0 & 1 \end{pmatrix} \\ & = \begin{pmatrix} sa & sc & e + (1 - s)P_x \\ sb & sd & f + (1 - s)P_y \\ 0 & 0 & 1 \end{pmatrix} \end{aligned}$$
코드로 나타내면 아래와 같습니다.
const p = {
x: origin.x - transformOrigin.x,
y: origin.y - transformOrigin.y
};
box.style.transform = new DOMMatrix([
scale * a,
scale * b,
scale * c,
scale * d,
e + (1 - scale) * p.x,
f + (1 - scale) * p.y
])
.toString();
transformOrigin
는 뷰포트에 대한 transform-origin
의 상대 좌표를 의미합니다. 기존 변환 행렬이 적용된 상태이므로 이를 계산하기 위해서는 다소 노력이 필요합니다. 이에 대해서는 본 블로그의 지난 포스트를 참고하시길 바랍니다.
시리즈는 다음 편에서 계속됩니다. 읽어주셔서 감사합니다!
묻고 답하기
개인적인 판단에 의해 적절하다고 여겨지는 경우, 모두가 볼 수 있도록 이곳에 문답이 추가됩니다. 그렇지 않더라도 질문에 대한 답변은 별도로 이루어집니다.
Subscribe to my newsletter
Read articles from 고라니드로 directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
