2D平面中關於矩陣(Matrix)跟圖形變換的講解

在二維平面上,常用的有以下三種基本的圖形變化:

1)Translation

2)Scale

3)Rotation

在Android的開發中,我們也經常會用到這樣的一些圖形變換,尤其是我們在寫自定義View時,更是會經常利用到Matrix來實現一些效果,比如平移,旋轉,縮放及切變等,相信很多朋友應該很想知道,矩陣實現這種變換的原理是什麼,什麼是矩陣的左乘右乘,它們在實現效果上有什麼差別嗎?今天就讓我們一起來看一下吧。

都是由點組成的

平面上的元素,就是點,線,面,而線就是由一個個點組成的,而是由一條條線組成的,所以歸根結底,平面上所有的圖形都是由點組成的。而在我們座標系中,一個點就是由一對x,y值組成的,p = {x, y}。而在平面上,過兩點間的,我們可以畫一條直線,所以我們一般通過 p1, p2可定義一條直線,e = {p1, p2},而圖形呢,則是由衆多的點和點之間的的線段組成的。所以,其實平面上的圖形變換,就是點座標位置的變換。
在平面上,一個點,可以通過一個向量或者矩陣來表示:

而下面這條紅色的直線則是由一組組的點組成的,起始點是(120,0),終點是(240,120)。

Translation(平移)

如果我們現在要平移這條直線,向右120(tx),向下120(ty),那麼新的點會是怎麼樣呢?很顯然,起始點就會是(240,120),而終點就會是(360,240),效果如下:

綠色的線就是平移後的線了,可以看出每一個新的點的值是

這樣的一個變換translation也可以用一對值來表示,t = {tx, ty},其中tx是在x座標上的偏移量,而ty是在y座標上的偏移量。移動點 p 到 p',我們只要加上這個偏移就行,如果用矩陣或者向量來表示就是:

(可能會有朋友覺得奇怪,怎麼是加呢,Android裏面矩陣不都是乘嗎?這是什麼原因呢?)

Scale(縮放)

那如果我們對這條直線進行放大呢,比如放大2倍呢,一般來講,我們縮放,是指所有維度的縮放,當然在這裏就只有x座標跟y座標,當然也可以只針對一個維度,但是就會變形了哦。我們先看一下放大到2倍的效果和只放大列的效果吧。
          
很顯然,兩邊都放大的話,起始點由(120,0)變成(240,0),終點由(240,120)變成(480,240)。而如果只放大列的話,起始點的座標是不變的。而且我們可以看到,放大的時候,線也跟着向右平移了一個單位,爲什麼會這樣呢?這是因爲縮放是基於原點(0,0)的,在Android中,也就是屏幕的左上角,但縮放的位數大於1的時候,就會遠離原點,而相反,當縮放的位數小於1時,則會趨近原點。
縮放的變換是由下面的矩陣來表示的:

那麼縮放後的直線的點就是:

各位朋友,可以想一下,這樣直接Scale的話,這個圖形可是會平移的哦,如果不想要平移,應該怎麼辦?

Rotation(旋轉)

我們再看一下下圖,這條直線順時針旋轉了45度,也就是往逆時針方向旋轉了 - 45 度,


那麼新的點是怎麼算出來的呢?
逆時針(注意這裏)旋轉的矩陣表示是:

同樣的,旋轉後的點就是根據下面的矩陣相乘而得出來的結果:

我們可以將我們的點代進去求解,可得新的起始點P0'爲(84.85,84.85),而新的結束點爲:(84.85,254.56),可看出,剛好是上面綠色線所在的地方。

Combine Transformation (組合變換)

對於Scale 和 Rotation 來說,它們都是基於原點(0,0)的變換,那如果我們要讓它基於某個點縮放或者旋轉,就比如繞着起始點轉呢,而這也經常是我們想要的一種效果。這個點就是所謂的旋轉,而解決辦法其實就是將圖形先平移到原點,再進行縮放或者旋轉的變化,然後再移回來,就可以了。
假設這三種變換的矩陣表示如下:


那麼它應該實施的變換就如下:先平移 T 到原點,再基於原點進行縮放(或者旋轉),然後再平移回去,


其實這一步,我們可以在Canvas的代碼中看到的,如下:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * Preconcat the current matrix with the specified scale. 
  3.  * 
  4.  * @param sx The amount to scale in X 
  5.  * @param sy The amount to scale in Y 
  6.  * @param px The x-coord for the pivot point (unchanged by the scale) 
  7.  * @param py The y-coord for the pivot point (unchanged by the scale) 
  8.  */  
  9. public final void scale(float sx, float sy, float px, float py) {  
  10.     translate(px, py);  
  11.     scale(sx, sy);  
  12.     translate(-px, -py);  
  13. }  
上面代碼中的軸點的實現,其實就是對於平移的來回操作,至於爲什麼是translate(px,py)在前,而translate(-px,py)在後呢,這涉及到矩陣左乘和右乘的計算,後面我們會談到的。

Homogeneous Coordinates(齊次座標)

在上面的矩陣中,我們可以看到平移的矩陣是相加的,而旋轉跟縮放的矩陣都是相乘的,這樣計算起來多麻煩呀!於是爲了方便計算,大家都統一用一種方式來進行計算,聰明的計算機圖形科學家,它們就設計出這樣一種座標系,叫homogeneous coordinates,而它的目的只是爲了更加方便地去用矩陣來計算圖形的變換,沒有其他。
那什麼是齊次座標呢?
其實就是在原來2D的維度,再加上一個新的維度,多出來的維度的值永遠是1,比如點的矩陣就變成:

而Translation(平移)的矩陣表示就變成:

這樣,平移變換的加法就可以變成乘法:

而Scale(縮放)跟Rotation(旋轉)相對應的矩陣也就變成:

看到這幾個,大家發現了沒有?沒錯,這幾個就是我們Android中用到的矩陣了。

當我們在Canvas上用Scale的時候,其實就是乘上S矩陣,當我們用Rotate的時候,其實就是乘上R矩陣,大家明白沒有?
關於矩陣左乘右乘,前乘後乘的關係,如果有困惑的朋友,可以看一下下面這篇文章:

引用自:http://blog.csdn.net/linmiansheng/article/details/18801947
發佈了40 篇原創文章 · 獲贊 11 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章