1 /********************************************************************************
2 *                                                                               *
3 *              D o u b l e - P r e c i s i o n  Q u a t e r n i o n             *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1994,2005 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (at your option) any later version.            *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: FXQuatd.cpp,v 1.19.2.1 2005/06/22 06:15:59 fox Exp $                         *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXStream.h"
29 #include "FXObject.h"
30 #include "FXVec2d.h"
31 #include "FXVec3d.h"
32 #include "FXVec4d.h"
33 #include "FXQuatd.h"
34 #include "FXMat3d.h"
35 
36 
37 using namespace FX;
38 
39 /*******************************************************************************/
40 
41 namespace FX {
42 
43 // Construct from angle and axis
FXQuatd(const FXVec3d & axis,FXdouble phi)44 FXQuatd::FXQuatd(const FXVec3d& axis,FXdouble phi){
45   setAxisAngle(axis,phi);
46   }
47 
48 
49 // Construct from roll, pitch, yaw
FXQuatd(FXdouble roll,FXdouble pitch,FXdouble yaw)50 FXQuatd::FXQuatd(FXdouble roll,FXdouble pitch,FXdouble yaw){
51   setRollPitchYaw(roll,pitch,yaw);
52   }
53 
54 
55 // Construct quaternion from axes
FXQuatd(const FXVec3d & ex,const FXVec3d & ey,const FXVec3d & ez)56 FXQuatd::FXQuatd(const FXVec3d& ex,const FXVec3d& ey,const FXVec3d& ez){
57   setAxes(ex,ey,ez);
58   }
59 
60 
61 // Construct quaternion from 3x3 matrix
FXQuatd(const FXMat3d & mat)62 FXQuatd::FXQuatd(const FXMat3d& mat){
63   setAxes(mat[0],mat[1],mat[2]);
64   }
65 
66 
67 // Set axis and angle
setAxisAngle(const FXVec3d & axis,FXdouble phi)68 void FXQuatd::setAxisAngle(const FXVec3d& axis,FXdouble phi){
69   register FXdouble a=0.5*phi;
70   register FXdouble s=sin(a)/len(axis);
71   x=axis.x*s;
72   y=axis.y*s;
73   z=axis.z*s;
74   w=cos(a);
75   }
76 
77 
78 // Obtain axis and angle
79 // Remeber that: q = sin(A/2)*(x*i+y*j+z*k)+cos(A/2)
80 // for unit quaternion |q| == 1
getAxisAngle(FXVec3d & axis,FXdouble & phi) const81 void FXQuatd::getAxisAngle(FXVec3d& axis,FXdouble& phi) const {
82   register FXdouble n=sqrt(x*x+y*y+z*z);
83   if(n>0.0){
84     axis.x=x/n;
85     axis.y=y/n;
86     axis.z=z/n;
87     phi=2.0*acos(w);
88     }
89   else{
90     axis.x=1.0;
91     axis.y=0.0;
92     axis.z=0.0;
93     phi=0.0;
94     }
95   }
96 
97 
98 // Set quaternion from roll (x), pitch(y) yaw (z)
setRollPitchYaw(FXdouble roll,FXdouble pitch,FXdouble yaw)99 void FXQuatd::setRollPitchYaw(FXdouble roll,FXdouble pitch,FXdouble yaw){
100   register FXdouble sr,cr,sp,cp,sy,cy;
101   register FXdouble rr=0.5*roll;
102   register FXdouble pp=0.5*pitch;
103   register FXdouble yy=0.5*yaw;
104   sr=sin(rr); cr=cos(rr);
105   sp=sin(pp); cp=cos(pp);
106   sy=sin(yy); cy=cos(yy);
107   x=sr*cp*cy-cr*sp*sy;
108   y=cr*sp*cy+sr*cp*sy;
109   z=cr*cp*sy-sr*sp*cy;
110   w=cr*cp*cy+sr*sp*sy;
111   }
112 
113 
114 // Obtain yaw, pitch, and roll
115 // Math is from "3D Game Engine Design" by David Eberly pp 19-20.
116 // However, instead of testing asin(Sy) against -PI/2 and PI/2, I
117 // test Sy against -1 and 1; this is numerically more stable, as
118 // asin doesn't like arguments outside [-1,1].
getRollPitchYaw(FXdouble & roll,FXdouble & pitch,FXdouble & yaw) const119 void FXQuatd::getRollPitchYaw(FXdouble& roll,FXdouble& pitch,FXdouble& yaw) const {
120   register FXdouble s=2.0*(w*y-x*z);
121   if(s<1.0){
122     if(-1.0<s){
123       roll=atan2(2.0*(y*z+w*x),1.0-2.0*(x*x+y*y));
124       pitch=asin(s);
125       yaw=atan2(2.0*(x*y+w*z),1.0-2.0*(y*y+z*z));
126       }
127     else{
128       roll=-atan2(2.0*(x*y-w*z),1.0-2.0*(x*x+z*z));
129       pitch=-1.57079632679489661923;
130       yaw=0.0;
131       }
132     }
133   else{
134     roll=atan2(2.0*(x*y-w*z),1.0-2.0*(x*x+z*z));
135     pitch=1.57079632679489661923;
136     yaw=0.0;
137     }
138   }
139 
140 
adjust()141 FXQuatd& FXQuatd::adjust(){
142   register FXdouble t=x*x+y*y+z*z+w*w;
143   register FXdouble f;
144   if(t>0.0){
145     f=1.0/sqrt(t);
146     x*=f;
147     y*=f;
148     z*=f;
149     w*=f;
150     }
151   return *this;
152   }
153 
154 
155 // Exponentiate unit quaternion
156 // Given q = theta*(x*i+y*j+z*k), where length of (x,y,z) is 1,
157 // then exp(q) = sin(theta)*(x*i+y*j+z*k)+cos(theta).
exp(const FXQuatd & q)158 FXQuatd exp(const FXQuatd& q){
159   register FXdouble theta=sqrt(q.x*q.x+q.y*q.y+q.z*q.z);
160   register FXdouble scale;
161   FXQuatd result(q.x,q.y,q.z,cos(theta));
162   if(theta>0.000001){
163     scale=sin(theta)/theta;
164     result.x*=scale;
165     result.y*=scale;
166     result.z*=scale;
167     }
168   return result;
169   }
170 
171 
172 // Take logarithm of unit quaternion
173 // Given q = sin(theta)*(x*i+y*j+z*k)+cos(theta), length of (x,y,z) is 1,
174 // then log(q) = theta*(x*i+y*j+z*k).
log(const FXQuatd & q)175 FXQuatd log(const FXQuatd& q){
176   register FXdouble scale=sqrt(q.x*q.x+q.y*q.y+q.z*q.z);
177   register FXdouble theta=atan2(scale,q.w);
178   FXQuatd result(q.x,q.y,q.z,0.0);
179   if(scale>0.0){
180     scale=theta/scale;
181     result.x*=scale;
182     result.y*=scale;
183     result.z*=scale;
184     }
185   return result;
186   }
187 
188 
189 // Invert quaternion
invert(const FXQuatd & q)190 FXQuatd invert(const FXQuatd& q){
191   register FXdouble n=q.x*q.x+q.y*q.y+q.z*q.z+q.w*q.w;
192   return FXQuatd(-q.x/n,-q.y/n,-q.z/n,q.w/n);
193   }
194 
195 
196 // Invert unit quaternion
unitinvert(const FXQuatd & q)197 FXQuatd unitinvert(const FXQuatd& q){
198   return FXQuatd(-q.x,-q.y,-q.z,q.w);
199   }
200 
201 
202 // Conjugate quaternion
conj(const FXQuatd & q)203 FXQuatd conj(const FXQuatd& q){
204   return FXQuatd(-q.x,-q.y,-q.z,q.w);
205   }
206 
207 
208 // Multiply quaternions
operator *(const FXQuatd & p,const FXQuatd & q)209 FXQuatd operator*(const FXQuatd& p,const FXQuatd& q){
210   return FXQuatd(p.w*q.x+p.x*q.w+p.y*q.z-p.z*q.y,
211                  p.w*q.y+p.y*q.w+p.z*q.x-p.x*q.z,
212                  p.w*q.z+p.z*q.w+p.x*q.y-p.y*q.x,
213                  p.w*q.w-p.x*q.x-p.y*q.y-p.z*q.z);
214   }
215 
216 
217 // Rotation of a vector by a quaternion; this is defined as q.v.q*
218 // where q* is the conjugate of q.
operator *(const FXQuatd & quat,const FXVec3d & vec)219 FXVec3d operator*(const FXQuatd& quat,const FXVec3d& vec){
220   return vec*toMatrix(quat);
221   }
222 
223 
224 // Construct quaternion from arc a->b on unit sphere.
arc(const FXVec3d & f,const FXVec3d & t)225 FXQuatd arc(const FXVec3d& f,const FXVec3d& t){
226   register FXdouble dot,div;
227   dot=f.x*t.x+f.y*t.y+f.z*t.z;
228   FXASSERT(-1.0<=dot && dot<=1.0);
229   div=sqrt((dot+1.0)*2.0);
230   FXASSERT(0.0<div);
231   return FXQuatd((f.y*t.z-f.z*t.y)/div,(f.z*t.x-f.x*t.z)/div,(f.x*t.y-f.y*t.x)/div,div*0.5);
232   }
233 
234 
235 // Spherical lerp
lerp(const FXQuatd & u,const FXQuatd & v,FXdouble f)236 FXQuatd lerp(const FXQuatd& u,const FXQuatd& v,FXdouble f){
237   register FXdouble alpha,beta,theta,sin_t,cos_t;
238   register FXint flip=0;
239   cos_t = u.x*v.x+u.y*v.y+u.z*v.z+u.w*v.w;
240   if(cos_t<0.0){ cos_t = -cos_t; flip=1; }
241   if((1.0-cos_t)<0.000001){
242     beta = 1.0-f;
243     alpha = f;
244     }
245   else{
246     theta = acos(cos_t);
247     sin_t = sin(theta);
248     beta = sin(theta-f*theta)/sin_t;
249     alpha = sin(f*theta)/sin_t;
250     }
251   if(flip) alpha = -alpha;
252   return FXQuatd(beta*u.x+alpha*v.x, beta*u.y+alpha*v.y, beta*u.z+alpha*v.z, beta*u.w+alpha*v.w);
253   }
254 
255 
256 // Set quaternion from axes
setAxes(const FXVec3d & ex,const FXVec3d & ey,const FXVec3d & ez)257 void FXQuatd::setAxes(const FXVec3d& ex,const FXVec3d& ey,const FXVec3d& ez){
258   register FXdouble trace=ex.x+ey.y+ez.z;
259   register FXdouble scale;
260   if(trace>0.0){
261     scale=sqrt(1.0+trace);
262     w=0.5*scale;
263     scale=0.5/scale;
264     x=(ey.z-ez.y)*scale;
265     y=(ez.x-ex.z)*scale;
266     z=(ex.y-ey.x)*scale;
267     }
268   else if(ex.x>ey.y && ex.x>ez.z){
269     scale=2.0*sqrt(1.0+ex.x-ey.y-ez.z);
270     x=0.25*scale;
271     y=(ex.y+ey.x)/scale;
272     z=(ex.z+ez.x)/scale;
273     w=(ey.z-ez.y)/scale;
274     }
275   else if(ey.y>ez.z){
276     scale=2.0*sqrt(1.0+ey.y-ex.x-ez.z);
277     y=0.25*scale;
278     x=(ex.y+ey.x)/scale;
279     z=(ey.z+ez.y)/scale;
280     w=(ez.x-ex.z)/scale;
281     }
282   else{
283     scale=2.0*sqrt(1.0+ez.z-ex.x-ey.y);
284     z=0.25*scale;
285     x=(ex.z+ez.x)/scale;
286     y=(ey.z+ez.y)/scale;
287     w=(ex.y-ey.x)/scale;
288     }
289   }
290 
291 
292 // Get quaternion axes
getAxes(FXVec3d & ex,FXVec3d & ey,FXVec3d & ez) const293 void FXQuatd::getAxes(FXVec3d& ex,FXVec3d& ey,FXVec3d& ez) const {
294   register FXdouble tx=2.0*x;
295   register FXdouble ty=2.0*y;
296   register FXdouble tz=2.0*z;
297   register FXdouble twx=tx*w;
298   register FXdouble twy=ty*w;
299   register FXdouble twz=tz*w;
300   register FXdouble txx=tx*x;
301   register FXdouble txy=ty*x;
302   register FXdouble txz=tz*x;
303   register FXdouble tyy=ty*y;
304   register FXdouble tyz=tz*y;
305   register FXdouble tzz=tz*z;
306   ex.x=1.0-tyy-tzz;
307   ex.y=txy+twz;
308   ex.z=txz-twy;
309   ey.x=txy-twz;
310   ey.y=1.0-txx-tzz;
311   ey.z=tyz+twx;
312   ez.x=txz+twy;
313   ez.y=tyz-twx;
314   ez.z=1.0-txx-tyy;
315   }
316 
317 
318 // Obtain local x axis
getXAxis() const319 FXVec3d FXQuatd::getXAxis() const {
320   register FXdouble ty=2.0*y;
321   register FXdouble tz=2.0*z;
322   return FXVec3d(1.0-ty*y-tz*z,ty*x+tz*w,tz*x-ty*w);
323   }
324 
325 
326 // Obtain local y axis
getYAxis() const327 FXVec3d FXQuatd::getYAxis() const {
328   register FXdouble tx=2.0*x;
329   register FXdouble tz=2.0*z;
330   return FXVec3d(tx*y-tz*w,1.0-tx*x-tz*z,tz*y+tx*w);
331   }
332 
333 
334 // Obtain local z axis
getZAxis() const335 FXVec3d FXQuatd::getZAxis() const {
336   register FXdouble tx=2.0*x;
337   register FXdouble ty=2.0*y;
338   return FXVec3d(tx*z+ty*w,ty*z-tx*w,1.0-tx*x-ty*y);
339   }
340 
341 
342 // Convert quaternion to 3x3 matrix
toMatrix(const FXQuatd & quat)343 FXMat3d toMatrix(const FXQuatd& quat){
344   return FXMat3d(quat);
345   }
346 
347 
348 // Convert 3x3 matrix to quaternion
fromMatrix(const FXMat3d & mat)349 FXQuatd fromMatrix(const FXMat3d& mat){
350   return FXQuatd(mat[0],mat[1],mat[2]);
351   }
352 
353 
354 }
355 
356