詳細(xì)解析 CGAffineTransform 矩陣

CGAffineTransform 是對(duì)于仿射變換矩陣進(jìn)行了封裝赖临,而要理解仿射變換(affine transformation)先要理解線性變換(linear transformation)

線性變換

教材中的定義

設(shè)V, U 分別是n維和m維線性空間胞锰,T是一個(gè)從V到U的映射,如果T滿足
1.任意V中向量 α1兢榨、α2嗅榕,有T(α1 + α2) = T(α1)+T(α2)
2.任意V中向量 α,有T(λα) = λT(α)
那么顺饮,T就稱為從V到U的線性映射,或線性變換

簡(jiǎn)而言之線性變換就是把一個(gè)線性空間的向量映射到另一個(gè)線性空間去凌那,而且這個(gè)映射具備線性原則兼雄,即
1.兩個(gè)向量之和的映射 = 兩個(gè)向量映射的和
2.(向量 * 常數(shù)) 的映射 = (向量的映射) * 常數(shù)

根據(jù)這樣的定義可知線性變換的特點(diǎn)
1.直線經(jīng)過變換依然是直線
2.原點(diǎn)不變(T(0) = 0)
3.根據(jù)矩陣的性質(zhì),任何n維實(shí)數(shù)空間的線性變換都可以用矩陣來表示即 T(x) = Ax

二維空間內(nèi)的線性變換

當(dāng)V = U = 二維線性空間時(shí)帽蝶,就可以理解為二維空間內(nèi)部的坐標(biāo)變換

用矩陣表示為(采用與CGAffineTransform相同的矩陣赦肋,可能與教材給定的方式有差異,但本質(zhì)相同):
\begin{bmatrix} x'&y' \end{bmatrix} = \begin{bmatrix} x &y \end{bmatrix} * \begin{bmatrix} a & b\\ c & d \end{bmatrix}
列出對(duì)應(yīng)的方程組:
x' = ax + cy \\ y' = bx + dy

二維空間內(nèi)的線性變換可以實(shí)現(xiàn)很多效果

旋轉(zhuǎn)

\left[ \begin{matrix} x'&y' \end{matrix} \right] = \left[ \begin{matrix} x & y \end{matrix} \right] * \left[ \begin{matrix} cosφ & sinφ \\ -sinφ & cosφ \end{matrix} \right]

縮放

\left[ \begin{matrix} x'&y' \end{matrix} \right] = \left[ \begin{matrix} x & y \end{matrix} \right] * \left[ \begin{matrix} xScale & 0\\ 0 & yScale \end{matrix} \right]

對(duì)稱

\left[ \begin{matrix} x'&y' \end{matrix} \right] = \left[ \begin{matrix} x & y \end{matrix} \right] * \left[ \begin{matrix} a & b\\ c & d \end{matrix} \right]
1.當(dāng) a=1, d=-1, b=c=0時(shí)
沿x軸對(duì)稱

2.當(dāng) a=-1, d=1, b=c=0時(shí)
沿y軸對(duì)稱

3.當(dāng) a=-1,d=-1, b=c=0時(shí)
原點(diǎn)對(duì)稱

4.當(dāng) a=d=0, b=c=1時(shí)
沿 y=x 對(duì)稱

錯(cuò)切(平行四邊形化)

\left[ \begin{matrix} x'&y' \end{matrix} \right] = \left[ \begin{matrix} x & y \end{matrix} \right] * \left[ \begin{matrix} a & b\\ c & d \end{matrix} \right]
1.當(dāng) a=d=1, b=0, c為任意 時(shí)
沿x方向錯(cuò)切

2.當(dāng) a=d=1, c=0, b為任意 時(shí)
沿y方向錯(cuò)切

注:以上均可通過 let transform = CGAffineTransform(a: a, b: b, c: c, d: d, tx: 0, ty: 0)來實(shí)現(xiàn)

仿射變換

CGAffineTransform 除了 a,b,c,d 還包含tx励稳,ty佃乘。之所以二維空間的變換要使用三維矩陣,是因?yàn)槎S空間線性變換沒辦法實(shí)現(xiàn)平移的效果驹尼,需要借助三維空間的線性變換趣避,再映射到二維空間,形成二維空間的平移效果扶欣。

維基百科的動(dòng)態(tài)圖鹅巍,較好的展示了這一點(diǎn)


rotate.gif

CGAffineTransform

\left[ \begin{matrix} x'&y'& 1 \end{matrix} \right] = \left[ \begin{matrix} x & y & 1 \end{matrix} \right] * \left[ \begin{matrix} a & b & 0\\ c & d & 0\\ tx& ty& 1 \end{matrix} \right]
\begin{aligned} x' = ax + cy + tx \\ y' = bx + dy + ty \end{aligned}

a b c d tx ty 常用含義整理

由上可知 tx,ty只負(fù)責(zé)平移料祠,而a,b,c,d在不同操作下具有不同含義

只有旋轉(zhuǎn)時(shí)

a = d = cosφ
b = sinφ
c = -sinφ

只有縮放時(shí)

a: x方向縮放系數(shù)
d: y方向縮放系數(shù)

旋轉(zhuǎn)與縮放復(fù)合時(shí)

復(fù)合情況下,當(dāng)前transfrom中 a,b,c,d 是多個(gè)矩陣相乘后的結(jié)果

縮放系數(shù):行列式值 ---- 科普原因

即:

public func scale() -> CGFloat{
    let scale = sqrt(a*d - b*c)
    return scale
}

旋轉(zhuǎn)弧度:
因?yàn)閺?fù)合情況下a為 scale 與 cosφ 的乘積澎羞,所以可得:
\begin{aligned} cosφ = a / scale \\ φ = arccos(a / scale) \end{aligned}

/// -π ~ π
public func rotate() -> CGFloat{
     let scale = sqrt(a*d - b*c)
     if scale == 0 {
         return 0
     }
     var rotate = acos(a/scale)
     //由于rotate >= 0 && <= π, 當(dāng)旋轉(zhuǎn)超過π或?yàn)樨?fù)時(shí)髓绽,要加入符號(hào)判斷
     if (b < 0) {
         rotate = -rotate
     }
     return rotate
 }
    

transform, anchorPoint, frame

對(duì)transform進(jìn)行變換時(shí),frame會(huì)發(fā)生變化妆绞,但這種變化不是直接將frame這個(gè)長(zhǎng)方形內(nèi)的坐標(biāo)同時(shí)變換成新的frame顺呕,而是先在其 ****自身坐標(biāo)系**** 內(nèi)所有的點(diǎn)進(jìn)行transform變換,然后再更新到其frame的變化

而自身坐標(biāo)系的原點(diǎn)括饶,就是其anchorPoint株茶,因?yàn)閍nchorPoint默認(rèn)(0.5,0.5),也就是其中心點(diǎn)图焰。

縮放并改變anchorPoint后frame的變化

scale.gif

在這個(gè)transform進(jìn)行x, y 同時(shí)放大2倍的過程中启盛,其frame的變化

縮放前frame(137.5, 283.5, 100.0, 100.0)
縮放后frame(87.5, 233.5, 200.0, 200.0)

還是上面的view,現(xiàn)將其anchorPoint改為(0, 0)技羔,在對(duì)其進(jìn)行上面的x, y 放大2倍的過程僵闯,推算一下frame最終會(huì)變?yōu)槎嗌佟?/p>

frame的計(jì)算原理

frame.origin.x = position.x - anchorPoint.x * bounds.size.width

frame.origin.y = position.y - anchorPoint.y * bounds.size.height

1.ahchorPoint變?yōu)?(x: 0, y: 0)
2.anchorPoint改變不影響position,position = (x: 187.5, y:333.5)
3.由上述frame計(jì)算原理可得 frame = (187.5, 333.5, 100, 100)
4.因?yàn)槠渥陨碜鴺?biāo)系坐標(biāo)為(0,0,100,100),縮放后為(0,0,200,200), 轉(zhuǎn)化為frame = (187.5, 333.5, 200, 200)

運(yùn)行結(jié)果一致

縮放前frame(137.5, 283.5, 100.0, 100.0)
修改anchorPoint為0后的frame(187.5, 333.5, 100.0, 100.0)
縮放后frame(187.5, 333.5, 200.0, 200.0)

不改變anchorPoint而修改旋轉(zhuǎn)中心點(diǎn)的方式

在對(duì)transform進(jìn)行旋轉(zhuǎn)或縮放等操作時(shí)藤滥,修改anchorPoing可以修改旋轉(zhuǎn)或縮放的中心鳖粟,這是我們常用的操作,但是anchorPoint的修改直接會(huì)導(dǎo)致元素位置變化拙绊,所以做完transform變化后通常還要恢復(fù)anchorPoint向图。

要想transform中心點(diǎn)變化還不改變anchorPoint泳秀,可以通過先平移后旋轉(zhuǎn)從而達(dá)到和修改anchorPoint一樣的效果

/// 生成繞任意點(diǎn)旋轉(zhuǎn)的transfrom
/// - Parameters:
///   - radian: 旋轉(zhuǎn)弧度
///   - circleCenter: 旋轉(zhuǎn)中心
///   - viewCenter: 被旋轉(zhuǎn)view的中心
private func rotateTransform(radian:CGFloat, circleCenter:CGPoint, viewCenter: CGPoint) -> CGAffineTransform {
        /// 轉(zhuǎn)化為將旋轉(zhuǎn)的view 自身的坐標(biāo)系
        let x = circleCenter.x - viewCenter.x
        let y = circleCenter.y - viewCenter.y
        
        let transform = CGAffineTransform(a: cos(radian), b: sin(radian), c: -sin(radian), d: cos(radian), tx: x - x*cos(radian)+y*sin(radian), ty: y-x*sin(radian)-y*cos(radian))
        return transform
}

具體效果


rotate.gif

重點(diǎn)整理

  1. 二維線性變換能實(shí)現(xiàn)很多效果,如縮放榄攀,旋轉(zhuǎn)等晶默,這些變換使用二維矩陣就夠了,而CGAffineTransfrom使用三維矩陣的原因是航攒,二維空間下的線性變換無法實(shí)現(xiàn)平移磺陡,需要從三維的線性變換來實(shí)現(xiàn)二維的平移
  2. 仿射變換都是根據(jù)自身坐標(biāo)系來進(jìn)行,這個(gè)自身坐標(biāo)系以anchorPoint所指向的點(diǎn)為(0漠畜,0)币他,而不是直接使用和其frame一樣的坐標(biāo)系
  3. 靈活應(yīng)用 a,b,c,d,tx,ty
  4. 了解frame的計(jì)算方式,以及transform如何影響frame
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末憔狞,一起剝皮案震驚了整個(gè)濱河市蝴悉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瘾敢,老刑警劉巖拍冠,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異簇抵,居然都是意外死亡庆杜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門碟摆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晃财,“玉大人,你說我怎么就攤上這事典蜕《鲜ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵愉舔,是天一觀的道長(zhǎng)钢猛。 經(jīng)常有香客問我,道長(zhǎng)轩缤,這世上最難降的妖魔是什么命迈? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮典奉,結(jié)果婚禮上躺翻,老公的妹妹穿的比我還像新娘。我一直安慰自己卫玖,他們只是感情好公你,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著假瞬,像睡著了一般陕靠。 火紅的嫁衣襯著肌膚如雪迂尝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天剪芥,我揣著相機(jī)與錄音垄开,去河邊找鬼。 笑死税肪,一個(gè)胖子當(dāng)著我的面吹牛溉躲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播益兄,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锻梳,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了净捅?” 一聲冷哼從身側(cè)響起疑枯,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛔六,沒想到半個(gè)月后荆永,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡国章,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年具钥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捉腥。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡氓拼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抵碟,到底是詐尸還是另有隱情,我是刑警寧澤坏匪,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布拟逮,位于F島的核電站,受9級(jí)特大地震影響适滓,放射性物質(zhì)發(fā)生泄漏敦迄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一凭迹、第九天 我趴在偏房一處隱蔽的房頂上張望罚屋。 院中可真熱鬧,春花似錦嗅绸、人聲如沸脾猛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猛拴。三九已至羹铅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間愉昆,已是汗流浹背职员。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跛溉,地道東北人焊切。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像芳室,于是被迫代替她去往敵國和親专肪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355