3

Ok, we are not talking about OpenGL with this question, but this will be used with OpenGL ES 2.0.

Question: How do create and rotate a Quaternion with the following code?

I have been reading up and studying about this and still can't quite gasp the concepts. I thought I understood it, but once I started making some calculations to rotate the quaternion I realized I can't even get back to where I started.

So let us say that we have a cube, and the center of it is at (0, 0, 0). We want to rotate it on the x-axis by 45 degrees. What would I do? (Only the Quaternion)

Assuming success, how would you get the amount of rotation from 'W'? I know that '1' indicates that there is no rotation, but what if it was rotated 173 degrees?

Trying to rotate to a given direction, 45 degrees, and then get that value from W. I feel like I need to convert the angle to rads or something, but not exactly sure. Tutorials online vary from one to the next.

Here is my code:

import java.util.Scanner;
import Quaternion;

public class Main {

    public static void main(String[] args) {
        Quaternion q1 = new Quaternion(0, 0, 0, 1);
        Quaternion q2 = new Quaternion(0, 0, 0, (float) Math.cos(toRAD(45.0f) / 2));

        q1 = q2.mul(q1);

        System.out.println("q1: " + q1.getX() + ", " + q1.getY() + ", " + q1.getZ() + " with " + toANGLE(2.0f * Math.acos(q1.getW())));
    }

    private static double toRAD(float angle) {
        return angle * (Math.PI / 180.0f);
    }

    private static float toANGLE(double rad) {
        return (float) (rad * (180.0f / Math.PI));
    }
}

Here is the code for a Quaternion:

public class Quaternion  // Credit goes to 'thebennybox' (http://www.youtube.com/user/thebennybox)
{
        private float x;
        private float y;
        private float z;
        private float w;

        public Quaternion(float x, float y, float z, float w)
        {
                this.x = x;
                this.y = y;
                this.z = z;
                this.w = w;
        }

        public float length()
        {
                return (float)Math.sqrt(x * x + y * y + z * z + w * w);
        }

        public Quaternion normalize()
        {
                float length = length();

                return new Quaternion(x / length, y / length, z / length, w / length);
        }

        public Quaternion conjugate()
        {
                return new Quaternion(-x, -y, -z, w);
        }

        public Quaternion mul(Quaternion r)
        {
                float w_ = w * r.getW() - x * r.getX() - y * r.getY() - z * r.getZ();
                float x_ = x * r.getW() + w * r.getX() + y * r.getZ() - z * r.getY();
                float y_ = y * r.getW() + w * r.getY() + z * r.getX() - x * r.getZ();
                float z_ = z * r.getW() + w * r.getZ() + x * r.getY() - y * r.getX();

                return new Quaternion(x_, y_, z_, w_);
        }

        public Quaternion mul(Vector3f r)
        {
                float w_ = -x * r.getX() - y * r.getY() - z * r.getZ();
                float x_ =  w * r.getX() + y * r.getZ() - z * r.getY();
                float y_ =  w * r.getY() + z * r.getX() - x * r.getZ();
                float z_ =  w * r.getZ() + x * r.getY() - y * r.getX();

                return new Quaternion(x_, y_, z_, w_);
        }

        public float getX()
        {
                return x;
        }

        public void setX(float x)
        {
                this.x = x;
        }

        public float getY()
        {
                return y;
        }

        public void setY(float y)
        {
                this.y = y;
        }

        public float getZ()
        {
                return z;
        }

        public void setZ(float z)
        {
                this.z = z;
        }

        public float getW()
        {
                return w;
        }

        public void setW(float w)
        {
                this.w = w;
        }
}
Zeveso
  • 1,274
  • 3
  • 21
  • 41
  • 1
    Yes, you need to provide radians to Java's math functions. – AndyG Oct 24 '13 at 16:05
  • OHH, it already needs to be in RAD. Thanks for the correction AndyG! – Zeveso Oct 24 '13 at 16:07
  • :) Did this solve your problem or are your quaternions still not working? If the latter is the case, you may need to update your question. – AndyG Oct 24 '13 at 19:56
  • @AndyG I did update my question. The code works for the first System.println(), but not the second. I have been playing with it, but still can't get it to work. – Zeveso Oct 24 '13 at 23:04

1 Answers1

4

I'm still not 100% sure what your question is asking, but I'll give it a shot.

Problem:

Given a quaternion representing a 0 degree rotation about x, y, z, generate a new quaternion representing a 45 degree rotation about the x axis

  • Start with a quaternion representing no rotation, call it q1

q1 = (w1, x1, y1, z1)

q1.w1 = cos(0/2) = 1

q1.x1 = 0 * sin(0/2) = 0

q1.y1 = 0 * sin(0/2) = 0

q1.z1 = 0 * sin(0/2) = 0

So q1 = (1, 0, 0, 0)

  • Generate a new rotation that is 45 degrees (PI/4 radians) about the X axis We need a temporary quaternion to modify q1. Let's call it q2.

q2 = (w2, x2, y2, z2)

q2.w2 = cos(PI/4 / 2) = cos(PI/8)

q2.x2 = 1.0 * sin(PI/4 / 2) = 1.0 * sin(PI/8) = sin(PI/8)

q2.y2 = 0.0 * sin(PI/4 / 2) = 0.0

q2.z2 = 0.0 * sin(PI/4 / 2) = 0.0

so q2 = (cos(PI/8), sin(PI/8), 0, 0)

  • Now this last step is important, you modify your original quaternion by a left-hand multiplication of the temporary quaternion

What I mean is this:

q1 = q2 * q1

Your multiplication function is written correctly, so the problem is not there. Remember that quaternion multiplications are not commutative. That is q2 * q1 is NOT the same as q1*q2!

At this point q1 is modified to represent a 45 degree rotation about the X axis.

To print out the angle in degrees, you need to compute 2.0 * acos(q1.w) / PI * 180

Your code is incorrectly computing q1.w/PI * 180 to get the angle in degrees.

More specifically, change

toANGLE(resQuat.getW())

to

toANGLE(2.0f * Math.acos(resQuat.getW()))

I haven't looked at your code beyond that, but try applying these concepts and see if that fixes your problem.

AndyG
  • 39,700
  • 8
  • 109
  • 143
  • 1
    Very helpful, thank you very much! - You explained it great where others did not. – Zeveso Oct 25 '13 at 14:49
  • 1
    However, quick question: Why is it that when W != 1 & Quaternion is normalized that W == 1 again? (1 == no rotation) – Zeveso Oct 25 '13 at 15:01
  • 2
    @Zeveso: That won't necessarily always be the case. When you normalize a quaternion you are trying to get a rotation about an axis of unit length from a rotation about an axis of non-unit length. W may be reduced if the magnitude of the quaternion was previously greater than 1, but can increase (more rotation) if the magnitude was previously less than 1. Try this example on for size: create a quaternion for 45 degree rotation about x=4, y=3, z=2. Examine w. Now normalize the quaternion and examine w again. – AndyG Oct 25 '13 at 16:26
  • 1
    WTF?? MY MIND == BLOWN!. What ever. I don't get what just happened. plz explain!!! - q1: 4.0, 3.0, 2.0 with 45.000008 - (norm) q1: 0.73208576, 0.54906434, 0.36604288 with 160.5302 - How do I protect against that? – Zeveso Oct 25 '13 at 16:57
  • @Zeveso: There's nothing to protect against; this is generally what you want for properly representing your rotations. Think of it like normalizing a vector. You don't change the direction of the vector, but the values of x, y, and z grow/shrink to compensate so that it becomes of unit length; you're not trying to only have the y axis to be 1, for example. – AndyG Oct 25 '13 at 18:48
  • So it the normalized value = to 0 rotation? Or is it equal to the 45 degrees? (160 deg Quaternion) || Take this for example. We put in a q3(9, -4, 8, 90deg) and do q1 = q3.mul(q1); after q1 = q1.norm(); How come that doesn't output as 9, -4, 8, 90deg? – Zeveso Oct 25 '13 at 20:08
  • 1
    When creating your quaternions, ensure that the axes they rotate about are normal vectors. When you multiply two unit quaternions together, you'll end up with a unit quaternion (mathematically). In principle you may need to re-normalize when the magnitude becomes some threshold away from 1.0. – AndyG Oct 25 '13 at 20:16
  • haha, got so many questions. Let me leave it at this. - Thank you. – Zeveso Oct 26 '13 at 20:10