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