• 二维图形的矩阵变换(二)——WPF中的矩阵变换基础


    在前文二维图形的矩阵变换(一)——基本概念中已经介绍过二维图像矩阵变换的一些基础知识,本文中主要介绍一下如何在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 };
            }
        }
    View Code

    通过这个扩展函数,前面的变换可以简化如下:

        var transForm = GeometryTransForm.Identity.Scale(2, 2).Translate(10, 20);

    另外,这个类也支持直接和Point相乘,用起来还是蛮方便的。

    UI的矩阵变换

    由于篇幅所限,本文只介绍了WPF矩阵变换的基础操作,下一篇文章中再介绍如何将矩阵变换应用到UI界面上

  • 相关阅读:
    40 +必不可少的前端Web开发备忘单
    web前端开发必读的HTML5的书籍
    Java String的内存机制
    使用C#通过Oracle.DataAccess连接Oracle,部署时需要注意版本问题
    CI(CodeIgniter)的"Disallowed Key Characters."异常处理
    开源软件许可协议简介
    读《考拉小巫的英语学习日记》有感
    vi的复制、粘贴、查找、删除等常用命令
    读《马云创业启示录》有感
    jQuery ajax 同步失效?
  • 原文地址:https://www.cnblogs.com/TianFang/p/3930235.html
Copyright © 2020-2023  润新知