Limit rotation Angle Extension

Nov 22, 2010 at 6:41 AM

I extended the Trackball to include a limited range for the angle rotation around the sphere (e.g. if you only want to be able see one hemisphere, set the limits of angle rotations from -90 to 90 deg).

///I added the following properties:

 private Vector3D _angleLowerLimits = new Vector3D(-180, -180, -180);

        public Vector3D AngleLowerLimits
        {
            get
            {
                return _angleLowerLimits;
            }
            set
            {
                _angleLowerLimits = value;
            }
        }

        private Vector3D _angleUpperLimits = new Vector3D(180, 180, 180);

        public Vector3D AngleUpperLimits
        {
            get
            {
                return _angleUpperLimits;
            }
            set
            {
                _angleUpperLimits = value;
            }
        }

///I modified the track function as follows:

  private void Track(Point currentPosition)
        {
            Vector3D currentPosition3D = ProjectToTrackball(
                EventSource.ActualWidth, EventSource.ActualHeight, currentPosition);

            Vector3D axis = Vector3D.CrossProduct(_previousPosition3D, currentPosition3D);
            if (!axis.Equals(new Vector3D()))
            {
                double angle = Vector3D.AngleBetween(_previousPosition3D, currentPosition3D);
                Quaternion delta = new Quaternion(axis, -angle);

                // Get the current orientantion from the RotateTransform3D
                AxisAngleRotation3D r = _rotation;
                Quaternion q = new Quaternion(_rotation.Axis, _rotation.Angle);
// here's the modification
                Quaternion qNew = q * delta;
                double x=0;
                double y=0;
                double z=0;

                GetEulerAngles(qNew, ref x, ref y, ref z);
                x *= 180 / Math.PI;
                y *= 180 / Math.PI;
                z *= 180 / Math.PI;

               if ((AngleLowerLimits.Z < z && z < AngleUpperLimits.Z)  && (AngleLowerLimits.Y < y && y < AngleUpperLimits.Y)  && (AngleLowerLimits.X < x && x < AngleUpperLimits.X ))
               {
                    // Compose the delta with the previous orientation
                    q *= delta;

                    // Write the new orientation back to the Rotation3D
                    _rotation.Axis = q.Axis;
                    _rotation.Angle = q.Angle;
              }
            }

            _previousPosition3D = currentPosition3D;
        }

 

// you also need the function GetEulerangles:

 private void GetEulerAngles(Quaternion q, ref double yaw, ref double pitch, ref double roll)
        {
            double w2 = q.W * q.W;
            double x2 = q.X * q.X;
            double y2 = q.Y * q.Y;
            double z2 = q.Z * q.Z;
            double unitLength = w2 + x2 + y2 + z2;    // Normalised == 1, otherwise correction divisor.
            double abcd = q.W * q.X + q.Y * q.Z;
            double eps = 1e-7;    // TODO: pick from your math lib instead of hardcoding.
            double pi = Math.PI; 
            if (abcd > (0.5 - eps) * unitLength)
            {
                yaw = 2 * Math.Atan2(q.Y, q.W);
                pitch = pi;
                roll = 0;
            }
            else if (abcd < (-0.5 + eps) * unitLength)
            {
                yaw = -2 * Math.Atan2(q.Y, q.W);
                pitch = -pi;
                roll = 0;
            }
            else
            {
                double adbc = q.W * q.Z - q.X * q.Y;
                double acbd = q.W * q.Y - q.X * q.Z;
                yaw = Math.Atan2(2 * adbc, 1 - 2 * (z2 + x2));
                pitch = Math.Asin(2 * abcd / unitLength);
                roll = Math.Atan2(2 * acbd, 1 - 2 * (y2 + x2));
            }
        }

 

I haven't checked if anyone has done something like this in the forum- if so please ignore. Also, this feature limits a rotation in all directions even if it's crossing an angle boundary only in one direction, but it still looks good when you use it.

 

To set the limits, just set:

   _3DTools.Trackball tb = new _3DTools.Trackball();
     tb.AngleLowerLimits = new Vector3D(-360, -90, -90);
     tb.AngleUpperLimits = new Vector3D(360, 90, 90);