how can I rotate object around a specific axis with specific angle?

Nov 10, 2010 at 3:35 PM

I miss this method from WPF, is there anyway to do it in Balder, I guess it will concern heavily to Maths but I'm not good at it.

Very grateful for any help

Coordinator
Nov 14, 2010 at 9:27 PM

Sorry. Didn't see this post. 

Basically what is missing is a method on the Matrix class that can create a Matrix object from a given axis and the angle within that axis. Something that is very useful for a lot of scenarios, so I'll put up a work item and try to get to that soon.

 

Coordinator
Nov 14, 2010 at 9:28 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Nov 16, 2010 at 12:44 AM

Here's the code I added to Balder.Math.Matrix for this situation:

        public static Matrix CreateRotation(Vector axis, float degrees)
        {
            var matrix = new Matrix();
            var c = (float)System.Math.Cos(MathHelper.ToRadians(degrees));
            var s = (float)System.Math.Sin(MathHelper.ToRadians(degrees));
            var t = 1 - c;

            axis.Normalize();

            var x = axis.X;
            var y = axis.Y;
            var z = axis.Z;

            matrix.M11 = t * x * x + c;
            matrix.M21 = t * x * y + s * z;
            matrix.M31 = t * x * z - s * y;
            matrix.M41 = 0f;
            matrix.M12 = t * x * y - s * z;
            matrix.M22 = t * y * y + c;
            matrix.M32 = t * y * z + s * x;
            matrix.M42 = 0f;
            matrix.M13 = t * x * y + s * y;
            matrix.M23 = t * y * z - s * x;
            matrix.M33 = t * z * z + c;
            matrix.M43 = 0f;
            matrix.M14 = 0f;
            matrix.M24 = 0f;
            matrix.M34 = 0f;
            matrix.M44 = 1f;
            return matrix;
        }

However, if you're doing many, many rotations, (by repeatedly multiplying rotation matrices together), you'll find that numerical errors will be introduced that will eventually warp your geometries.  This is a fundamental issue with rotation matrices and is caused because they are over-specified.  The solution is to use quaternions to represent rotation.  Quaternions can be multiplied together, just like matrices, and when you're ready to use them, you can convert them into a rotation matrix.  But you keep the quaternion as the gold standard of the rotation.  (Also, quaternions can be computationally inexpensive compared to matrices.)

If anyone is interested, here is the quaternion class I put together for this purpose.  It can also create a rotation from an axis an angle, just like the code above.

namespace Bob1492
{
    public struct Quaternion
    {
        public Quaternion(Balder.Math.Vector rotationAxis, double angleInRadians)
        {
            double t = angleInRadians / 2.0;

            this.Q1 = Math.Cos(t);
            this.Q2 = rotationAxis.X * Math.Sin(t);
            this.Q3 = rotationAxis.Y * Math.Sin(t);
            this.Q4 = rotationAxis.Z * Math.Sin(t);
        }

        public static Quaternion FromAxisAngle(Balder.Math.Vector rotationAxis, double angleInDegrees)
        {
            Balder.Math.Vector v = rotationAxis;
            v.Normalize();

            double t = angleInDegrees / 180 * Math.PI / 2.0;

            Quaternion Q = new Quaternion();

            Q.Q1 = Math.Cos(t);
            Q.Q2 = v.X * Math.Sin(t);
            Q.Q3 = v.Y * Math.Sin(t);
            Q.Q4 = v.Z * Math.Sin(t);

            return Q;
        }

        public Balder.Math.Matrix ToMatrix()
        {
            double TX = Q2 * Q2;
            double TY = Q3 * Q3;
            double TZ = Q4 * Q4;
            double TQ = TY + TZ;

            double TK = 0;
            double denominator = TQ + TX + Q1 * Q1;
            if (denominator != 0)
            {
                TK = 2 / (denominator);
            }

            Balder.Math.Matrix m = new Balder.Math.Matrix();

            m.M11 = Convert.ToSingle(1 - TK*TQ);
            m.M22 = Convert.ToSingle(1 - TK*(TX + TZ));
            m.M33 = Convert.ToSingle(1 - TK*(TX + TY));
            TX     = TK*Q2;  
            TY     = TK*Q3;   
            TQ     = (TK*Q4)*Q1; 
            TK     = TX*Q3;
            m.M12 = Convert.ToSingle(TK - TQ);
            m.M21 = Convert.ToSingle(TK + TQ);
            TQ     = TY*Q1;
            TK     = TX*Q4;
            m.M13 = Convert.ToSingle(TK+TQ);
            m.M31 = Convert.ToSingle(TK-TQ);
            TQ     = TX*Q1;   
            TK     = TY*Q4;
            m.M23 = Convert.ToSingle(TK - TQ);
            m.M32   =   Convert.ToSingle(TK + TQ);

            return m;
        }

        public static Quaternion operator *(Quaternion L, Quaternion R)
        {
            Quaternion Q = new Quaternion();

            Q.Q1 = L.Q1 * R.Q1 - L.Q2 * R.Q2 - L.Q3 * R.Q3 - L.Q4 * R.Q4; 
            Q.Q2 = L.Q2 * R.Q1 + L.Q1 * R.Q2 - L.Q4 * R.Q3 + L.Q3 * R.Q4;
            Q.Q3 = L.Q3 * R.Q1 + L.Q4 * R.Q2 + L.Q1 * R.Q3 - L.Q2 * R.Q4;
            Q.Q4    =   L.Q4 * R.Q1 - L.Q3 * R.Q2 + L.Q2 * R.Q3 + L.Q1 * R.Q4;

            return Q;
        }

        public static Quaternion Identity
        {
            get
            {
                return new Quaternion { Q1 = 1 };
            }
        }

        #region Private Members

        private double Q1;
        private double Q2;
        private double Q3;
        private double Q4;




        #endregion
    }
}

 

Nov 16, 2010 at 5:08 PM

Thank you !!

Nov 17, 2010 at 11:28 AM
Edited Nov 17, 2010 at 1:21 PM

wow this quaternion really works like we think it's supposed to, thanks a lot bob1492

it actually solves most problems with controlling the rotation of our objects here at SVS

as discused in the discussion named "quaternion rotations .."

Coordinator
Nov 17, 2010 at 5:56 PM

Great work bob1492 - would you mind me pulling that specific functionality into Balders quaternion struct, and also the CreateRotation() method you've created?

Nov 18, 2010 at 5:20 AM
Definitely! Glad it helps.