Somewhat offtopic but possibly useful info on quats:
I wrote the 'coordinate system' class that is mostly equivalent in usage to 4x4 matrix (can be multiplied, etc) but has quaternion and vector inside (quaternion for rotation, vector for translation). It's very convenient for doing things like that.
Explanations of how you can figure out what to do with quaternions:
The basic idea of this is very simple, mostly same as with 3x3 matrix for rotation and vector for translation.
Let's first define
Q rotate V = Q*V*~Q , in other words, rotation of vector by quaternion. In programming it can be written as Q.rotate(V)
This "rotate" is much like rotation_matrix*vector, in that it also does rotation, and all properties of rotation apply.
For instance,
1.) Q rotate (V1+V2) = (Q rotate V1) + (Q rotate V2)
2.) Q1 rotate (Q2 rotate V) = (Q1*Q2) rotate V
3.) (~Q) rotate (Q rotate V) = V
The "rotate" is really sorta multiplication & from now on i will assume it have same precedence. These formulas can all be derived from definition of "rotate"
Now, we can define Euclidean transform(?) i.e. rotation and translation, as
E=(Q, P)
where P is translation and Q is quaternion. We can make it behave just like 4x4 matrix. We can define E*V to mean rotation&translation of V by E just as if E would be an 4x4 matrix, define ~E as inverse transform, and define E1*E2 as concatenated rotations.
So we have:
V' = E*V = (Q rotate V) + P , where Q is rotation and P is translation.
The inverse transform can be easily found as
V' = (Q rotate V) + P
V' - P = Q rotate V
~Q rotate (V' - P) = V
~Q rotate V' - ~Q rotate P = V
That is, for
E=(Q,P)
inverse is
~E=(~Q, - (~Q rotate P))
Another thing we can be interested in is concatenation of transformations. Say V is transformed by E1 then by E2 .
(E2*E1)*V = P2+Q2 rotate (P1+Q1 rotate V) = P2 + Q2 rotate P1 + Q2*Q1 rotate V
So we have
E2*E1=(Q2*Q1,P2 + Q2 rotate P1)
There is the code i use (somewhat crude), may be more readable than math above:
// ignore the templates - them is there just so i can use it with // floats and doubles alike.// You can just assume T is double and "something<t>" // is "somethingd" to indicate that it's using double.template <class T>struct CoordSys{ Quaternion<T> orientation; Vec3<T> position; CoordSys() {} ; CoordSys(Quaternion<T> o,Vec3<T>pos) { position=pos; orientation=o; }; //helpser void Translate(const Vec3<T> &by) { position+=QuaternionToMatrix(orientation)*by; }; void Rotate(const Quaternion<T> &by) { orientation=#ifndef dont_normalize Normalized#endif (orientation*by); }; inline void Rotate(const Vec3<T> &axis,T angle) { orientation=#ifndef dont_normalize Normalized#endif (orientation*(Quaternion<T>(axis,angle))); }};template<class T>CoordSys<T> operator*(const CoordSys<T> &c1,const CoordSys<T> &c2){ CoordSys<T> result; result.orientation=#ifndef dont_normalize Normalized#endif (c1.orientation*c2.orientation) ; result.position=c1.orientation.Rotate(c2.position)+c1.position;};// transforms vector like 4x4 matrix wouldtemplate<class T>Vec3<T> operator*(const CoordSys<T> &c,const Vec3<T> &v){ return c.orientation.rotate(v) + c.position;};template<class T>CoordSys<T> Inverse(const CoordSys<T> &c1){ CoordSys<T> result;// for rotation quaternions, signs do not matter// (Q and -Q works the same). So i use minus conjugate, // because it negates real part only and is // faster than conjugate result.orientation=MinusConjugate(c1.orientation); result.position= -(result.orientation.rotate(c1.position)); return result;};template<class T>inline Matrix4x4<T> TransformFrom(const CoordSys<T> &c1){ Matrix4x4<T> result(QuaternionToMatrix(c1.orientation)); result.a[0][3]=c1.position.x; result.a[1][3]=c1.position.y; result.a[2][3]=c1.position.z; return result;};template<class T>inline Matrix4x4<T> TransformTo(const CoordSys<T> &c1){ return TransformFrom(Inverse(c1));};
The TransformFrom and TransformTo is used to get matrix for use in openGL.
How it is related:
To find "how CoordSys C2 looks from other CoordSys C1" (i assume that's what you want), i'd just use C2*Inverse(C1)
(if it would be really really speed critical and compiler wouldn't optimize everything out, i would maybe write it manually. But in any other case it's allright to work with CoordSys and not it's implementation details)
It's especially good when there's something complicated you has to do. It could be also good if you have code that used 4x4 matrices, or have experience using them. Also it hides details of implementation of coordinate system in a way that really makes sense (you may for example have CoordSysM that is implemented as 4x4 matrix , but is exactly same in usage)