# Index

## Lesson 2 -- 2D Transformation & Animation

In this lesson, we will learn about basic geometrical transformations, like `translate`

, `rotate`

and `scale`

. We will also talk a little bit about its more general form -- *transform matrix*.

Then, we will apply these transformations with the simplest animation technique in `canvas`

as practice.

## translate

Like the picture says, translation is moving every point in a same way. Every point $(x_0, y_0)$ is mapped to $(x_1, y_1)$ such that $x_1 = x_0 + \delta_x$, $y_1 = y_0 + \delta_y$.

So actually, the representation points (like circle center, which is not essential a real point), can also be translated in the same way.

So what retain are the size, shape, as well as *direction*.

## rotate

Rotation is about *around which point* you rotate a shape, and to what *extent*. This is the invariant in transformation. Formally, we define the rotation by a *axis point* and a radian degree, so for every point $(x, y)$ in the original shape, we first translate it to the coordinate where origin is axis, so we got $(x - a_x, y - a_y)$, with it, we transform it to *polar coordinate*, $$ \theta = \arctan(\frac{y - a_y}{x - a_x}) \ r = \sqrt{(x - a_x)^2 + (y - a_y)^2}$$, so the new point should only differ in $\theta' = \theta + \delta_\theta$, and we reversely transformed $p'$ to the original coordinate.

Actually, rotation can be various. In the 3D world, you can rotate along a line as well.

## scale

scaling is simpler to understand -- just differ in size. But wait a minute, how? well, the intuition is to multiply every component of coordinate of every point by a factor. However, will the *shape* keep? Or, how to define the equivalence between shapes?

Unfortunately, this is a paradox:

In geometry, two subsets of a Euclidean space have the same shape if one can be transformed to the other by a combination of translations, rotations (together also called rigid transformations), and uniform scalings.

So, we first define the scaling, then we define the equivalence.

## Transform matrix

So, do we have some way to uniform all those different sorts of transformation?

Yes, we have. The answer is matrix.

As long as we represent the shape as collection of point, and point as a vector of coordinates, and all we want is map a point to another point, so a matrix operation is intuitive.

For translate,

$$A = \Big [\begin{matrix}
1 & 0 & v_x \
0 & 1 & v_y \
0 & 0 & 1

\end{matrix}\Big]$$

And the point must be extended to $p = (p_x, p_y, 1)$.

For rotation, let's consider the case when axis is origin

$$A = \Big [\begin{matrix} \cos \theta & -\sin\theta \ \sin \theta & \cos \theta \end{matrix} \Big]$$

For scaling, it is

$$A = \Big [\begin{matrix} v_x & 0 \ 0 & v_y \end{matrix} \Big]$$

So, ideally, using a $3 \times 3$ matrix, we can represent any primitive transformation, and by the associative property of matrix multiplication, we can get complex transformation by simple multiplying.

## Animation

The idea is simple: Let `draw()`

to be a function which will be called every X seconds. And we do a step-based transformation every time. It can be simply $Time \rightarrow Picture$ or $(Picture, \delta_{Time}) \rightarrow Picture'$.

Upon that, we got following program:

```
module Main where
import Prelude
import Control.Monad.Eff.Console
import DOM.Timer
import Graphics.Canvas (getCanvasElementById, getContext2D)
import Graphics.Canvas.Free
import Math hiding (log)
import Data.Maybe
import Control.Monad
import Data.Int (toNumber)
main = do
mcanvas <- getCanvasElementById "canvas"
case mcanvas of
Nothing -> do
log "failed to load canvas element"
Just canvas -> do
let s0 = 400 -- The length of segment
let r = 100.0 -- original radius of the line
repeatEvery 100 0 $ \s -> do -- every 100 ms, `s` is current step in integer
context <- getContext2D canvas
runGraphics context $ do
clear -- clear out the screen
setFillStyle "#000000" -- use black
-- formula 1
let s' = s `mod` s0
let x = if s' * 2 > s0 then s0 - s' else s'
-- formula 2
let theta = (toNumber s' / toNumber s0) * Math.pi
let r' = r - ((toNumber s') / 10.0)
let lineX = (toNumber x) - (Math.cos theta) * r'
let lineY = (Math.sin theta) * r' + 10.0
line (toNumber x) 10.0 lineX lineY
-- formula 3
let size' = (toNumber s') / 50.0
at (toNumber x) 10.0 $ circle (2.0 + size')
-- looper
repeatEvery t s f = do
_ <- timeout t $ do
f s
repeatEvery t (s + 5) f
return unit
at x y gfx = do
save
translate x y
gfx
restore
circle size = do
beginPath
arc { x: 0.0, y: 0.0, r: size, start: 0.0, end: Math.pi * 2.0 }
fill
clear = do
setFillStyle "#FFFFFF"
rect {x: 0.0, y: 0.0, h: 400.0, w: 400.0 }
fill
line x0 y0 x1 y1 = do
beginPath
moveTo x0 y0
lineTo x1 y1
stroke
```

There are three important formulas, determining the system state:

### Formula 1

The changing of $s'$ with $s$ is like

Thus, The changing of $x$ with $s$ is

### Formula 2

This does two things: fix one end to the circle, and another end rotate around the circle with a $\theta$ changing with $s$. And also, the length of $r$ will change.

### Formula 3

Two things: scaling and translating.