這是補充記錄關(guān)于CG的幾何變換的一些知識,涉及到簡單的矩陣變換
變換矩陣
在Core Graphics進行圖層縮放、旋轉(zhuǎn)、平移的時候,本質(zhì)的操作就是使用CGAffineTransform這個3x2矩陣對象,與我們的CGPoint這個1x2的矩陣(其實就是對應(yīng)就是[x,y]這個向量)進行矩陣相乘操作,得到的新矩陣就是變換后的新向量。一般通過CALayer得到的圖層都是矢量,因此可以把整個圖層進行相應(yīng)的縮放、旋轉(zhuǎn)、平移。
typedef struct CGAffineTransform {
CGFloat a;
CGFloat b;
CGFloat c;
CGFloat d;
CGFloat tx;
CGFloat ty;
} CGAffineTransform;
這個結(jié)構(gòu)體對應(yīng)的矩陣如下(看不到LaTeX公式的請看Apple Developer Document):
$ \begin{bmatrix} a & b & 0 \\ c & d & 0 \\ t_{x} & t_{y} & 1 \end{bmatrix} $
Apple采用了用[1]補齊1x3的向量,和用[0,0,1]的轉(zhuǎn)置補齊的3x3的變換矩陣相乘來做仿射變換。雖然可能覺理論上可以直接用2x3變換矩陣和3x1的向量([x,y,1]的轉(zhuǎn)置)運算,得到一個2x1的向量,省3個CGFloat的空間。但是由于這種變換操作疊加次數(shù)特別多,與其每次得到的向量結(jié)果再補齊[1],還不如一次性就用一個1x3和3x3運算,用空間換取時間,這也許是QuartzCore的實現(xiàn)者的考慮吧。
$ \begin{bmatrix} x & y & 1 \end{bmatrix} \times \begin{bmatrix} a & b & 0 \\ c & d & 0 \\ t_{x} & t_{y} & 1 \end{bmatrix} = \begin{bmatrix} ax+cy+t_{x} & bx+dy+t_{y} & 1 \end{bmatrix} $
注意:iOS上坐標(biāo)原點從屏幕左上方起,x軸指向右方,y軸指向下方。macOS的原點在屏幕左下方,x軸指向右方,y軸指向上方,要注意區(qū)別
平移
變換矩陣第三行的t_x和t_y對應(yīng)的就是x、y的平移量,因此矩陣變換很簡單,比如將[x,y]向量平移到[x+a,y+b]:
平移矩陣:
$ \begin{bmatrix} x & y & 1 \end{bmatrix} \times \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ a & b & 1 \end{bmatrix} = \begin{bmatrix} x+a & y+b & 1 \end{bmatrix} $
對應(yīng)API:
CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty);
縮放
縮放的本質(zhì),就是對向量[x,y]通過同時乘以相同的系數(shù)a,得到[ax,ay],那么矩陣很簡單,只需要一個對角矩陣,系數(shù)都為a就行
縮放矩陣:
$\begin{bmatrix} x & y & 1 \end{bmatrix} \times \begin{bmatrix} a & 0 & 0 \\ 0 & a & 0 \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} ax & by & 1 \end{bmatrix} $
這個可以使用CA的API來簡單構(gòu)造(也可以直接自己初始化):
CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy);
旋轉(zhuǎn)
旋轉(zhuǎn)的變換矩陣初看上去好像難以理解(各種cos、sin),其實就是一個簡單的解方程的出來的結(jié)果,注意這里需要的是弧度,而且iOS上旋轉(zhuǎn)的正弧度代表順時針(macOS上就是正弧度是順時針),需要注意
旋轉(zhuǎn)矩陣:
$ \begin{bmatrix} x & y & 1 \end{bmatrix} \times \begin{bmatrix} \cos \theta & \sin \theta & 0 \\ - \sin \theta & \cos \theta & 0 \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} x \cos \theta - y \sin \theta & x \sin \theta + y \cos \theta & 1 \end{bmatrix} $
推導(dǎo)過程:

$ P = (x,y) = (r\cos A , r\sin A) \\ P^{'} = (r \cos B, r \sin B) = (r\cos(A + \theta), r\sin(A + \theta)) \\ r\cos(A + \theta) = r\cos A \cos \theta - r\sin A \sin \theta = x \cos \theta - y \sin \theta \\ r\sin(A + \theta) = r\sin A \cos \theta + r\cos A \sin \theta = y \cos \theta + x \sin \theta \\ \therefore M = \begin{bmatrix} \cos \theta & \sin \theta & 0 \\ - \sin \theta & \cos \theta & 0 \\ 0 & 0 & 1 \end{bmatrix} $
對應(yīng)API:
CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle);
弧度可以用自帶的定義,比如M_PI_4這些,也可以手動轉(zhuǎn)換,比如用弧度、角度轉(zhuǎn)換的宏:
#define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)
#define DEGREES_TO_RADIANS(x) ((x)/180.0*M_PI)
錯切
錯切,就是一種特殊的線性變換(不平移),指的是某一個坐標(biāo)軸依賴不變,另一個軸線性變換,參考Wiki上的圖片:
錯切矩陣:
$ \begin{bmatrix} x & y & 1 \end{bmatrix} \times \begin{bmatrix} 1 & 0 & 0 \\ m & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} x+my & y & 1 \end{bmatrix} $
這種變換沒有提供專用的API,我們自己可以用CGAffineTransformIdentity創(chuàng)建一個矩陣,然后賦值矩陣中c或者b的值就行
疊加
既然了解了這些圖層操作的矩陣變換,我們也可以自己定義矩陣,比如非線性變換(得到的向量不平行)、也可以把幾個連續(xù)的變換串起來,這時候就要注意矩陣的運算順序,比如先縮放50%,再旋轉(zhuǎn)M_PI_4,再平移到[x+100,y],那么等價于沿著45度平移50的距離(對應(yīng)結(jié)果坐標(biāo)就成了[x*sqrt(2)/4, y*sqrt(2)/4])
對應(yīng)API:
CGAffineTransform CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2); // 最通用
CGAffineTransform CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy);
// 縮放
CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle); //旋轉(zhuǎn)
CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty); // 平移