Flipping elements with WPF

http://yichuanshen.de/blog/2010/11/13/flipping-elements-with-wpf/

Have you already seen ForgottenTime’s new flip animation eye candy? If not, it’s about time! It took me several days to figure out how to do it…

My first thought was to find out how to do a 4-point-tranformation of a given image. (See figure on the left.) It’s no problem to create an “screenshot” of a UI element and I could easily calculate the four vertices of the transformed image (with given angle and a little bit trigonometry) and transform the original screenshot via the function to achieve a 3D effect. Unfortunately, there’s no (easy and fast) way to do that in C# and Windows Presentation Framework, so I had to think of something else.

After my research on the Internet I came across some official demo WPF applications, also using advanced UI techniques such as flipping. So I dug into the code to find out how they did it. As it turned out, they were using 3D graphics.

WELCOME TO THE 3RD DIMENSION

Strictly speaking a computer screen cannot display real 3D graphics of course, only projections of a 3D space onto a plane… the screen. It’s also called a “viewport”, a 2D window, that allows the user to gaze into the imaginary 3D space behind. Just like we have eyes, a viewport needs a “camera” (to be really precise, a PerspectiveCamera).

Let’s assume the image we want to flip is a square with the dimensions 129×129.

<!-- XAML code -->
<Viewport3D x:Name="viewport3D" Width="129" Height="129">
    <Viewport3D.Resources>
    </Viewport3D.Resources>
    <Viewport3D.Camera>
        <PerspectiveCamera x:Name="cam3D"
            FieldOfView="45"
            LookDirection="0,0,-1 "
            UpDirection="0,1,0" />
    </Viewport3D.Camera>
</Viewport3D>

After the window is loaded, we create a two-dimensional object, a square, which represents our image in 3D space and calculate where our camera should be. If all that is done, we can literally rotate the object around the y-axis and thus flip the image around.

THE TWO-DIMENSIONAL OBJECT

Every object in our 3D space is made of triangles. The triangle surface of such an object is called a mesh. It’s relatively easy to build a square out of two triangles as the sketch below shows.

We center the image around the origin, so that the camera can be easily positioned on the z-axis. To create such a simple object (notice this is two-dimensional!) you have to write tons of code:

// C# code
GeometryModel3D model3D;

private void BuildModel() {
    // Customize the brushes
    // Can be any brush (ImageBrush, DrawingBrush, VisualBrush, ...)
    ImageBrush front = new ImageBrush(this.frontImageSource);
    ImageBrush back = new ImageBrush(this.backImageSource);
    back.Transform = new ScaleTransform(-1, 1, .5, 0); // Flip back image

    // Create mesh
    MeshGeometry3D mesh = new MeshGeometry3D();
    double radius = 129 / 2.0; // 64.5
    mesh.Positions.Add(new Point3D(-radius, -radius, 0));
    mesh.Positions.Add(new Point3D(radius, -radius, 0));
    mesh.Positions.Add(new Point3D(radius, radius, 0));
    mesh.Positions.Add(new Point3D(-radius, radius, 0));
    mesh.TriangleIndices.Add(0);
    mesh.TriangleIndices.Add(1);
    mesh.TriangleIndices.Add(2);
    mesh.TriangleIndices.Add(0);
    mesh.TriangleIndices.Add(2);
    mesh.TriangleIndices.Add(3);
    mesh.TextureCoordinates.Add(new Point(0, 1));
    mesh.TextureCoordinates.Add(new Point(1, 1));
    mesh.TextureCoordinates.Add(new Point(1, 0));
    mesh.TextureCoordinates.Add(new Point(0, 0));

    // Add texture
    DiffuseMaterial frontMat = new DiffuseMaterial(front);
    DiffuseMaterial backMat = new DiffuseMaterial(back);
    frontMat.AmbientColor = backMat.AmbientColor = Colors.White;

    model3D = new GeometryModel3D();
    model3D.Geometry = mesh;
    model3D.Material = frontMat;
    model3D.BackMaterial = backMat;

    Model3DGroup group = new Model3DGroup();
    group.Children.Add(model3D);
    group.Children.Add(new AmbientLight(Colors.White));

    ModelVisual3D visual = new ModelVisual3D();
    visual.Content = group;
    viewport3D.Children.Add(visual);
}

THE CAMERA POSITION

The camera has to be some distance away from the square, so that everything is within the camera’s field of view. Especially when the image is rotated by 90? around the y-axis, where it’s nearest to the camera. So how do we calculate the distance?

The sketch above shows the image which has already been rotated by 90?. Let’s first look at the left side of the sketch. As we can see the camera is positioned . We’re going to calculate x using tangent.

Now if we put the camera at  it’s garanteed that everything is visible in the viewport. But as we can see in the sketch above, there’s space below and above (as well as left and right) the original unrotated image (space marked with variable s) which will make the image appear smaller in the viewport. We have to enlarge the viewport, so that the image will appear normal-sized again. We can calculate s as follows:

Now we have made all calculations, we can (finally) transform everything into code:

// C# code
private void PositionCamera() {
    double radius = 129 / 2.0; // 64.5

    // Calculate 3D cam position for flip animation
    double x = radius / Math.Tan(degToRad(45 / 2.0));
    cam3D.Position = new Point3D(0, 0, x + radius);

    // Add border for flip animation
    double s = radius * Math.Tan(degToRad(45 / 2.0));
    viewport3D.Height = viewport3D.Width = 2 * radius + 2 * s;
}

private void Window_Loaded(object sender, RoutedEventArgs e) {
    BuildModel();
    PositionCamera();
}

private double degToRad(double deg) {
     return deg / 180 * Math.PI;
}

The degToRad function is needed, because C#’s Trigonometry only takes radian angles.

THE ANIMATION

Now that we have painstakingly set up our beautiful 3D scene, we can finally animate it!

// C# code
public void Flip() {
    // Rotate
    AxisAngleRotation3D rotation = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
    model3D.Transform = new RotateTransform3D(rotation, new Point3D(0, 0, 0));

    DoubleAnimation flipAnimation = new DoubleAnimation(0, 180, new Duration(TimeSpan.FromMilliseconds(1000)));
    /* To flip back just swap 0 and 180 ;) */

    // Do magic!
    rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, flipAnimation);
}

Try it out! What a glorious effect! Once you saw it, you can’t seem to stop starting the animation over and over again.

时间: 2024-10-15 05:43:05

Flipping elements with WPF的相关文章

WPF 3D 知识点大全以及实例

原文:WPF 3D 知识点大全以及实例 引言 现在物联网概念这么火,如果监控的信息能够实时在手机的客服端中以3D形式展示给我们,那种体验大家可以发挥自己的想象. 那生活中我们还有很多地方用到这些,如上图所示的Kinect 在医疗上的应用,当然还有体感游戏等等. 3D 用来增加视觉效果,给人以更加直观,真实的感觉. 3D如此美妙,那我们在WPF中又该从何处入手开启我们的3D编程旅程? WPF中3D开发技术的基础知识应该有以下几点: 3D开发基础知识 WPF中3D开发的基础元素(Elements)

[WPF实用技巧]如何使WPF的TreeView节点之间有连线

示例代码:TreeViewEx.zip 原文地址:http://www.codeproject.com/Tips/673071/WPF-TreeView-with-WinForms-Style-Fomat   Introduction WPF default TreeView is very good, but many people still want it to have lines join each of its child elements, like Windows Forms T

Writing a Reusable Custom Control in WPF

In my previous post, I have already defined how you can inherit from an existing control and define your own reusable chunk. The reusable XAML code that I have defined there is actually a composition of one of more existing elements in a common desig

WPF内部DeliverEvent读锁和PrivateAddListener写锁导致死锁

?? 准备工作 1.对失去响应进程创建转储文件 2.配置该进程的pdb文件 3.复制该进程所在机器的系统dll(sos.dll, clr.dll, mscordacwks.dll) 4.配置系统dll的pdb文件路径, 或者直接从msdl.microsoft/download/symbols下载(注意端口是否被封) 分析过程: 1.利用~*e !clrstack 得到UI线程的ID 为12 2.利用~12e !clrstack -p 得到堆栈信息及函数参数值 3.从中发现先申请写锁, 后申请读锁

Twitter OA prepare: Flipping a bit

You are given a binary array with N elements: d[0], d[1], ... d[N - 1]. You can perform AT MOST one move on the array: choose any two integers [L, R], and flip all the elements between (and including) the L-th and R-th bits. L and R represent the lef

WPF入门教程系列(一) 创建你的第一个WPF项目

WPF入门教程系列(一) 创建你的第一个WPF项目 WPF基础知识 快速学习绝不是从零学起的,良好的基础是快速入手的关键,下面先为大家摞列以下自己总结的学习WPF的几点基础知识: 1) C#基础语法知识(或者其他.NET支持的语言):这个是当然的了,虽然WPF是XAML配置的,但是总还是要写代码的,相信各位读者应该也都有这个基础了. 2) HTML语言:虽然WPF是窗体程序但是由于使用的XAML语言,如果以前接触过HTML.XHTML.ASP.NET之路的东西的话会,接受这些标签会很有帮助的,如

在WPF中使用PlaneProjection模拟动态3D效果

原文:在WPF中使用PlaneProjection模拟动态3D效果 虽然在WPF中也集成了3D呈现的功能,在简单的3D应用中,有时候并不需要真实光影的3D场景.毕竟使用3D引擎会消耗很多资源,有时候使用各种变换和假的阴影贴图也能设计出既省资源,又有很好用户体验的“伪”3D界面. 在Silverlight中,因为性能问题,一般并不使用真3D引擎,微软为Silverlight提供了System.Windows.Media.PlaneProjection 类,用投影变换来模拟3D的效果. 下面让我们看

WPF Input Validation Using MVVM

Data validation is a key part in WPF.Validation is used to alert the user that the data he entered is illegal.In this post we will see how to Validate user input using MVVM binding.I have created 2 different templates for input validation :Click WPFI

《Programming WPF》翻译 第7章 5.可视化层编程

原文:<Programming WPF>翻译 第7章 5.可视化层编程 形状元素能提供一种便利的方式与图形一起工作,在一些情形中,添加表示绘图的元素到UI树中,可能是比它的价值更加麻烦.你的数据可能被构造以一种易于编写代码的方式--简单地表现一系列基于数据的绘图操作,而不是构造一棵对象树. WPF提供一个“可视化层”API,作为一个对形状元素较低级别的折中.(实际上,形状元素全都在可视化层得顶部被实现.)这个API使我们编写按需生成的代码. 可视化是一个可见的对象.WPF应用程序的外观是将它所