为什么变换顺序非常重要

更新:2007 年 11 月

单个 Matrix 对象可存储一个变换或一系列变换。后者称为“复合变换”。复合变换的矩阵是通过单个变换的矩阵相乘得到的。

复合变换示例

在复合变换中,单个变换的顺序非常重要。例如,依次旋转、缩放和平移与依次平移、旋转和缩放得到的结果不同。在 GDI+ 中,复合变换是从左到右构造的。如果用 S、R 和 T 分别代表缩放、旋转和平移矩阵,则乘积 SRT(按照这个顺序)就是依次缩放、旋转和平移获得的复合变换的矩阵。由乘积 SRT 生成的矩阵与由乘积 TRS 生成的矩阵不同。

造成顺序很重要的一个原因就是,像旋转和缩放这样的变换是针对坐标系的原点进行的。缩放以原点为中心的对象与缩放已离开原点的对象所得到的结果不同。同样,旋转以原点为中心的对象与旋转已离开原点的对象所得到的结果也不同。

下面的示例结合缩放、旋转和平移(按照这个顺序)以形成复合变换。传递给 RotateTransform 方法的 Append 参数指示将在缩放之后进行旋转。同样地,传递给 TranslateTransform 方法的参数 Append 指示将在旋转之后进行平移。AppendPrependMatrixOrder 枚举的成员。

Dim rect As New Rectangle(0, 0, 50, 50)
Dim pen As New Pen(Color.FromArgb(128, 200, 0, 200), 2)
e.Graphics.ResetTransform()
e.Graphics.ScaleTransform(1.75F, 0.5F)
e.Graphics.RotateTransform(28, MatrixOrder.Append)
e.Graphics.TranslateTransform(150, 150, MatrixOrder.Append)
e.Graphics.DrawRectangle(pen, rect)

Rectangle rect = new Rectangle(0, 0, 50, 50);
Pen pen = new Pen(Color.FromArgb(128, 200, 0, 200), 2);
e.Graphics.ResetTransform();
e.Graphics.ScaleTransform(1.75f, 0.5f);
e.Graphics.RotateTransform(28, MatrixOrder.Append);
e.Graphics.TranslateTransform(150, 150, MatrixOrder.Append);
e.Graphics.DrawRectangle(pen, rect);

下面的示例与上面的示例调用相同的方法,但是调用的顺序完全相反。作为其结果的操作顺序依次是平移、旋转和缩放,与依次缩放、旋转和平移所产生的结果大不相同。

Dim rect As New Rectangle(0, 0, 50, 50)
Dim pen As New Pen(Color.FromArgb(128, 200, 0, 200), 2)
e.Graphics.ResetTransform()
e.Graphics.TranslateTransform(150, 150, MatrixOrder.Append)
e.Graphics.RotateTransform(28, MatrixOrder.Append)
e.Graphics.ScaleTransform(1.75F, 0.5F)
e.Graphics.DrawRectangle(pen, rect)

Rectangle rect = new Rectangle(0, 0, 50, 50);
Pen pen = new Pen(Color.FromArgb(128, 200, 0, 200), 2);
e.Graphics.ResetTransform();
e.Graphics.TranslateTransform(150, 150, MatrixOrder.Append);
e.Graphics.RotateTransform(28, MatrixOrder.Append);
e.Graphics.ScaleTransform(1.75f, 0.5f);
e.Graphics.DrawRectangle(pen, rect);

在复合变换中,颠倒单个变换顺序的一种方法是颠倒方法调用序列的顺序。控制操作顺序的第二种方法是更改矩阵顺序的参数。下面的示例与上面的示例相同,不同的是 Append 已更改为 Prepend。矩阵是按照 SRT 顺序进行相乘的,其中 S、R 和 T 分别表示缩放、旋转和平移的矩阵。复合变换的顺序依次是缩放、旋转和平移。

Dim rect As New Rectangle(0, 0, 50, 50)
Dim pen As New Pen(Color.FromArgb(128, 200, 0, 200), 2)
e.Graphics.ResetTransform()
e.Graphics.TranslateTransform(150, 150, MatrixOrder.Prepend)
e.Graphics.RotateTransform(28, MatrixOrder.Prepend)
e.Graphics.ScaleTransform(1.75F, 0.5F)
e.Graphics.DrawRectangle(pen, rect)

Rectangle rect = new Rectangle(0, 0, 50, 50);
Pen pen = new Pen(Color.FromArgb(128, 200, 0, 200), 2);
e.Graphics.ResetTransform();
e.Graphics.TranslateTransform(150, 150, MatrixOrder.Prepend);
e.Graphics.RotateTransform(28, MatrixOrder.Prepend);
e.Graphics.ScaleTransform(1.75f, 0.5f);
e.Graphics.DrawRectangle(pen, rect);

刚才的示例与本主题中的第一个示例所产生的结果完全相同。这是因为我们同时颠倒了方法调用的顺序和矩阵相乘的顺序。

请参见

参考

Matrix

其他资源

坐标系和坐标变换

在托管 GDI+ 中使用变换