1. 程式人生 > >理解FLASH 8中的矩陣變換

理解FLASH 8中的矩陣變換

以下是譯文。

簡介:

Flash 8在Flash開發中,給Flash開發者帶來了一個新的,令人興奮的高度。現在開發者不僅僅能在Flash工具中中流暢的操作bitmaps,還能通過圖片的變換矩陣,完整的控制圖片的轉換。這在Flash早期的版本中是不可能的。因此在早期,就可以實現特定的變換矩陣,例如矩陣的切變。如果你用ActionScript去實現的話,會經過複雜的數學包括巢狀的圖片。而就當前的改進而言,這些都已經是歷史了。

矩陣與矩陣變換:

在瞭解矩陣是如何變換之前,我們先理解什麼是矩陣。一個矩陣是一個由幾行幾列數字組成的矩形陣列(或者表)。一個由m行、n列組成的矩陣就是所知的m x n矩陣。這代表了矩陣的維度。常見的矩陣都是在行與列中包含一些數字,並且被兩個巨大的中括號包起來。


矩陣有許多不同的用途,通常用來儲存資料或者用特定的矩陣計算解決問題。Flash用矩陣定義仿射變換。

仿射變換就是在變換座標系中,保持共線性和相對距離的變換。這就意味著在座標系空間上,假如原先是在一條線上的點,經過仿射變換,還將繼續在一條線上。同時,在平行線上的點繼續是平行線,通過縮放,那些相對的空間和距離會一直保持在一個特定的比例。仿射變換允許重新定位,縮放,切變,旋轉。不過,他們不能做椎體,扭曲,透視。如果你曾經做過Flash裡面的符號變換,你應該會記得仿射變換的這些性質。


在Flash中的變換矩陣,用3行3列的3X3矩陣定義了這些放射變換。在矩陣中值的位置被標明為a、b、c、d、tx、ty、u、v還有w。在矩陣中,他們被命名為元素。


這些a、b、c、d、tx還有ty是矩陣變換中最重要的元素。其他的位置,u、v還有w雖然不重要,但必須存在在矩陣中。在Flash中,a、b、c、d、tx還有ty都對應一中特定的變換。

a - x軸 縮放 

b - y軸 切變 

c - x軸 切變 

d - y軸 縮放 

tx - x軸 平移 

ty - y軸 平移  

而u、v還有w的位置的值,將分別以的靜態的值0、0、1保留下來。


 注意:

在Flash的矩陣類中,u、v、w是不會像a、b、c、d、tx、ty元素那樣可以修改的。

這些在以上觀點中,a、b、c、d、tx、ty作為有效的元素或者屬性。他們會在矩陣變換的公式中作為引數,求解新的x、y的值,這些變換公式如下:

x' = x*a + y*c + tx 
y' = x*b + y*d + ty

x和y代表原始的x和y所處的位置,而x'和y'(讀成x-素和y-素)代表變換後的位置。這些公式是用來求解在圖片或者圖片中任意一個與矩陣變換相關聯的點的新座標位置的。

注意:

你也許會看到變換矩陣表示為:


或者類似的(注意u、v、w屬性在底下,與右邊相反)。這也是基礎的相同矩陣,只是用了不同的元素朝向罷了。雖然,一些操作運算會因為這個不同採用不同的處理,但是變換在朝向上不會隨這些不同而改變。最終的結果依然保持相同。

 在這篇文章書寫的時候,Flash幫助文件,在這種方式下用c和b的調換,不正確的表示了一個矩陣的變換。

單位矩陣:

當一個矩陣運算的結果沒有導致變換,那麼這就是眾所周知的單位矩陣。


如果,任意一個正方形矩陣 (行數與列數相等的矩陣叫正方形矩陣)從左上角到右下角的對角線上,值都是1(行座標值與列座標值相同的點),而非此對角線上的值都是0,這樣的正方形矩陣稱為單位矩陣。試想一下,假如用單位矩陣計算一個矩陣變換值的結果。

x' = x*1 + y*0 + 0
y' = x*0 + y*1 + 0

上面可以簡化為:

x' = x

y' = y

發現沒有,經過矩陣的計算,x和y的值沒有發生變化。x'等於x,y'等於y。你可以把單位矩陣想象成一個數值上為1來縮放的矩陣的矩陣,或者常規的數值1。當你用1乘以任意數值,你會得到原來數值。相同的規則適用於單位矩陣變換。在Flash中,除了特定的,剩下的所有矩陣,都以單位矩陣開始--即,沒有變換的矩陣。這包括參與圖片的矩陣。如果一個圖片最終沒有改變或者變換,那麼它的變換矩陣就是單位矩陣。

應用變換:

試想一下,假如一個單位矩陣的元素髮生了變化。例如,Flash變換矩陣中,在一個以單位矩陣為開始的矩陣中,其中一個元素的值由1變為2,那麼會發生什麼呢?記住,單獨的x縮放元素


x' = 2*x + 0*y + 0
y' = 0*x + 1*y + 0

x' = 2x
y' = y


你現在情況變成了,新的x等於舊的x的兩倍;x' = 2x。經過以上的矩陣變換後,一個圖片的寬度將是原來的兩倍。


那假如,將b的值改為1會如何呢?


x' = 1*x + 1*y + 0 
y' = 0*x + 1*y + 0

x' = x + y 
y' = y


現在,你的新的x'不僅與舊x有關,還與y值有關,這就意味著,當y值變大,新的x'值也將跟著變大。這就導致x軸的切變由元素c來控制。

假如你想在任意一個方向上平移這個圖片,你只要改變矩陣中tx和ty的值就可以了。


x' = 1*x + 1*y + 5 
y' = 0*x + 1*y + 10

x' = x + y + 5
y' = y + 10

變換的結果依然是切變。但是x和y的值相對原來的移動了5和10。

你也許會發現在矩陣中沒有元素用來表示旋轉。事實上,旋轉混合縮放和切變的結果。通過縮放和切變的共同作用,可以扭曲座標系,來達到旋轉的效果。

在最簡單的情況下,任何一個單一的旋轉變換都可以用以下的矩陣來表示:


角度值表示變換中將要旋轉的大小。

就變換矩陣而言,最難理解的就是旋轉的概念。不僅僅是它不像縮放、切變那樣有元素來表示,而且是旋轉需要依賴四個其他的元素(縮放和切變)來實現。旋轉的內部就已經很難理解了,更不用說在旋轉之上改變那些元素的值,或者以旋轉為基礎的元素的值。如果,你非常熟悉二維空間的三角函式和旋轉向量,那麼接下來,你就會看到頻繁的使用sine和cosine函數了。這些概念也使用。

假設有一個簡單的例子,用於矩陣變換,它要求旋轉30度。


x' = cosine(30)*x - sine(30)*y + 0
y' = sine(30)*x + cosine(30)*y + 0

x' = .87*x - .5*y
y' = .5*x + .87*y

假設已知點(4,0),旋轉30度,大致的x、y值應當是(3.5,2)。


向公式傳入 x = 4,y = 0:

x' = .87*4 - .5*0
y' = .5*4 + .87*0

x' = 3.46
y' = 2

下面體驗一下圖片的變換矩陣,看看變換是如何表現的。

矩陣演示

ActionScript的矩陣類:

ActionScript中的矩陣是存在Flash Matrix類的一個例項當中。這個類位於flash.geom 包中。假如你要建立你自己的矩陣類,你需要使用它的完全路徑。

var my_matrix = new flash.geom.Matrix();
或者,你先引入該類,這樣就可以直接使用“new Matrix”了
import flash.geom.Matrix;

var my_matrix = new Matrix();

矩陣的建構函式允許你在建立一個矩陣例項時,指定a、b、c、d、tx、ty的值。

import flash.geom.Matrix;

var my_matrix = new Matrix( a, b, c, d, tx, ty );

如果,沒有指定引數,你就會得到一個單位矩陣。

矩陣中最主要的就是a、b、c、d、tx、ty。這些都是你在做矩陣變換時需要用到的屬性。

Flash還提供了一些其他的函式來簡化你的工作。其中一些常用的函式如下:

translate(tx:Number, ty:Number) : Void
scale(sx:Number, sy:Number) : Void
rotate(angle:Number) : Void
identity() : Void

在Flash幫助文件中還能找到矩陣類其他函式的說明和資訊。因為切變不是典型的矩陣變換函式,所以你不一定能在矩陣類中找到函式skew()。可以通過調整矩陣中元素b和c,來生成一個切變函式。

以上提到的任意一個方法都是用來操作修改矩陣的;他們不會返回一個新的變換矩陣。這是一個基本概念。

矩陣平移

translate(tx:Number, ty:Number) : Void

到的任意一,移動的方法只是用tx和ty來移動位置。通過做加法運算,傳入矩陣中的tx和ty,加到任意一個已經存在的值上。

my_matrix.translate( 5, 10 );
與以下相同:
my_matrix.tx += 5;
my_matrix.ty += 10;

矩陣縮放

scale(sx:Number, sy:Number) : Void

函式縮放,通過sx的大小縮放變換矩陣中的x,通過sy的大小縮放變換矩陣中的y值。這些值是做乘法,所以數值1表示不縮放。用來縮放的值同樣會改變矩陣中tx和ty的值,從而改變矩陣的位置。旋轉與縮放的原理是一樣的。

my_matrix.scale( 1.5, 2 );
與一下相同:
my_matrix.a *= 1.5;
my_matrix.b *= 2;
my_matrix.c *= 1.5;
my_matrix.d *= 2;
my_matrix.tx *= 1.5;
my_matrix.ty *= 2;

矩陣旋轉

rotate(angle:Number) : Void

函式旋轉,通過指定角度的大小來旋轉一個變換矩陣,引數角度指的是弧度值。相對於縮放,旋轉不僅僅要修改元素a、b、c、d的值,還要修改tx和ty的值。你應該猜到,在旋轉上,數學公式會更復雜一些。

my_matrix.rotate( Math.PI/4 );
與一下相同:
var sin = Math.sin( Math.PI/4 );
var cos = Math.cos( Math.PI/4 );
var a = my_matrix.a;
var b = my_matrix.b;
var c = my_matrix.c;
var d = my_matrix.d;
var tx = my_matrix.tx;
var ty = my_matrix.ty;
my_matrix.a = a*cos - b*sin;
my_matrix.b = a*sin + b*cos;
my_matrix.c = c*cos - d*sin;
my_matrix.d = c*sin + d*cos;
my_matrix.tx = tx*cos - ty*sin;
my_matrix.ty = tx*sin + ty*cos;
單位矩陣

identity() : Void

不管之前的變換,單位矩陣方法會將一個矩陣轉化為單位矩陣。本質上而言,呼叫該方法會清除矩陣中所有的變換。

my_matrix.identity();
與一下相同:
my_matrix.a = 1;
my_matrix.b = 0;
my_matrix.c = 0;
my_matrix.d = 1;
my_matrix.tx = 0;
my_matrix.ty = 0;

動畫裁剪矩陣

當做動畫裁剪的時候,你要呼叫變換矩陣中的transform物件的matrix屬性,來達到動畫裁剪的目的。

var my_matrix = my_mc.transform.matrix;

這個矩陣實際上是拷貝了真正的矩陣,用作動畫裁剪。這就意味著,假如你嘗試修改直接修改矩陣的元素,他們將不會在動畫裁剪的矩陣上反應出來。正確的方法是,你操作拷貝的矩陣,然後重新賦值到真正的transform物件的matrix屬性上,來實現裁剪。

my_mc.transform.matrix.translate(5, 0); // no change

var my_matrix = my_mc.transform.matrix;
my_matrix.translate(5, 0);
my_mc.transform.matrix = my_matrix; // changes my_mc's position

體驗一下下面的動畫裁剪,看看呼叫動畫裁剪的變換矩陣,來操作矩陣會有什麼效果。

矩陣裁剪

 :

和其他的Flash8的類包一樣。Matrix累也有方法clone(),用來方便你拷貝一個已經存在的矩陣。從動畫裁剪的變換物件訪問矩陣,是可以自動拷貝該矩陣的。

當一個動畫裁剪的子動畫裁剪有矩陣變換,並且父動畫裁剪也有矩陣變換時。子動畫裁剪的可視結果,將是他自己的矩陣變化和父矩陣變化的混合。

矩陣乘法

矩陣更像一堆混合的數字。你可以做一些基礎的數學運算,例如加法、以及乘法。當處理矩陣變換時,乘法是尤為重要的一個運算。矩陣乘法是將定義在矩陣中的變換,應用起來。事實上,之前公式中提到的從矩陣中得到的x'和y'是,是點或者行向量的做矩陣乘法的結果。

一個行向量是一個 1 x n的矩陣,一行,n個值的矩陣。一個向量常代表一個二維或三位空間中的點。對於我們而言,一個向量就是一個由x、y和數字1組成的1 x 3的矩陣,代表二維空間中的一個點。


雖然,x、y值才是向量中重要的數值。數字1仍然像u、v、w一樣,必須存在在變換矩陣中。

這樣一個向量可以與一個變換矩陣相乘,讓變換作用在一個已經提供的點上。然而,就矩陣相乘而言,它要求第一個做乘法的矩陣的列數必須與第二個做乘法的矩陣的行數相同。因為向量現在是1 x 3並且變換矩陣是3 x 3,這對於等式左邊的行向量,它是滿足以上條件的。


這個規定,是由矩陣相乘的法則決定的。在矩陣乘法中,最終的矩陣是一個m x n矩陣,m是第一個矩陣的行數,n是第二個矩陣的列數。這個規則同樣讓順序變得重要了。例如,給出A和B矩陣,A * B 不一定等於 B * A。在一些情況下,這些情況是存在的,你會在後面看到這些情況。

結果矩陣中的每一個新值,都是對應的第一個矩陣的列值和第二個矩陣的行值做積,之後再做和的結果。例如,給出一個矩陣 * 向量,結果向量中第一個值會是:


x*a + y*c + 1*tx

每一列都做類似的運算,每一列可以表示為:


or


你在結果中發現沒有?與之前類似計算 x' 和 y' 相同的公式.

x' = x*a + y*c + tx
y' = x*b + y*d + ty

注意:

當用一個修改朝向的矩陣時,在乘法運算上會稍有不同。不是使用行向量 * 矩陣,你會用 矩陣 * 列向量。 一個列向量就如同一個行向量,只是在書寫上,列向量是m x 1的矩陣的形式。


運算最終的結果是一樣的,只是它最終以列向量存在,而不是行向量。

向量一般用在矩陣乘法中,來求解做變換的點的新座標。但,其他矩陣也會與矩陣做乘法,來創造一個新的變換矩陣,這個矩陣包含了被乘的矩陣的變換。

假設兩個矩陣做乘法。


因為第一個矩陣的列數等於第二個矩陣的行數,所以可以做矩陣乘法。在這裡也用了矩陣與向量相乘相同的方法,只是,這裡還要計算第二個矩陣中其他的列數。


在每一行每一列做完乘法和加法之後,你會得到下面這個:


可以簡化為:


你會發現,這與之前的3 x 3矩陣的風格是一樣的,u、v、w的值是0,0,1.

我們現在可以將這個定理用在相同的例子上。這裡有兩個變換矩陣,一個縮放x為百分之兩百,一個切變元素y為1。




在最終的變換矩陣中,切變元素相乘得到了2。

現在讓我們見證一下,交換相乘矩陣的位置。你覺得結果會和上面一樣嗎?




事實上結果是不相同的。縮放不再改變切變的值,並且,僅僅是將其包含在內。

註釋:

記住,任意一個矩陣與單位矩陣相乘,將會得到原來的矩陣。

不用擔心太多的矩陣相乘會改變太多。你不一定需要知道矩陣是如何相乘的。事實上,Flash的矩陣類,已經向開發者提供了這些操作函式。既有點(方法transformPoint())也有矩陣(方法concat())的。也許,你根本不需要使用以上的矩陣乘法。不過,知道矩陣乘法是如何運作的,可以幫助開發者有一個更好的全域性瞭解。

import flash.geom.Matrix;

var scale_matrix, skew_matrix;

scale_matrix = new Matrix(2, 0, 0, 1, 0, 0);
skew_matrix = new Matrix(1, 0, 1, 1, 0, 0);
skew_matrix.concat(scale_matrix);
trace(skew_matrix); // (a=2, b=0, c=2, d=1, tx=0, ty=0)

scale_matrix = new Matrix(2, 0, 0, 1, 0, 0);
skew_matrix = new Matrix(1, 0, 1, 1, 0, 0);
scale_matrix.concat(skew_matrix);
trace(scale_matrix); // (a=2, b=0, c=1, d=1, tx=0, ty=0)
例如,平移,縮放,旋轉和單位,連線同樣會改變當前的矩陣例項,而不是返回一個新的矩陣。

對於Flash的點例項,可以用矩陣的方法transformPoint()與行向量相乘

import flash.geom.Matrix;
import flash.geom.Point;

var scale_matrix, original_point, scaled_point;

scale_matrix = new Matrix(2, 0, 0, 1, 0, 0);
original_point = new Point(10, 10);
scaled_point = scale_matrix.transformPoint(original_point);
trace(scaled_point); // (x=20, y=10)
注意,通過方法transformPoint(),得到了一個新的變換點。同樣要注意的是,在Flash中使用方法concat()和方法transformPoint()做乘法的書寫的順序,與矩陣做相乘時是相反的。例如,給出矩陣A、B和點P:

A*B -> B.concat(A);
P*A -> A.transformPoint(P);

就父子圖片的矩陣相乘,你可以這樣:

Child.concat(Parent);

級聯矩陣

假如,我們有兩個圖片,第一個是第二個的子變換。首先第一個,子圖片,切變元素x為1。然後第二個,父圖片,縮放x元素200%。子變換矩陣的最後時間線表現結果是既有縮放也有切變的圖片。也可以用一個簡單的剪下來實現,不用巢狀一個父矩陣變換。這樣一個剪下的矩陣變換,等同於一個原始的子圖片的變換矩陣乘以父圖片的矩陣變換。


父圖片的變換矩陣,是為了矩陣乘法,在以後改變它的子圖片的變換的。記住,真實的與子圖片關聯的矩陣,是不包含這些變化的。真實矩陣,相對於圖片,包括直接影響真實矩陣的變換,都是特別的。對於任意的父圖片矩陣,或者其他的父圖片矩陣都不會被涵蓋在這裡。然後,Flash在圖片的所有父類都提供了的transform物件裡面,以一個矩陣屬性的形式,提供了所有的變換。這就是concatenatedMatrix屬性。它表示作用於巢狀圖片的,所有相關矩陣乘積的結果。

var parent_matrix = parent_mc.transform.matrix;
var child_matrix = parent_mc.child_mc.transform.matrix;
var childconcat_matrix = parent_mc.child_mc.transform.concatenatedMatrix;

child_matrix.concat(parent_matrix);

trace(child_matrix); // (a=2, b=0, c=2, d=1, tx=0, ty=0)
trace(childconcat_matrix); // (a=2, b=0, c=2, d=1, tx=0, ty=0)
你可以把concatenatedMatrix屬性對於矩陣,看成globalToLocal對於點。它提供了匹配目標矩陣的級聯變換的變換。使用一個級聯矩陣,你可以在主時間線上改變一個圖片,來匹配父圖片有多種變換的巢狀圖片的變換。

逆矩陣

你也可以移除在巢狀圖片中所有的變換,讓它看起來像似一點也沒有變換過。然而,唯獨連線矩陣,是沒有提供這樣的功能的。你就需要一個逆矩陣了。

存在一個獨特的正方形矩陣,並且與當前矩陣的乘積為單位矩陣,就稱該矩陣為逆矩陣。這兩個矩陣都被看成可逆的,並且是彼此的逆矩陣。所有的Flash變換矩陣都是可逆的,這就是說,存在另外一個矩陣與這些矩陣的積為單位矩陣。假設,單位矩陣 I 和 矩陣 A,存在一個矩陣B,那麼:

A * B = I

並且

B * A = I

手動計算一個逆矩陣是比較複雜的。它涉及找出矩陣的共軛矩陣,除以共軛矩陣的行列式。用我們的仿射變換矩陣處理起來是比較容易的,但是具體的過程,就已經高於當前的教程了。它可以簡化為。

假設矩陣:


它的逆矩陣可以通過以下來找到:


通過矩陣的乘法,還有方法concat(),Flash中的Matrix類,可以更加輕鬆的找到逆矩陣。用方法invert(),你可以非常輕鬆的轉化一個矩陣為它的逆矩陣。

import flash.geom.Matrix;

var my_matrix = new Matrix(2,3,5,7,2,4);
trace(my_matrix); // (a=2, b=3, c=5, d=7, tx=2, ty=4)

my_matrix_inverse = my_matrix.clone();
my_matrix_inverse.invert();
trace(my_matrix_inverse); // (a=-7, b=3, c=5, d=-2, tx=-6, ty=2)

my_matrix_inverse.concat(my_matrix);
trace(my_matrix_inverse); // (a=1, b=0, c=0, d=1, tx=0, ty=0)
以上可以看出,原始矩陣與逆矩陣相乘的結果是單位矩陣。

一個圖片使用逆矩陣,可以用級聯矩陣的逆乘以當前矩陣,移除來自所有父圖片的變換。  

var concat_matrix = parent_mc.child_mc.transform.concatenatedMatrix;
concat_matrix.invert();
concat_matrix.concat(parent_mc.child_mc.transform.matrix);
parent_mc.child_mc.transform.matrix = concat_matrix;

使用矩陣變換

既然已經說了這麼多矩陣變換,現在是時候來說說如何使用它們了。現在只說Flash 8,通過ActionScript,我們可以非常輕鬆的在IDE裡面修改圖片(集成了開發環境),這都歸功於矩陣變換。

首先,從我們所知道的開始。基礎的矩陣變換的方法有,平移,縮放,旋轉。如果你已經忘記了,這裡是沒有切變的。要不要改改這個?

為了實現一個與其他相似的自定義切變方法。你需要考慮到,這個切變也許會用在其他變換的之上,包括平移。假設當前任意一個存在變換的矩陣中,如果你嘗試去設定這個非單位矩陣中的切變屬性(b 和 c)的值,來做一個簡單的切變,是有可能破壞矩陣原有的變換的。幸運的是,矩陣乘法,是一個將變換,作用在已經存的變換的矩陣上的過程。這就是說,新增一個切變,就是將一個單位切變矩陣,乘以當前存在的矩陣罷了。

function skew(matrix, x, y){
	matrix.concat( new flash.geom.Matrix(1, y, x, 1, 0, 0) );
}

這個簡單的切變,傳給了方法concat一個新的單位矩陣,並且矩陣的c和b的值設定成了x和y。結果是,原來的矩陣,乘以了新的切變矩陣,並且切變不會影響原來的矩陣的變換。像平移,縮放,旋轉。這個切變函式,同樣會改變當前的矩陣的平移元素。這裡唯一不同的的是,因為這個切變方法不是Flash的Matrix類的一部分,用起這個方法的時候,目標矩陣會當成引數傳入。

skew(my_matrix, 1, 0);

有一件事你會注意到,比如方法scale(並且這個新方法skew),是不以當前變換矩陣的為基準的那種變換。它們已經考慮到了之前的已經存在的變換,但他們的縮放和切變,不是以那些變換為基礎的。更加清晰的表達我所說的情況是,例如,旋轉一個圖片45度。當考慮到對這個圖片做一次縮放,概念上,會有兩種結果;要麼好像沒有以旋轉為基礎的旋轉,要麼以旋轉為基礎的縮放。


用縮放函式,和Matrix類其他方法,你可以在那個旋轉的基礎上,就像看到的第一種變化那樣,沒有旋轉過,不像看到的第二個那種。假如你想旋轉的同時做矩陣縮放,像上面看到的第二個那樣,那麼你實際會看到先縮放,再旋轉。就父-子關係考慮一下這種情況,當子動畫縮放時,其實也跟著父矩陣的旋轉而旋轉的。這個與函式sacle()的縮放是相反的,函式sacle()更像父矩陣來縮放一個已經旋轉過的子矩陣。矩陣變換的方法,像似父變換新增到已經存在的子矩陣上。為了獲得相反的效果,你要做的就是用矩陣乘法,不過要改變相乘時的順序。

對於這樣的交換,要以方法的變換開始,然後在這之上用原始的相乘。對於剛開始的矩陣,只要在一個單位矩陣上,用需要的函式就行了。經過這些,我們可以建立一個新方法,來影響矩陣“內部”的變換。

function innerScale (matrix, x, y){
	var scale_matrix = new flash.geom.Matrix();
	scale_matrix.scale(x, y);
	scale_matrix.concat(matrix);
	return scale_matrix;
}

function innerSkew (matrix, x, y){
	var skew_matrix = new flash.geom.Matrix();
	// assume skew function for matrices is defined
	skew(skew_matrix, x, y);
	skew.concat(matrix);
	return skew_matrix;
}

function innerRotate (matrix, angle){
	var rotate_matrix = new flash.geom.Matrix();
	rotate_matrix.rotate(angle);
	rotate_matrix.concat(matrix);
	return rotate_matrix;
}

請注意,這些方法,事實上會返回一個新矩陣,而不是改變原有矩陣。這些例子的目的,比上文會簡單一些。以下是一個運用在圖片上的例子。

var my_matrix = my_mc.transform.matrix;
my_matrix = innerScale(my_matrix, 2, 1);
my_mc.transform.matrix = my_matrix;

在用這些變換是,你也許會注意到,平移不受影響。這是因為基礎的變換實際上沒有平移,所以,無論在原有矩陣上如何使用變換,最終矩陣的平移元素還是會返回原來的值。

然而,假如你也不想原始矩陣的平移元素受到改變?Flash已經提供了一個方法,在移動點的時候忽略平移元素的改變。就是方法deltaTransformPoint(),而且它就像方法transformPoint一樣,除了transformPoint在變換時不修改平移元素。其他的變換矩陣方法沒有類似的情況,但是你可以通過儲存原來的平移位置,在變換之後重新賦值給矩陣,來輕鬆的提供類似的方法。是的,不涉及複雜的矩陣運算。

function deltaScale (matrix, x, y){
	var position = new flash.geom.Point(matrix.tx, matrix.ty);
	matrix.scale(x, y);
	matrix.tx = position.x;
	matrix.ty = position.y;
}

function deltaSkew (matrix, x, y){
	var position = new flash.geom.Point(matrix.tx, matrix.ty);
	// assume skew function for matrices is defined
	skew(matrix, x, y);
	matrix.tx = position.x;
	matrix.ty = position.y;
}

function deltaRotate (matrix, angle){
	var position = new flash.geom.Point(matrix.tx, matrix.ty);
	matrix.rotate(angle);
	matrix.tx = position.x;
	matrix.ty = position.y;
}

這些方法,像原來的變換方法那樣,但不同於內部的方法,會改變在正在使用的矩陣內部的值,而不是返回一個新的矩陣。

另外,你可以根據已經定義好的原有的方法,來改變矩陣中的值,不過,更簡單的方法是,儲存原有的值,在變換過後,重新賦值,特別是當操作非常低效的時候,比如旋轉。

function deltaRotate (matrix, angle){
	var sin = Math.sin( angle );
	var cos = Math.cos( angle );
	var a = matrix.a;
	var b = matrix.b;
	var c = matrix.c;
	var d = matrix.d;
	matrix.a = a*cos - b*sin;
	matrix.b = a*sin + b*cos;
	matrix.c = c*cos - d*sin;
	matrix.d = c*sin + d*cos;
}

轉換轉化

有時候,你可能需要與已經存在在Flash IDE中的矩陣比較矩陣的值,或者與ActionScript更加普遍的屬性比較。不過,變換矩陣的值,不總是與其他的Flash副本相關聯。例如,平移直接與Flash中的位置x(_x)和y(_y)相關,但是,縮放,切變,旋轉處理起來有一些不同。

平移

對大多數情況而言,平移是簡單的。屬性tx和ty,在矩陣中直接與ActionScript提供的_x 和  _y相關聯,_x和_y,是父時間線中圖片的位置。不過,在Flash IDE中,這些值在屬性檢查時可以不同。

Flash IDE允許位置以兩個不同的地點為基準。可以在圖片的左上角,或者在圖片的中心。使用哪個的選項,會出現在面板上的表格中。


但是,這個中間選項,並不是永遠表示問題中圖片的本地的0,0位置,或者原點。它實際上與變換工具的變換中心點相關,並且在Flash中,它是表示為小的黑點和白的圓圈。預設情況下,這是位於圖片的中間,但是可以通過變換工具來調整。針對變換的值,或者只是那種情況下的_x 和 _y值,來匹配這個變換中心點,這會要求放在圖片中的原始位置,以黑色和白色表示的十字。

ActionScript訪問不到變換的中心點的,所以當你在Flash中操作圖片時,記住這個。最好保證註冊點與圖片原始點對齊,這樣你就可以,在Flash IDE中,直接關聯看到的值,並且這些值是可以通過ActionScript訪問到的。


var my_matrix = my_mc.transform.matrix;

trace("x: " + my_matrix.tx);
trace("y: " + my_matrix.ty);

縮放

縮放,大多數與變換矩陣的a和b直接相關,但在切變的變換後會變複雜。沒有切變,a和b直接關聯Flash提供的縮放元素——那些在IDE的變換面板和ActionScript中已經存在的_xscale和_yscale——用一個縮放比例為1的,矩陣縮放的最簡單的例外來說,不過在Flash中,縮放是以100為基準的,對於縮放100%。

當呼叫切變時,縮放的一個軸發生了變換,會在另外一個相關的軸上繼續拉伸,無論x到y或者相反順序。因為這些關係,只要使用畢達哥拉斯定理來獲得正確的縮放因子就行了。


var my_matrix = my_mc.transform.matrix;

trace("x scale: " + Math.sqrt(my_matrix.a * my_matrix.a + my_matrix.b * my_matrix.b));
trace("y scale: " + Math.sqrt(my_matrix.c * my_matrix.c + my_matrix.d * my_matrix.d));

切變

在Flash 8中之前的變換矩陣,切變的值是不能被ActionScript訪問到的。不過,Flash IDE,確實依然能夠在變換面板上提供切變的值,以度數的形式顯示。這與真實變換矩陣求解變換的值有一些不同。值是代表比例的,在一個軸上與另外一個軸相關的縮放值。他們不是旋轉或者角度值。

在Flash中,表示角度的旋轉值,當一個邊發生切變時,會在另外一個軸上建立該角度值。例如,x軸上的切變,在傾斜的y軸上和他的原來的垂直方向之間,建立了一個角度。


角度值是與切變的大小相關的。用軸上任意一個點,用圖片矩陣轉換它,可以通過arctangent讓我們得到旋轉的值。x軸切變到y軸朝向,則需要一個90度差值。

var my_matrix = my_mc.transform.matrix;

var px = new flash.geom.Point(0, 1);
px = my_matrix.deltaTransformPoint(px);
var py = new flash.geom.Point(1, 0);
py = my_matrix.deltaTransformPoint(py);

trace("x skew: " +((180/Math.PI) * Math.atan2(px.y, px.x) - 90));
trace("y skew: " +((180/Math.PI) * Math.atan2(py.y, py.x)));

旋轉

如果你知道切變,你就知道旋轉了。Flash用x切變角度來旋轉。

var my_matrix = my_mc.transform.matrix;

var px = new flash.geom.Point(0, 1);
px = my_matrix.deltaTransformPoint(px);

trace("rotation: " +((180/Math.PI) * Math.atan2(px.y, px.x) - 90));

應用

基礎敘述完畢。是時候讓矩陣變換的知識用在真實世界的應用程式中了。既包括改變圖片,也包括BitmapData操作。

搖動的笑臉組

這裡,有一組無需的笑臉組成的動畫,已經巢狀存在在的圖片內了,並不斷變換著。當選中一個時,它會衝出父類的變換圖,通過使用級聯矩陣反轉,讓他自己真實的顯示出來。

檢視下載

根據以上的展示,一個巢狀的,變換的動畫可以通過使用invert和級聯矩陣,非常輕鬆的脫離它的變換。這個例子是這樣,但是在圖片的動畫上添加了,從原始的變化,到新的翻轉的變化。可以通過使用自定義的方法matrixInterpolate()來實現。

Flash中的點類,有一個方法interpolate(),允許你根據線定理,連線兩個點。基本上,這是那些取“兩者之間”的方法,來取兩者之間的值。Matrix類是沒有這樣的方法的,因此,為這個例子建立了一個方法。它所做的就是返回一個新矩陣,給定一個t引數,取值在0和1之間,使新矩陣的值在兩個矩陣的值中選擇。

function matrixInterpolate(m1, m2, t){
	var mi = new flash.geom.Matrix();
	mi.a = m1.a + (m2.a - m1.a)*t;
	mi.b = m1.b + (m2.b - m1.b)*t;
	mi.c = m1.c + (m2.c - m1.c)*t;
	mi.d = m1.d + (m2.d - m1.d)*t;
	mi.tx = m1.tx + (m2.tx - m1.tx)*t;
	mi.ty = m1.ty + (m2.ty - m1.ty)*t;
	return mi;
}

用事件onEnterFrame繫結這個,動畫就可以從原始巢狀變換,到反轉變換,如此重複了。

然而,關於反轉變換的一件事情是,它不只是反轉級聯矩陣。只是反轉會讓矩陣成為一個單位矩陣,讓圖片移動到0,0位置,縮放到普通比例(這個例子中的動畫的小版本)。相反