• 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…

    Four-point-transformation

    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.

    Sketch: Image

    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?

    Sketch: Camera Position

    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 (0|0|64.5). We’re going to calculate x using tangent.

    egin{align*}	an 22.5^{circ}&=frac{64.5}{x}\Rightarrow x&=frac{64.5}{	an 22.5^{circ}}end{align*}

    Now if we put the camera at left(0|0|frac{64.5}{	an 22.5^{circ}}+64.5
ight) 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:

    egin{align*}	an22.5^{circ}&=frac{s}{64.5}\Rightarrow s&=64.5*	an22.5^{circ}end{align*}

    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.

  • 相关阅读:
    Mybatis框架学习笔记一(基于注解的配置)
    HDU 1686 Oulipo (KMP模板题)
    监控Windows性能指标
    Locust设置检查点
    《TCP/IP网络编程》读书笔记
    基于单向链表的队列的实现
    windows下基于异步通知IO模型的回声服务器和客户端的实现
    c语言实现迭代器iterator
    c语言hash表的实现
    c语言双向链表的实现
  • 原文地址:https://www.cnblogs.com/itelite/p/4704159.html
Copyright © 2020-2023  润新知