原文:二维图形的矩阵变换(三)——在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;
}
}

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

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

    原文:二维图形的矩阵变换(二)--WPF中的矩阵变换基础 在前文二维图形的矩阵变换(一)——基本概念中已经介绍过二维图像矩阵变换的一些基础知识,本文中主要介绍一下如何在WPF中进行矩阵变换. Matr ...

  2. 3ds max学习笔记(十五)-- 二维图形的操作

    (二维图形的创建) 1,在命令面板的[新建],单击第二个按钮: 从中选择对象名称,在视图种单击拖动进行创建,特殊:线:摁[shift]限制水平,垂直方向: 2,二维对象参数: 在渲染中启用:显示二维线 ...

  3. 通过Matrix进行二维图形仿射变换

    Affine Transformation是一种二维坐标到二维坐标之间的线性变换,保持二维图形的"平直性"和"平行性".仿射变换可以通过一系列的原子变换的复合来 ...

  4. matlab绘制二维图形

    常用的二维图形命令: plot:绘制二维图形 loglog:用全对数坐标绘图 semilogx:用半对数坐标(X)绘图 semilogy:用半对数坐标(Y)绘图 fill:绘制二维多边填充图形 pol ...

  5. VS2008集成QT的OpenGL开发(实现二维图形的旋转)

    主要是利用Qt中的定时器实现了二维图形的旋转功能: #ifndef QGLTEST_H #define QGLTEST_H #include <QGLWidget> #include &l ...

  6. openGL实现二维图形和三维图形

    openGL是一个强大的底层图形库,其命令最初的时候使用C语言实现的.openGL定义了一个图形程序接口,常用于制作处理三维图像,功能强大,调用方便,在图像处理十分受欢迎. 实现图形主要使用的是ope ...

  7. QT 二维图形 原理、发展及应用

    转载自 网易博客:sun的博客 http://zhouyang340.blog.163.com/blog/static/3024095920126710504178/ 2D绘图 Qt4中的2D绘图部分 ...

  8. Matlab 常用绘图指令(二维图形)

    使用matlab的时候常常会忘掉一些指令,每次都要重新查找,挺麻烦的,这里收集一些常用的绘图指令,供自己和大家以后方便查找和使用. 1.例子-包含了常用绘图命令 clear clc %%数据准备 x ...

  9. C# 动态生成word文档 [C#学习笔记3]关于Main(string[ ] args)中args命令行参数 实现DataTables搜索框查询结果高亮显示 二维码神器QRCoder Asp.net MVC 中 CodeFirst 开发模式实例

    C# 动态生成word文档 本文以一个简单的小例子,简述利用C#语言开发word表格相关的知识,仅供学习分享使用,如有不足之处,还请指正. 在工程中引用word的动态库 在项目中,点击项目名称右键-- ...

随机推荐

  1. uva673 - Parentheses Balance(栈)

    题意:1.空串合法.2.若A和B合法,则AB合法.3.若A合法,则(A)和[A]合法. 思路:遍历串,遇到(或[,则压入队列,若遇到),判断:若栈空,则不合法:若栈顶元素不是(,也不合法.]同理.因为 ...

  2. SQLite学习心得

    SQLite是一款很有名气的小型开源跨平台数据库,作为目前最流行的开源嵌入式关系型数据库,在系统结构设计中正在扮演着越来越重要的角色. 本文主要沿着 http://www.cppblog.com/we ...

  3. Servlet、Filter 生命周期

    Servlet作为JavaEE必须掌握的内容,Struts2通过使用Filter的功能实现了一个MVC的框架.因此掌握这Servlet以及Filter的生命周期显得非常重要. 1. Servlet的生 ...

  4. Mac OS 踩坑指南

    前言 其实mac os本身还是很不错的,软硬结合使得其性能.效率.续航得到了很好的优化. 但是毕竟是一个"小众"操作系统,很多在Win上已经用习惯的东西在这里都没有,或者完全不一样 ...

  5. Ubuntu 16.04配置OpenCV 3.1.0 for Java

    我们都知道,OpenCV是基于C++的开源计算机视觉库,但是从2.4.4版本开始提供了Java绑定,也就是说,我们也可以使用Java来开发基于OpenCV的计算机视觉应用.目前,最新的版本是3.1.0 ...

  6. hbase on spark

    1.在spark的伪分布式环境下安装HBASE (1)版本:我使用的spark版本是1.3.0,使用的hbase版本是hbase-0.94.16 (2)解压,tar zxvf  hbase-0.94. ...

  7. 实现Linux select IO复用C/S服务器代码

    已在ubuntu 下验证可用 服务器端 #include<stdio.h>#include<unistd.h>#include<stdlib.h>#include& ...

  8. 关于对db_block_gets的理解与实验

    实验 一. 自己手动创建的小表 创建一个区大小为  40k  SYS@ORCL>show parameter db_block_size NAME                         ...

  9. Oracle bug 使用max或min函数into到一个char类型报字符缓冲区太小的错误

    这个BUG出现会报错如下: selectto_char(max(RENEWAL_DATE)) intoM_YEAR_MONTH fromt_renewal_schedule; ORA-06502: P ...

  10. 关于FileSystemWatcher监听文件创建

    FileSystemWatcher中的Created事件不但可以监听用户创建的文件,当用户删除某个文件时,系统会在再监听的那个盘上的回收站创建一个文件,在回收站创建的文件也会触发Created事件,而 ...