WheelZoom

Topics: Developer Forum
May 10, 2012 at 4:33 AM

In the TrackBallDecorator class 

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            double zoomValue;
            double zoomFactor = 1.1;
            if(e.Delta<0)
            {
                zoomValue = zoomFactor;
            }
            else
            {
                zoomValue = 1/zoomFactor;
            }
            Viewport3D viewport3D = this.Viewport3D;
            if (viewport3D != null)
            {
                if (viewport3D.Camera != null)
                {
                    if (viewport3D.Camera.IsFrozen)
                    {
                        viewport3D.Camera = viewport3D.Camera.Clone();
                    }
                    var transform3DGroup = new Transform3DGroup();
                    transform3DGroup.Children.Add(viewport3D.Camera.Transform);
                    Vector3D projectToTrackball = ProjectToTrackball(ActualWidth, ActualHeight, e.GetPosition(this));
                    transform3DGroup.Children.Add(new ScaleTransform3D(new Vector3D(zoomValue, zoomValue, zoomValue), new Point3D(projectToTrackball.X,projectToTrackball.Y,projectToTrackball.Z)));
                    viewport3D.Camera.Transform = transform3DGroup;
                }
            }

        }
I dont know if this is a ggod way

May 11, 2012 at 5:36 AM

I rewrote the TrackBallDecorator to have Pan on left button, rotate on right and zoom on wheel.

The rotation is a bit strange but the rest works ok:

//---------------------------------------------------------------------------
//
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Limited Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/limitedpermissivelicense.mspx
// All other rights reserved.
//
// This file is part of the 3D Tools for Windows Presentation Foundation
// project.  For more information, see:
// 
// http://CodePlex.com/Wiki/View.aspx?ProjectName=3DTools
//
// The following article discusses the mechanics behind this
// trackball implementation: http://viewport3d.com/trackball.htm
//
// Reading the article is not required to use this sample code,
// but skimming it might be useful.
//
//---------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Shapes;
using System.Windows.Input;
using System.Windows.Markup; // IAddChild, ContentPropertyAttribute

namespace _3DTools
{
    public class TrackballDecorator : Viewport3DDecorator
    {
        public TrackballDecorator()
        {
            // used so that we always get events while activity occurs within
            // the viewport3D
            _eventSource = new Border();
            _eventSource.Background = Brushes.Transparent;
            
            PreViewportChildren.Add(_eventSource);
        }

        /// <summary>
        ///     A transform to move the camera or scene to the trackball's
        ///     current orientation and scale.
        /// </summary>
        public Transform3D Transform
        {
            get
            {
                if (this.Viewport3D == null)
                    return Transform3D.Identity;
                return this.Viewport3D.Camera.Transform;
            }
        }

        #region Event Handling

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            _previousPosition2D = e.GetPosition(this);
            _previousPosition3D = ProjectToTrackball(ActualWidth, ActualHeight, _previousPosition2D);
            if (Mouse.Captured == null)
            {
                Mouse.Capture(this, CaptureMode.Element);
            }
        }

        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);

            if (IsMouseCaptured)
            {
                Mouse.Capture(this, CaptureMode.None);
            }
        }

        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);
            double zoomValue;
            const double zoomFactor = 1.1;
            if (e.Delta < 0)
            {
                zoomValue = zoomFactor;
            }
            else
            {
                zoomValue = 1 / zoomFactor;
            }
           
            var scaleTransform3D = new ScaleTransform3D(new Vector3D(zoomValue, zoomValue, zoomValue), ScreenToWorld(e.GetPosition(this)));
            UpdateCameraTransform(scaleTransform3D);

        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (IsMouseCaptured)
            {
                Point currentPosition = e.GetPosition(this);

                // avoid any zero axis conditions
                if (currentPosition == _previousPosition2D) return;

                // Prefer tracking to zooming if both buttons are pressed.
                if (e.LeftButton == MouseButtonState.Pressed)
                {
                    TranslateTransform3D translateTransform3D = Pan(currentPosition);
                    UpdateCameraTransform(translateTransform3D);
                }
                else if (e.RightButton == MouseButtonState.Pressed)
                {
                    RotateTransform3D rotateTransform3D = Track(currentPosition);
                    UpdateCameraTransform(rotateTransform3D);
                }

                _previousPosition2D = currentPosition;
                _previousPosition3D = ProjectToTrackball(ActualWidth, ActualHeight, currentPosition);
            }
        }

        private void UpdateCameraTransform(Transform3D transform3D)
        {
            Viewport3D viewport3D = this.Viewport3D;
            if (viewport3D != null)
            {
                if (viewport3D.Camera != null)
                {
                    if (viewport3D.Camera.IsFrozen)
                    {
                        viewport3D.Camera = viewport3D.Camera.Clone();
                    }
                    var transform3DGroup = new Transform3DGroup();
                    transform3DGroup.Children.Add(viewport3D.Camera.Transform);
                    transform3DGroup.Children.Add(transform3D);
                    viewport3D.Camera.Transform = transform3DGroup;
                }
            }
        }

        #endregion Event Handling

        private TranslateTransform3D Pan(Point currentPosition)
        {
            //Vector3D currentPosition3D = ProjectToTrackball(ActualWidth, ActualHeight, currentPosition);
            Vector3D panVector3D = ScreenToWorld(_previousPosition2D) - ScreenToWorld(currentPosition);
            var translateTransform3D = new TranslateTransform3D(panVector3D);
            return translateTransform3D;
        }

        private RotateTransform3D Track(Point currentPosition)
        {
            Vector3D currentPosition3D = ProjectToTrackball(ActualWidth, ActualHeight, currentPosition);

            Vector3D axis = Vector3D.CrossProduct(_previousPosition3D, currentPosition3D);
            double angle = Vector3D.AngleBetween(_previousPosition3D, currentPosition3D);

            if (axis.Length == 0) 
                return new RotateTransform3D();
            Quaternion delta = new Quaternion(axis, -angle);
            return new RotateTransform3D(new QuaternionRotation3D(delta),ScreenToWorld(currentPosition));
        }

        private Camera Camera
        {
            get
            {
                if (this.Viewport3D == null)
                    return null;
                return this.Viewport3D.Camera;
            }
        }

        private Point3D ScreenToWorld(Point point)
        {
            bool success = false;
            Matrix3D worldToViewportTransform = MathUtils.TryWorldToViewportTransform((Viewport3DVisual) VisualTreeHelper.GetParent(this.Viewport3D.Children[0]), out success);
            if(success && worldToViewportTransform.HasInverse)
            {
                var point3D = new Point3D(point.X, point.Y, 0);
                worldToViewportTransform.Invert();
                Point3D transformed = worldToViewportTransform.Transform(point3D);
                return transformed;
            }
            else
            {
                return new Point3D(0,0,0);
            }
        }

        private Vector3D ProjectToTrackball(double width, double height, Point point)
        {
            if(this.Camera==null)
                return new Vector3D(0,0,0);
            double x = point.X / (width / 2);    // Scale so bounds map to [0,0] - [2,2]
            double y = point.Y / (height / 2);

            x = x - 1;                           // Translate 0,0 to the center
            y = 1 - y;                           // Flip so +Y is up instead of down

            double z2 = 1 - x * x - y * y;       // z^2 = 1 - x^2 - y^2
            double z = z2 > 0 ? Math.Sqrt(z2) : 0;

            return new Vector3D(x, y, z);
        }

        private Point _previousPosition2D;
        private Vector3D _previousPosition3D = new Vector3D(0, 0, 1);

        private Border _eventSource;        
    }
}