在前文二維圖形的矩陣變換(一)——基本概念中已經介紹過二維圖像矩陣變換的一些基礎知識,本文中主要介紹一下如何在WPF中進行矩陣變換。
Matrix結構
在WPF中,用Matrix結構(struct類型)表示二維變換矩陣,它是一個3*3的數組,結構如下,
由於第三列是常量0,0,1,因此並不作為公開屬性,可見的只有剩余六個屬性。
構造變換
雖然Matrix類公開了這六個屬性讓我們設置,但是靠直接設置這六個屬性來實現平移、旋轉等變換對於我們來說實在太困難了,因此又增加了如下許多函數來幫助我們實現這一過程,常見了有:
-
Rotate
-
RotateAt
-
Scale
-
ScaleAt
-
Skew
-
Translate
這些函數的效果是疊加的,例如,我們要先平移(10,20),然后繞原點旋轉30度,方式如下:
Matrix matrix = Matrix.Identity;
matrix.Translate(10, 20);
matrix.Rotate(30);
其中Matrix.Identity是矩陣的默認值,它是一個恆等矩陣(不進行任何變換,可以用於重置)。
反轉矩陣
關於反轉矩陣,Matrix類中提供了一個屬性和函數:
-
HasInverse 屬性 用於檢查該矩陣是否可以反轉。
-
Invert() 用於獲取反轉矩陣
反轉矩陣可以非常方便我們進行矩陣的逆運算,十分有用。
應用變換
在WPF中可以接受矩陣運算的基礎元素有Point和Vector,可以通過Transform函數進行矩陣變換:
var transForm = Matrix.Identity;
transForm.Scale(2, 3);
var point = new Point(1, 1);
var newPoint = transForm.Transform(point);
Console.WriteLine(newPoint); //輸出(2,3)
在C#中還重載了"*"運算符,這樣更加直觀了:
var newPoint = point * transForm;
另外,Transform函數還有一個可以接收數組的的版本,這個版本中並不生成新的對象,因此具有更高的效率。
復合變換
前文已經介紹過,矩陣是可以通過乘運算實現變換的疊加的,Matrix類中提供了Multiply函數進行兩個矩陣相乘,在C#中也可以使用"*"運算符來實現這一過程。
Matrix scale = Matrix.Identity;
scale.Scale(2, 2);
Matrix transLate = Matrix.Identity;
transLate.Translate(10, 20);
var transForm = scale * transLate;
Matrix transForm2 = Matrix.Identity;
transForm2.Scale(2, 2);
transForm2.Translate(10, 20);
Contract.Assert(transForm == transForm2);
需要注意的是,矩陣並不滿足交換律,如:
Contract.Assert((transLate * scale) != (scale * transLate));
擴展函數
在日常的使用過程中,我們的變換矩陣往往是通過一系列操作疊加起來的。可能是為了效率,WPF的變換函數返回值都是Void,疊加起來並不方便。這里我寫了幾個擴展函數簡化這一過程:

public class GeometryTransForm { Matrix _matrix; public Matrix Matrix { get { return _matrix; } private set { _matrix = value; } } /// <summary> /// 獲取一個恆等變換 /// </summary> public static GeometryTransForm Identity { get { return new GeometryTransForm(); } } /// <summary> /// 以指定點為中心旋轉指定的角度。 /// </summary> /// <param name="angle">要旋轉的角度(單位為度)。</param> /// <param name="centerX">要圍繞其旋轉的點的 x 坐標。</param> /// <param name="centerY">要圍繞其旋轉的點的 y 坐標。</param> public GeometryTransForm Rotate(double angle, double centerX = 0, double centerY = 0) { _matrix.RotateAt(angle, centerX, centerY); return this; } /// <summary> /// 圍繞指定的點按指定的量縮放 /// </summary> /// <param name="scaleX">沿 x 軸的縮放量</param> /// <param name="scaleY">沿 y 軸的縮放量</param> /// <param name="centerX">縮放操作中心點的 x 坐標</param> /// <param name="centerY">縮放操作中心點的 y 坐標</param> public GeometryTransForm Scale(double scaleX, double scaleY, double centerX = 0, double centerY = 0) { _matrix.ScaleAt(scaleX, scaleY, centerX, centerY); return this; } /// <summary> /// 在 x 和 y 維中指定角度的扭曲。 /// </summary> /// <param name="skewX">用於扭曲此的 x 維角度</param> /// <param name="skewY">用於扭曲此的 y 維角度</param> public GeometryTransForm Skew(double skewX, double skewY) { _matrix.Skew(skewX, skewY); return this; } /// <summary> /// 按指定偏移量的平移 /// </summary> /// <param name="offsetX">沿 x 軸的偏移量</param> /// <param name="offsetY">沿 y 軸的偏移量</param> public GeometryTransForm Translate(double offsetX, double offsetY) { _matrix.Translate(offsetX, offsetY); return this; } public GeometryTransForm Transfrom(GeometryTransForm transform) { return Transfrom(transform.Matrix); } public GeometryTransForm Transfrom(Matrix transform) { _matrix = _matrix * transform; return this; } /// <summary> /// 反轉變換 /// </summary> public GeometryTransForm Invert() { _matrix.Invert(); return this; } public static Point operator *(Point point, GeometryTransForm transform) { return point * transform.Matrix; } //如果是struct就用不着這個了,每一次 = 都是Clone public GeometryTransForm Clone() { return new GeometryTransForm() { Matrix = this.Matrix }; } }
通過這個擴展函數,前面的變換可以簡化如下:
var transForm = GeometryTransForm.Identity.Scale(2, 2).Translate(10, 20);
另外,這個類也支持直接和Point相乘,用起來還是蠻方便的。
UI的矩陣變換
由於篇幅所限,本文只介紹了WPF矩陣變換的基礎操作,下一篇文章中再介紹如何將矩陣變換應用到UI界面上