protected override void BeginTransition3D(TransitionElement transitionElement, ContentPresenter oldContent, ContentPresenter newContent, Viewport3D viewport)
        {
            Brush clone = CreateBrush(oldContent);

            Size size = transitionElement.RenderSize;
            MeshGeometry3D leftDoor = CreateMesh(new Point3D(),
               new Vector3D(size.Width / 2, 0, 0),
               new Vector3D(0, size.Height, 0),
               1,
               1,
               new Rect(0, 0, 0.5, 1));

            GeometryModel3D leftDoorGeometry = new GeometryModel3D();
            leftDoorGeometry.Geometry = leftDoor;
            leftDoorGeometry.Material = new DiffuseMaterial(clone);

            AxisAngleRotation3D leftRotation = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
            leftDoorGeometry.Transform = new RotateTransform3D(leftRotation);

            GeometryModel3D rightDoorGeometry = new GeometryModel3D();
            MeshGeometry3D rightDoor = CreateMesh(new Point3D(size.Width / 2, 0, 0),
                 new Vector3D(size.Width / 2, 0, 0),
                 new Vector3D(0, size.Height, 0),
                 1,
                 1,
                 new Rect(0.5, 0, 0.5, 1));

            rightDoorGeometry.Geometry = rightDoor;
            rightDoorGeometry.Material = new DiffuseMaterial(clone);

            AxisAngleRotation3D rightRotation = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
            rightDoorGeometry.Transform = new RotateTransform3D(rightRotation, size.Width, 0, 0);


            Model3DGroup doors = new Model3DGroup();
            doors.Children.Add(leftDoorGeometry);
            doors.Children.Add(rightDoorGeometry);

            ModelVisual3D model = new ModelVisual3D();
            model.Content = doors;

            // Replace old content in visual tree with new 3d model
            transitionElement.HideContent(oldContent);
            viewport.Children.Add(model);

            DoubleAnimation da = new DoubleAnimation(90 - 0.5 * FieldOfView, Duration);
            leftRotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, da);

            da = new DoubleAnimation(-(90 - 0.5 * FieldOfView), Duration);
            rightRotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, da);

            da = new DoubleAnimation(0, Duration);
            da.Completed += delegate
            {
                EndTransition(transitionElement, oldContent, newContent);
            };
            clone.BeginAnimation(Brush.OpacityProperty, da);
        }
        protected override void BeginTransition3D(TransitionElement transitionElement, ContentPresenter oldContent, ContentPresenter newContent, Viewport3D viewport)
        {
            Size size = transitionElement.RenderSize;

            // Create a rectangle
            MeshGeometry3D mesh = CreateMesh(new Point3D(),
                new Vector3D(size.Width, 0, 0),
                new Vector3D(0, size.Height, 0),
                1,
                1,
                new Rect(0, 0, 1, 1));
            
            GeometryModel3D geometry = new GeometryModel3D();
            geometry.Geometry = mesh;
            VisualBrush clone = new VisualBrush(oldContent);
            geometry.Material = new DiffuseMaterial(clone);

            ModelVisual3D model = new ModelVisual3D();
            model.Content = geometry;

            // Replace old content in visual tree with new 3d model
            transitionElement.HideContent(oldContent);
            viewport.Children.Add(model);

            Vector3D axis;
            Point3D center = new Point3D();
            switch (Direction)
            {
                case RotateDirection.Left:
                    axis = new Vector3D(0, 1, 0);
                    break;
                case RotateDirection.Right:
                    axis = new Vector3D(0, -1, 0);
                    center = new Point3D(size.Width, 0, 0);
                    break;
                case RotateDirection.Up:
                    axis = new Vector3D(-1, 0, 0);
                    break;
                default:
                    axis = new Vector3D(1, 0, 0);
                    center = new Point3D(0, size.Height, 0);
                    break;
            }
            AxisAngleRotation3D rotation = new AxisAngleRotation3D(axis, 0);
            model.Transform = new RotateTransform3D(rotation, center);

            DoubleAnimation da = new DoubleAnimation(0, Duration);
            clone.BeginAnimation(Brush.OpacityProperty, da);

            da = new DoubleAnimation(90, Duration);
            da.Completed += delegate
            {
                EndTransition(transitionElement, oldContent, newContent);
            };
            rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, da);
        }
        protected override void BeginTransition3D(TransitionElement transitionElement, ContentPresenter oldContent, ContentPresenter newContent, Viewport3D viewport)
        {
            Size size = transitionElement.RenderSize;

            Point3D centerPoint;
            if (MouseAsCenter)
            {
                Point mouse2D = Mouse.GetPosition(transitionElement);
                centerPoint = new Point3D(mouse2D.X, mouse2D.Y, 0.5 * size.Width);
            }
            else
            {
                centerPoint = new Point3D(0.5 * size.Width, 0.5 * size.Height, 0.5 * size.Width);
            }

            int xparticles = 10, yparticles = 10;

            if (size.Width > size.Height)
            {
                yparticles = (int)(xparticles * size.Height / size.Width);
            }
            else
            {
                xparticles = (int)(yparticles * size.Width / size.Height);
            }

            double sx = 1.0 / xparticles, sy = 1.0 / yparticles;
            Vector3D u = new Vector3D(size.Width * sx, 0, 0);
            Vector3D v = new Vector3D(0, size.Height * sy, 0);
            Brush cloneBrush = CreateBrush(oldContent);
            Material clone = new DiffuseMaterial(cloneBrush);

            Vector3D[] velocities = new Vector3D[xparticles * yparticles];
            Vector3D[] angularVelocities = new Vector3D[xparticles * yparticles];
            Point3D[] centers = new Point3D[xparticles * yparticles];

            Point3DCollection positions = new Point3DCollection(4 * xparticles * yparticles);
            PointCollection textures = new PointCollection(4 * xparticles * yparticles);
            Int32Collection triangles = new Int32Collection(6 * xparticles * yparticles);
            int n = 0;
            for (int i = 0; i < xparticles; i++)
            {
                for (int j = 0; j < yparticles; j++)
                {
                    Point3D topleft = (Point3D)(i * u + j * v);
                    positions.Add(topleft);
                    positions.Add(topleft + u);
                    positions.Add(topleft + u + v);
                    positions.Add(topleft + v);

                    textures.Add(new Point(i * sx, j * sy));
                    textures.Add(new Point((i + 1) * sx, j * sy));
                    textures.Add(new Point((i + 1) * sx, (j + 1) * sy));
                    textures.Add(new Point(i * sx, (j + 1) * sy));


                    triangles.Add(n);
                    triangles.Add(n + 2);
                    triangles.Add(n + 1);

                    triangles.Add(n);
                    triangles.Add(n + 3);
                    triangles.Add(n + 2);

                    Vector3D f0 = positions[n] - centerPoint;
                    Vector3D f1 = positions[n + 1] - centerPoint;
                    Vector3D f2 = positions[n + 2] - centerPoint;
                    Vector3D f3 = positions[n + 3] - centerPoint;

                    f0 = f0 / f0.LengthSquared;
                    f1 = f1 / f1.LengthSquared;
                    f2 = f2 / f2.LengthSquared;
                    f3 = f3 / f3.LengthSquared;

                    velocities[n / 4] = 2 * size.Width * (f0 + f1 + f2 + f3);

                    Point3D center = centers[n / 4] = (Point3D)((i + 0.5) * u + (j + 0.5) * v);
                    angularVelocities[n / 4] = 200 * (Vector3D.CrossProduct(f0, positions[n] - center) +
                        Vector3D.CrossProduct(f1, positions[n + 1] - center) +
                        Vector3D.CrossProduct(f2, positions[n + 2] - center) +
                        Vector3D.CrossProduct(f3, positions[n + 3] - center));

                    n += 4;
                }
            }

            MeshGeometry3D mesh = new MeshGeometry3D();
            mesh.Positions = positions;
            mesh.TextureCoordinates = textures;
            mesh.TriangleIndices = triangles;

            GeometryModel3D geometryModel = new GeometryModel3D(mesh, clone);
            geometryModel.BackMaterial = clone;
            ModelVisual3D model = new ModelVisual3D();
            model.Content = geometryModel;

            // Replace old content in visual tree with new 3d model
            transitionElement.HideContent(oldContent);
            viewport.Children.Add(model);

            DispatcherTimer timer = new DispatcherTimer();
            int t = 0;
            double opacityDelta = 1.0 / (Duration.TimeSpan.Seconds * 60.0);
            timer.Interval = TimeSpan.FromSeconds(1.0 / 60.0);
            timer.Tick += delegate
            {
                t++;
                cloneBrush.Opacity = 1 - t * opacityDelta;
                if (cloneBrush.Opacity < opacityDelta)
                {
                    timer.Stop();
                    EndTransition(transitionElement, oldContent, newContent);
                    return;
                }
                mesh.Positions = null;
                AxisAngleRotation3D axisAngle = new AxisAngleRotation3D();
                RotateTransform3D rotation = new RotateTransform3D(axisAngle, new Point3D());
                for (int i = 0; i < positions.Count; i += 4)
                {
                    Vector3D velocity = velocities[i / 4];

                    axisAngle.Axis = angularVelocities[i / 4];
                    axisAngle.Angle = angularVelocities[i / 4].Length;
                    rotation.CenterX = centers[i / 4].X;
                    rotation.CenterY = centers[i / 4].Y;
                    rotation.CenterZ = centers[i / 4].Z;

                    positions[i] = rotation.Transform(positions[i]) + velocity;
                    positions[i + 1] = rotation.Transform(positions[i + 1]) + velocity;
                    positions[i + 2] = rotation.Transform(positions[i + 2]) + velocity;
                    positions[i + 3] = rotation.Transform(positions[i + 3]) + velocity;

                    centers[i / 4] += velocity;
                }
                mesh.Positions = positions;
            };
            timer.Start();            
        }
        protected override void BeginTransition3D(TransitionElement transitionElement, ContentPresenter oldContent, ContentPresenter newContent, Viewport3D viewport)
        {
            Size size = transitionElement.RenderSize;

            Point3D origin = new Point3D(); // origin of 2nd face
            Vector3D u = new Vector3D(), v = new Vector3D(); // u & v vectors of 2nd face

            double angle = Angle;
            Point3D rotationCenter;
            Vector3D rotationAxis;
            RotateDirection direction = Direction;

            TranslateTransform3D translation = null;
            double angleRads = Angle * Math.PI / 180;
            if (direction == RotateDirection.Left || direction == RotateDirection.Right)
            {
                if (Contained)
                {
                    rotationCenter = new Point3D(direction == RotateDirection.Left ? size.Width : 0, 0, 0);
                    translation = new TranslateTransform3D();
                    DoubleAnimation x = new DoubleAnimation(direction == RotateDirection.Left ? -size.Width : size.Width, Duration);
                    translation.BeginAnimation(TranslateTransform3D.OffsetXProperty, x);
                }
                else
                {
                    rotationCenter = new Point3D(size.Width / 2, 0, size.Width / 2 * Math.Tan(angle / 2 * Math.PI / 180));
                }
                
                rotationAxis = new Vector3D(0, 1, 0);

                if (direction == RotateDirection.Left)
                {
                    u.X = -size.Width * Math.Cos(angleRads);
                    u.Z = size.Width * Math.Sin(angleRads);

                    origin.X = size.Width;
                }
                else
                {
                    u.X = -size.Width * Math.Cos(angleRads);
                    u.Z = -size.Width * Math.Sin(angleRads);

                    origin.X = -u.X;
                    origin.Z = -u.Z;
                }
                v.Y = size.Height;
            }
            else
            {
                if (Contained)
                {
                    rotationCenter = new Point3D(0, direction == RotateDirection.Up ? size.Height : 0, 0);
                    translation = new TranslateTransform3D();
                    DoubleAnimation y = new DoubleAnimation(direction == RotateDirection.Up ? -size.Height : size.Height, Duration);
                    translation.BeginAnimation(TranslateTransform3D.OffsetYProperty, y);
                }
                else
                {
                    rotationCenter = new Point3D(0, size.Height / 2, size.Height / 2 * Math.Tan(angle / 2 * Math.PI / 180));
                }
                
                rotationAxis = new Vector3D(1, 0, 0);

                if (direction == RotateDirection.Up)
                {
                    v.Y = -size.Height * Math.Cos(angleRads);
                    v.Z = size.Height * Math.Sin(angleRads);

                    origin.Y = size.Height;
                }
                else
                {
                    v.Y = -size.Height * Math.Cos(angleRads);
                    v.Z = -size.Height * Math.Sin(angleRads);

                    origin.Y = -v.Y;
                    origin.Z = -v.Z;
                }
                u.X = size.Width;
            }

            double endAngle = 180 - angle;
            if (direction == RotateDirection.Right || direction == RotateDirection.Up)
                endAngle = -endAngle;

            ModelVisual3D m1, m2;
            viewport.Children.Add(m1 = MakeSide(oldContent, new Point3D(), new Vector3D(size.Width,0,0), new Vector3D(0,size.Height,0), endAngle, rotationCenter, rotationAxis, null));
            viewport.Children.Add(m2 = MakeSide(newContent, origin, u, v, endAngle, rotationCenter, rotationAxis, delegate
                {
                    EndTransition(transitionElement, oldContent, newContent);
                }));

            m1.Transform = m2.Transform = translation;

            // Replace old and new content in visual tree with new 3d models
            transitionElement.HideContent(oldContent);
            transitionElement.HideContent(newContent);
        }
        protected override void BeginTransition3D(TransitionElement transitionElement, ContentPresenter oldContent, ContentPresenter newContent, Viewport3D viewport)
        {
            int xparticles = 10, yparticles = 10;
            Size size = transitionElement.RenderSize;

            if (size.Width > size.Height)
                yparticles = (int)(xparticles * size.Height / size.Width);
            else
                xparticles = (int)(yparticles * size.Width / size.Height);

            MeshGeometry3D mesh = CreateMesh(new Point3D(), new Vector3D(size.Width, 0, 0), new Vector3D(0, size.Height, 0), xparticles - 1, yparticles - 1, new Rect(0, 0, 1, 1));
            Brush cloneBrush = CreateBrush(oldContent);
            Material clone = new DiffuseMaterial(cloneBrush);
            

            double ustep = size.Width / (xparticles - 1), vstep = size.Height / (yparticles - 1);

            Point3DCollection points = mesh.Positions;

            
            Point3DCollection oldPoints = points.Clone();

            double timeStep = 1.0 / 30.0;
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(timeStep);
            double time = 0;
            double duration = this.Duration.HasTimeSpan ? this.Duration.TimeSpan.TotalSeconds : 2;
            timer.Tick += delegate
            {
                time = time + timeStep;
                Point mousePos = Mouse.GetPosition(viewport);
                Point3D mousePos3D = new Point3D(mousePos.X, mousePos.Y, -10);
                    
                // Cloth physics based on work of Thomas Jakobsen http://www.ioi.dk/~thomas
                for (int i = 0; i < oldPoints.Count; i++)
                {
                    Point3D currentPoint = points[i];
                    Point3D newPoint = currentPoint + 0.9 * (currentPoint - oldPoints[i]);

                    if (newPoint.Y > size.Height)
                        newPoint.Y = size.Height;

                    oldPoints[i] = newPoint;
                }

                //for (int j = 0; j < 5; j++)
                //for (int i = oldPoints.Count - 1; i > 0 ; i--)
                for (int a = yparticles - 1; a >= 0; a--)
                for (int b = xparticles - 1; b >= 0; b--)
                {
                    int i = b * yparticles + a;
                    // constrain with point to the left
                    if (i > yparticles)
                        Constrain(oldPoints, i, i - yparticles, ustep);
                    // constrain with point to the top
                    if (i % yparticles != 0)
                        Constrain(oldPoints, i, i - 1, vstep);

                    // constrain the sides 
                    if (a == 0)
                        oldPoints[i] = new Point3D(oldPoints[i].X, 0, oldPoints[i].Z);
                    if (a == yparticles - 1)
                        oldPoints[i] = new Point3D(oldPoints[i].X, size.Height, oldPoints[i].Z);

                    if (b == 0)
                        oldPoints[i] = new Point3D(0, a * size.Height / (yparticles - 1), 0);

                    if (b == xparticles - 1)
                    {
                        double angle = time / duration * Math.PI / (0.8 + 0.5 * (yparticles - (double)a) / yparticles);
                        oldPoints[i] = new Point3D(size.Width * Math.Cos(angle), a * size.Height / (yparticles - 1), -size.Width * Math.Sin(angle));
                    }
                }

                if (time > (duration - 0))
                {
                    timer.Stop();
                    EndTransition(transitionElement, oldContent, newContent);
                }

                // Swap position arrays
                mesh.Positions = oldPoints;
                oldPoints = points;
                points = mesh.Positions;
            };
            timer.Start();
            

            GeometryModel3D geo = new GeometryModel3D(mesh, clone);
            geo.BackMaterial = clone;
            ModelVisual3D model = new ModelVisual3D();
            model.Content = geo;

            // Replace old content in visual tree with new 3d model
            transitionElement.HideContent(oldContent);
            viewport.Children.Add(model);
        }