# Quternions (basics)

This topic is 4924 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello! Yesterday - with a help of various resources - I've managed to implement my first quaternion class. Obviously, I've studied the topic pretty hard before attempting to do some code. Anyway - as you might guess - a problem occured. I have a quaternion representing the current rotation of an object and the second one which 'increments' the rotation (at least in theory...). Everything works fine at the beginning - the object is being rotated. The problem is that the rotation speed keeps decreasing until the rotation angle reaches 90 degrees where the object stops rotating completly! I am not sure what's the cause. Maybe it's a bug in the quaternion class code? Maybe I've misunderstood how to use quaternions properly? I'd be thankful for your help! Here is a source of the quaternion class. (forgive me my bad English commentaries, please)
#define DEG_TO_RAD(x)    x * 0.017453

// NOTE : CQuaternion class is used to represent quaternions of
//        the unit lenght. Though, all formulas used in the code
//		  take into account that every CQuaternion's magnitude
//		  equals 'one'. This way, calculations are more efficient.

class CQuaternion
{
public:

union
{
float		 q[4];		// Quaternion represented as an array
struct
{
float w;			// Scalar component
float x, y, z;  	// Vector component
};
};

// Constructors
CQuaternion() { }

CQuaternion(const CQuaternion &quat) {
q[0] = quat.q[0]; q[1] = quat.q[1]; q[2] = quat.q[2]; q[3] = quat.q[3];
}

CQuaternion(float w, float xyz[])  {
RotationAxis(w, xyz[0], xyz[1], xyz[2]);
}

CQuaternion(float w, float x, float y, float z)  {
RotationAxis(w, x, y, z);
}

// Destructor
~CQuaternion() { }

// Fundamental operations

// Sets the components of the quaternion to represent a rotation
void	RotationAxis(float angle, float x, float y, float z) {
q[0] = cosf( DEG_TO_RAD(angle) * 0.5 );  // Scalar component
float sint = sinf( DEG_TO_RAD(angle) * 0.5 );
q[1] = x * sint; // We assume that (x,y,z) form a unit-lenght vector
q[2] = y * sint;
q[3] = z * sint;
}

// Sets the quat to identity quaternion
void			SetIdentity() {
w = 1.0;
}

// Negates the quaternion (remember that negated quaternion represent the same angular displacement!)
void			Negate() 	  {
w = -w;
x = -x;
y = -y;
z = -z;
}

// Quaternion conjugate operation (negating the vector component)
CQuaternion		Conjugate()  	{
x = -x;

y = -y;
z = -z;
}

// Inverses the quaternion
// NOTE : since we deal with unit-length quaternions only,
// 		  quaternion inverse equals its conjugate. Furthermore,
//		  instead of negating the vector component of the quaternion
//		  we will negate 'w' thus reversing what is considered
//		  positive rotation by flipping the axis of rotation.
CQuaternion		Inverse() 		{
w = -w;
}

// Quaternion multiplication
// QResult = [ w1*w2 - v1 * v2  ( w1*v2 + w2*v1 + v2*v1 ) ]
CQuaternion		Mult(const CQuaternion& quat)	{

CQuaternion QResult;
QResult.q[0] = quat.q[0]*q[0]
-quat.q[1]*q[1]
-quat.q[2]*q[2]
-quat.q[3]*q[3];

QResult.q[1] = quat.q[0]*q[1]
-quat.q[1]*q[0]
-quat.q[2]*q[3]
-quat.q[3]*q[2];

QResult.q[2] = quat.q[0]*q[2]
-quat.q[1]*q[3]
-quat.q[2]*q[0]
-quat.q[3]*q[1];

QResult.q[3] = quat.q[0]*q[3]
-quat.q[1]*q[2]
-quat.q[2]*q[1]
-quat.q[3]*q[0];
return QResult;
}

// Quaternion multiplication which assigns the result to *this quaternion
void		MultAndSet(const CQuaternion& quat)	{

CQuaternion QResult;
QResult.q[0] = quat.q[0]*q[0]
-quat.q[1]*q[1]
-quat.q[2]*q[2]
-quat.q[3]*q[3];

QResult.q[1] = quat.q[0]*q[1]
-quat.q[1]*q[0]
-quat.q[2]*q[3]
-quat.q[3]*q[2];

QResult.q[2] = quat.q[0]*q[2]
-quat.q[1]*q[3]
-quat.q[2]*q[0]
-quat.q[3]*q[1];

QResult.q[3] = quat.q[0]*q[3]
-quat.q[1]*q[2]
-quat.q[2]*q[1]
-quat.q[3]*q[0];
q[0] = QResult.q[0];
q[1] = QResult.q[1];
q[2] = QResult.q[2];
q[3] = QResult.q[3];
}

// Normalizes the quaternion
void	Normalize() {
float mag = (float) sqrt(w*w + x*x + y*y + z*z);

if (mag > 0.0)
{
float oneOverMag = 1.0f/mag;
w *= oneOverMag;
x *= oneOverMag;
y *= oneOverMag;
z *= oneOverMag;
}
else SetIdentity();		// We've got a problem ...
}

// Creates a matrix basing	on the rotation stored in the quaternion
void	GetMatrix(float *matrix)	{

Normalize();

float xx = x*x;
float yy = y*y;
float zz = z*z;

matrix[0]  = 1.0f - 2.0f*(yy+zz);
matrix[4]  = 2.0f * (x*y+w*z);
matrix[9]  = 2.0f * (x*z-w*y);
matrix[12] = 0.0f;

matrix[1]  = 2.0f * (x*y-w*z);
matrix[5]  = 1.0f - 2.0f*(xx+zz);
matrix[9]  = 2.0f * (y*z+w*x);
matrix[13] = 0.0f;

matrix[2]  = 2.0f * (x*z+w*y);
matrix[6]  = 2.0f * (y*z-w*x);
matrix[10] = 1.0f - 2.0f*(xx+yy);
matrix[14] = 0.0f;

matrix[3]  = 0.0f;
matrix[7]  = 0.0f;
matrix[11] = 0.0f;
matrix[15] = 1.0f;
}
};


... and here is the actual use of the class :
	static CQuaternion quat(0.0, 0.0, 1.0, 0.0);
float			   matrix[16];

CQuaternion displace;
displace.RotationAxis(10.0 * dt, 0.0, 1.0, 0.0);

quat.MultAndSet(displace);
quat.GetMatrix(matrix);

glMultMatrixf(matrix);


Thanks in advance! [Edited by - clapton on July 23, 2005 11:24:47 AM]

##### Share on other sites
Yeah it looks like you have w in the wrong place. struct {x,y,z,w} is what I think you want.

##### Share on other sites
Quote:
 Original post by Name_UnknownYeah it looks like you have w in the wrong place. struct {x,y,z,w} is what I think you want.

Actually, all formulas where written with an assumption that w corresponds to q[0]. Anyway, I've tried what you suggest and it was not the point.

Thanks! :)

##### Share on other sites
Double-check your multiplication code. Also, is there any reason you're not zeroing out the vector component when setting to identity?

##### Share on other sites
I agree with jyk; your multiplication rules are not correct.

You have to reverse the quaternion component order (you were doing result = b*a, not result = a*b), and change the sign for quite a few of the terms.

// my code...// the correct multiplication rules (adjusted so that w is element 0... a lot of people use x as element 0 and w as element 3)// also reordered and resigned so that multiplication columns are easy to followresult.w = a.w* b.w + a.x*-b.x + a.y*-b.y + a.z*-b.zresult.x = a.w* b.x + a.x* b.w + a.y* b.z + a.z*-b.yresult.y = a.w* b.y + a.x*-b.z + a.y* b.w + a.z* b.xresult.z = a.w* b.z + a.x* b.y + a.y*-b.x + a.z* b.w// your code...// this was OK, but only by coincidence, so i changed the quaternion orderQResult.q[0] = q[0]*quat.q[0] - q[1]*quat.q[1] - q[2]*quat.q[2] - q[3]*quat.q[3];// i had to change the signs and quaternion order for the other three though...QResult.q[1] = q[0]*quat.q[1] + q[1]*quat.q[0] + q[2]*quat.q[3] - q[3]*quat.q[2];QResult.q[2] = q[0]*quat.q[2] - q[1]*quat.q[3] + q[2]*quat.q[0] + q[3]*quat.q[1];QResult.q[3] = q[0]*quat.q[3] + q[1]*quat.q[2] - q[2]*quat.q[1] + q[3]*quat.q[0];

P.S. These multiplication rules are easy to find in this format if you do some searching for "quaternion Julia set"... ie: (at the very bottom of the page).

[Edited by - taby on July 23, 2005 3:48:25 PM]

##### Share on other sites
Another thing... Boost has a great quaternion math library. Boost.org

I also have one, which contains a few functions that Boost does not. It also allows you to alter the multiplication rules at runtime; an idea that was passed along to me by Godwin Vickers of Hypercomplex.org.

Check here for the Utilities package (which contains the qmath library):

My library uses the component order x, y, z, w though, so take note of this before dissecting any code.

##### Share on other sites
you're negation is wrong to, w doesn't swap sign

##### Share on other sites
Quote:
 you're negation is wrong to, w doesn't swap sign.
You're thinking of conjugation; negating a quaternion negates all its components.

##### Share on other sites
Quote:
 Original post by Anonymous Posteryou're negation is wrong to, w doesn't swap sign

Actually, it's the conjugate where the real component is not negated.

The inverse is found by calculating the norm of the quaternion (real.real + imag_vector.imag_vector), then dividing the conjugate by this value.

Quaternion Math Tools, Douglas Sweetser

I've never actually used this form of negation before (must be a method common to 3D rotation?).

P.S. I edited my first post in this thread. It should provide you with the correct multiplication rules that you need.

##### Share on other sites

Quote:
 Original post by jykAlso, is there any reason you're not zeroing out the vector component when setting to identity?

Sure there is. :) Like I've said, the class is being made for the sake of efficiency mostly and while from practical point of view it represents a quaternion in more mathematical terms it does not. Anyway, getting straight to the problem ...

q = [ cos(omega/2) sin(omega/2) * n ]

where q is a quaternion, n is a vector of a rotation axis.

An angle which represents no angular displacement is k*360 degrees. Please note that if omega is an even multiple of 360 then cos(omega/2) = 1. If it's an odd multiple of 360, cos(omega/2) = -1. In both cases sin(omega/2) = 0, so the value of n is irrelevant.

Quote:
 Original post by tabyI agree with jyk; your multiplication rules are not correct.

I love you guys! That was the problem! :D Strange, though, since I actually copied this part from a different source code. ;)

Quote:
 The inverse is found by calculating the norm of the quaternion (real.real + imag_vector.imag_vector), then dividing the conjugate by this value.

Exactly. In my case I assumed that every quaternion is unit-length, so the formula simplifies to : inverse = conjugate (since magnitude of the quaternion is 1.0). Please don't blame me for heresies, I didn't say that my quaternion is mathematically correct. :)

Quote:
 I've never actually used this form of negation before (must be a method common to 3D rotation?).

You mean the negation I use?

Quote:
 It should provide you with the correct multiplication rules that you need.

Works perfectly! :D

Great thanks! :)

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 15
• 14
• 46
• 22
• 27
• ### Forum Statistics

• Total Topics
634047
• Total Posts
3015231
×