二维图形的矩阵变换(三)——在WPF中的应用矩阵变换

原文:二维图形的矩阵变换(三)——在WPF中的应用矩阵变换

UIElement和RenderTransform

首先,我们来看看什么样的对象可以进行变换。在WPF中,用于呈现给用户的对象的基类为Visual类,但是Visual对象并不具有变换功能,具有变换功能的是它的子类UIElement。这个类也是非常底层的类了,几乎我们所有的常用控件都是继承自它,也就是说,基本上所有的UI对象都是可以应用变换的。

然后,我们在再来看看UIElement中变换种类。UIElement支持两种变换:RenderTransform和LayoutTransform,其中LayoutTransform是会改变其布局,从而影响相邻的空间大小和位置的,如下图所示。

        

由于我们常用的是RenderTransfrom,并且两种变换的使用方式非常类似,下面的文章中就主要以RenderTransfrom作为介绍对象。下面的例子就简单的演示了其用法:

    <StackPanel Orientation="Vertical">
        <Button Content="A Button" Opacity="1" />
        <Button Content="Rotated Button">
            <Button.RenderTransform>
                <RotateTransform Angle="45" />
            </Button.RenderTransform>
        </Button>
        <Button Content="A Button" Opacity="1" />
    </StackPanel>

 

矩阵变换MatrixTransform

前面的例子中演示了旋转的变换RotateTransform的用法,其它几种基本变换也有相对的变换类:ScaleTransform 、TranslateTransform 、SkewTransform。我们也可以将多个变换放到一个变换组中实现叠加的效果。

这些基本变换用法相对比较简单,这里就不多介绍了。下面介绍本文的重点:矩阵变换MatrixTransform。它的用法和RotateTransform实际上也差不多:

    <Button Content="Rotated Button">
        <Button.RenderTransform>
            <MatrixTransform x:Name="myMatrixTransform">
                <MatrixTransform.Matrix >
                    <Matrix OffsetX="10" OffsetY="100" />
                </MatrixTransform.Matrix>
            </MatrixTransform>
        </Button.RenderTransform>
    </Button>

从上面的代码中可以看到,由于矩阵变换要设置六个值,并且这几个值不容易读,因此在XAML中使用显得非常不直观,大多数的时候我们是在代码中进行设置的。

单单从这个例子来看,是无法看出矩阵变换的什么优越性的。那是因为我们使用的变换比较简单,在前文二维图形的矩阵变换(一)——基本概念中介绍过,任何二维变换的序列均可存储于单个的 Matrix 对象,因此它是可以非常容易实现变换叠加效果的,下面就以我之前的文章用WPF实现一个简单的图片查看器中介绍到的例子用矩阵变换来改写一下。

这个例子的主要功能是实现一个支持鼠标拖动和滚轮缩放的图片查看器,在原文中是靠平移变换和缩放变换叠加实现的,这里用矩阵变换来实现一下。首先还是来看看XAML部分

    <Grid>
        <Image Source="source.jpg" MouseWheel="Image_MouseWheel" PreviewMouseLeftButtonDown="Image_MouseLeftButtonDown"

            PreviewMouseMove="Image_MouseMove">
            <Image.RenderTransform>
                <MatrixTransform x:Name="transForm" />
            </Image.RenderTransform>
        </Image>
    </Grid>

然后就是事件的实现了:

    private
void Image_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        var center = getPosition(sender, e);
        var scale = (e.Delta > 0 ? 1.2 : 1 / 1.2);

        var matrix = transForm.Matrix;
        matrix.ScaleAt(scale, scale, center.X, center.Y);

        transForm.Matrix = matrix;
    }

    Point dragStart;
    private
void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        dragStart = getPosition(sender, e);
    }

    private
void Image_MouseMove(object sender, MouseEventArgs e)
    {
        if ((e.LeftButton != MouseButtonState.Pressed))
        {
            return;
        }

        var current = getPosition(sender, e);
        var offset = current - dragStart;

        var matrix = transForm.Matrix;
        matrix.Translate(offset.X, offset.Y);

        transForm.Matrix = matrix;

        dragStart = current;
    }

    Point getPosition(object sender, MouseEventArgs e)
    {
        return e.GetPosition(sender as
UIElement) * transForm.Matrix;
    }

由于这个例子本身并不复杂,并不能很好的体现矩阵变换的优越性,但还是可见一斑的。原文中是通过平移变换和缩放变换叠加实现的,因此这两个变换是互相影响的,平移的时候需要考虑缩放率、缩放的时候要考虑偏移量,调整相应的参数进行校正。而矩阵变换相对简单得多,只需要产生将变换矩阵和原始变换矩阵相乘即可获得叠加效果。

另外,由于矩阵变换还可以应用于Point,因此非常方便实现一些附加功能的,例如,我们要获取放大后的图像在原始图像的位置时,只需要取屏幕上四周的四个点,对档期变换矩阵的逆矩阵相乘即可。其它的就不一一列举了,要实现完整的图片查看器,矩阵变换比平移变换和缩放变换叠加要方便太多。

 

矩阵变换的动画

在WPF中,变换过程是可以非常容易的改成动画的炫酷效果的,但不知道为什么,系统并没有提供动画效果的矩阵变换的内置实现。不过这个并不难解决,Google了一下就发现在Stack overflow上已经有人实现了,原文地址如下:Smooth animation using MatrixTransform?,为了防止方校长哪天爱心泛滥把这个网站改成寻人启事了,这里还是转录一下。  

    public class MatrixAnimation : MatrixAnimationBase
    {
        public Matrix? From
        {
            set { SetValue(FromProperty, value); }
            get { return (Matrix?)GetValue(FromProperty); }
        }

        public static DependencyProperty FromProperty =
            DependencyProperty.Register("From", typeof(Matrix?), typeof(MatrixAnimation),
                new PropertyMetadata(null));

        public Matrix? To
        {
            set { SetValue(ToProperty, value); }
            get { return (Matrix?)GetValue(ToProperty); }
        }

        public static DependencyProperty ToProperty =
            DependencyProperty.Register("To", typeof(Matrix?), typeof(MatrixAnimation),
                new PropertyMetadata(null));

        public IEasingFunction EasingFunction
        {
            get { return (IEasingFunction)GetValue(EasingFunctionProperty); }
            set { SetValue(EasingFunctionProperty, value); }
        }

        public static readonly DependencyProperty EasingFunctionProperty =
            DependencyProperty.Register("EasingFunction", typeof(IEasingFunction), typeof(MatrixAnimation),
                new UIPropertyMetadata(null));

        public MatrixAnimation()
        {
        }

        public MatrixAnimation(Matrix toValue, Duration duration)
        {
            To = toValue;
            Duration = duration;
        }

        public MatrixAnimation(Matrix toValue, Duration duration, FillBehavior fillBehavior)
        {
            To = toValue;
            Duration = duration;
            FillBehavior = fillBehavior;
        }

        public MatrixAnimation(Matrix fromValue, Matrix toValue, Duration duration)
        {
            From = fromValue;
            To = toValue;
            Duration = duration;
        }

        public MatrixAnimation(Matrix fromValue, Matrix toValue, Duration duration, FillBehavior fillBehavior)
        {
            From = fromValue;
            To = toValue;
            Duration = duration;
            FillBehavior = fillBehavior;
        }

        protected override Freezable CreateInstanceCore()
        {
            return new MatrixAnimation();
        }

        protected override Matrix GetCurrentValueCore(Matrix defaultOriginValue, Matrix defaultDestinationValue, AnimationClock animationClock)
        {
            if (animationClock.CurrentProgress == null)
            {
                return Matrix.Identity;
            }

            var normalizedTime = animationClock.CurrentProgress.Value;
            if (EasingFunction != null)
            {
                normalizedTime = EasingFunction.Ease(normalizedTime);
            }

            var from = From ?? defaultOriginValue;
            var to = To ?? defaultDestinationValue;

            var newMatrix = new Matrix(
                    ((to.M11 - from.M11) * normalizedTime) + from.M11,
                    ((to.M12 - from.M12) * normalizedTime) + from.M12,
                    ((to.M21 - from.M21) * normalizedTime) + from.M21,
                    ((to.M22 - from.M22) * normalizedTime) + from.M22,
                    ((to.OffsetX - from.OffsetX) * normalizedTime) + from.OffsetX,
                    ((to.OffsetY - from.OffsetY) * normalizedTime) + from.OffsetY);

            return newMatrix;
        }
    }

View Code

 

时间: 2024-07-31 15:03:46

二维图形的矩阵变换(三)——在WPF中的应用矩阵变换的相关文章

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

原文:二维图形的矩阵变换(二)--WPF中的矩阵变换基础 在前文二维图形的矩阵变换(一)--基本概念中已经介绍过二维图像矩阵变换的一些基础知识,本文中主要介绍一下如何在WPF中进行矩阵变换.   Matrix结构 在WPF中,用Matrix结构(struct类型)表示二维变换矩阵,它是一个3*3的数组,结构如下,      由于第三列是常量0,0,1,因此并不作为公开属性,可见的只有剩余六个属性.   构造变换 虽然Matrix类公开了这六个属性让我们设置,但是靠直接设置这六个属性来实现平移.旋

二维图形的矩阵变换(一)——基本概念

原文:二维图形的矩阵变换(一)--基本概念 基本的二维变换可包括旋转.缩放.扭曲,和平移四种,                      而这些几何运算则可以转换为一些基本的矩阵运算:      这几个变换都是线性的,但平移运算不是线性的,不能通过2*2矩阵运算完成.若要将点 (2, 1)在 x 方向将其平移 3 个单位,在 y 方向将其平移 4 个单位. 可通过先使用矩阵乘法再使用矩阵加法来完成此操作.      综合这几种基本运算,数学家们将其统一为一个3*3矩阵,存储形式如下:     

Android简明开发教程八:引路蜂二维图形绘制实例功能定义

有了前面对Android平台的介绍,基本上可以开始编写Android应用了,这里将以绘制二维图形为例,对Android开发的一般方 法做过介绍,其中涉及到自定义Application类,扩展View,Intent定义,发送消息,Data Binding(Adapter),和基本UI设计 .示例没有使用Android平台自带的二维图形API,而是调用了引路蜂二维图形库,引路蜂二维图形库Graphics 2D API实现了移 动平台上图形引擎,它能够以一种统一的方式处理各种基本图形(Shape),路

matlab中绘制二维图形

  matlab中有时为了看清方程,更加系统的了解方程,常常需要画图表达,那么为了绘制二维图形就需要了解matlab中常用的图形功能,下面介绍一下matlab中的二维图形功能. plot(x) ------------根据向量绘制二维曲线,例如: x=[1,2,3,5,4,3.5,2,0]; plot(x) plot(x,y)------------根据向量x,y绘制二维图形; 在matlab中利用plot(x,y)绘制y=2x^2(0<=x<=10)d的图形 x=linspace(0,10,

C++实现二维图形的傅里叶变换_C 语言

本文实例讲述了C++实现二维图形的傅里叶变换的方法.有一定的借鉴价值.分享给大家供大家参考. 具体代码如下: // Fourier.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "stdio.h" #include "math.h" #include <cv.h> #include <highg

JSP页面的二维数组如何传个本页面中的js里面?

问题描述 JSP页面的二维数组如何传个本页面中的js里面? 解决方案 解决方案二:没做过,如果不嫌麻烦的话我觉得可以可以把jsp中数组的内容重新赋值给js中的数组vararr;//js中的二维数组<%for(inti=0;i<jspArr.length;i++){//jspArr是jsp页面中的二维数组for(intj=0;j<jspArr[0].length;j++){%>arr[<%=i%][<%=j%>]=<%=jspArr[i][j]%>}}%

二维图形基本变换-c程序转换为java程序,最好用图形界面实现

问题描述 c程序转换为java程序,最好用图形界面实现 #include #include #define N 50 #define M 3.14159265 void erwei(); void sanwei(); void main() { int w; printf("请输入是几维图形变换:二维(2)或三维(3): "); scanf_s("%d", &w); if(w == 2) erwei(); else if(w == 3) sanwei();

水晶报表 二维码-请问一下在水晶报表中怎么插入二维码

问题描述 请问一下在水晶报表中怎么插入二维码 在网上找了一下 http://topic.csdn.net/u/20080306/13/b471356c-d5d8-4914-bfc0-bb5a742b9c1f.html发现了这个 Sub QRCodeSaveRead() bc1.Type = DotNetBarcode.Types.QRCode bc1.PrintChar = True '保存QRCode图片 strQRCode = "" strQRCode = Trim(txtCode

《MATLAB图像处理375例》——第2章 MATLAB图形的可视化 2.1 二维绘图

第2章 MATLAB图形的可视化 MATLAB图像处理375例数据可视化是MATLAB一项重要的功能.MATLAB所提供的丰富绘图功能,使得工程科研人员从繁琐的绘图细节中脱离出来,并专注于最关心的本质.通过数据可视化的方法,工程科研人员可以对样本数据的分布.趋势特性有一个直观的了解. 2.1 二维绘图 MATLAB图像处理375例在MATLAB中绘制二维图形,通常采用以下步骤. (1)准备数据. (2)设置当前绘图区. (3)绘制图形. (4)设置图形中曲线和标记点格式. (5)设置坐标轴和网格