Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why won't my Quaternion rotate properly?

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;
        }
}
like image 227
Zeveso Avatar asked Sep 18 '25 04:09

Zeveso


1 Answers

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.

like image 130
AndyG Avatar answered Sep 20 '25 19:09

AndyG