[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의 상대 좌표를 의미합니다. 기존 변환 행렬이 적용된 상태이므로 이를 계산하기 위해서는 다소 노력이 필요합니다. 이에 대해서는 본 블로그의 지난 포스트를 참고하시길 바랍니다.

시리즈는 다음 편에서 계속됩니다. 읽어주셔서 감사합니다!

묻고 답하기

개인적인 판단에 의해 적절하다고 여겨지는 경우, 모두가 볼 수 있도록 이곳에 문답이 추가됩니다. 그렇지 않더라도 질문에 대한 답변은 별도로 이루어집니다.