OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimQuaternion.cpp
Go to the documentation of this file.
6 
8 {
9  matrix.getRotate(*this);
10 }
11 
13 {
14  matrix.makeRotate(*this);
15 }
16 
20 {
21 
22  const value_type epsilon = 0.0000001;
23 
24  value_type length = sqrt( x*x + y*y + z*z );
25  if (length < epsilon)
26  {
27  // ~zero length axis, so reset rotation to zero.
28  *this = ossim::Quaternion();
29  return;
30  }
31  double tempAngle = ossim::degreesToRadians(angle);
32 
33  value_type inversenorm = 1.0/length;
34  value_type coshalfangle = cos( 0.5*tempAngle );
35  value_type sinhalfangle = sin( 0.5*tempAngle );
36 
37  theVector[0] = x * sinhalfangle * inversenorm;
38  theVector[1] = y * sinhalfangle * inversenorm;
39  theVector[2] = z * sinhalfangle * inversenorm;
40  theVector[3] = coshalfangle;
41 }
42 
44  value_type angle2, const ossimColumnVector3d& axis2,
45  value_type angle3, const ossimColumnVector3d& axis3)
46 {
47  ossim::Quaternion q1; q1.makeRotate(angle1,axis1);
48  ossim::Quaternion q2; q2.makeRotate(angle2,axis2);
49  ossim::Quaternion q3; q3.makeRotate(angle3,axis3);
50  *this = q1*q2*q3;
51 }
52 
54  value_type angle2, const ossimDpt3d& axis2,
55  value_type angle3, const ossimDpt3d& axis3)
56 {
57  ossim::Quaternion q1; q1.makeRotate(angle1,axis1);
58  ossim::Quaternion q2; q2.makeRotate(angle2,axis2);
59  ossim::Quaternion q3; q3.makeRotate(angle3,axis3);
60 
61  *this = q1*q2*q3;
62 }
63 
65 {
66  makeRotate(ossimDpt3d(vec1[0], vec1[1], vec1[2]),
67  ossimDpt3d(vec2[0], vec2[1], vec2[2]));
68 }
69 
70 
71 // /** Make a rotation Quat which will rotate vec1 to vec2
72 
73 // This routine uses only fast geometric transforms, without costly acos/sin computations.
74 // It's exact, fast, and with less degenerate cases than the acos/sin method.
75 
76 // For an explanation of the math used, you may see for example:
77 // http://logiciels.cnes.fr/MARMOTTES/marmottes-mathematique.pdf
78 
79 // @note This is the rotation with shortest angle, which is the one equivalent to the
80 // acos/sin transform method. Other rotations exists, for example to additionally keep
81 // a local horizontal attitude.
82 
83 // @author Nicolas Brodu
84 // */
85 void ossim::Quaternion::makeRotate( const ossimDpt3d& from, const ossimDpt3d& to )
86 {
87 
88  // This routine takes any vector as argument but normalized
89  // vectors are necessary, if only for computing the dot product.
90  // Too bad the API is that generic, it leads to performance loss.
91  // Even in the case the 2 vectors are not normalized but same length,
92  // the sqrt could be shared, but we have no way to know beforehand
93  // at this point, while the caller may know.
94  // So, we have to test... in the hope of saving at least a sqrt
95  ossimDpt3d sourceVector = from;
96  ossimDpt3d targetVector = to;
97 
98  value_type fromLen2 = from.length2();
99  value_type fromLen = 1.0;
100  // normalize only when necessary, epsilon test
101  if ((fromLen2 < 1.0-1e-7) || (fromLen2 > 1.0+1e-7))
102  {
103  fromLen = sqrt(fromLen2);
104  sourceVector /= fromLen;
105  }
106 
107  value_type toLen2 = to.length2();
108  // normalize only when necessary, epsilon test
109  if ((toLen2 < 1.0-1e-7) || (toLen2 > 1.0+1e-7))
110  {
111  value_type toLen;
112  // re-use fromLen for case of mapping 2 vectors of the same length
113  if ((toLen2 > fromLen2-1e-7) && (toLen2 < fromLen2+1e-7))
114  {
115  toLen = fromLen;
116  }
117  else toLen = sqrt(toLen2);
118  targetVector /= toLen;
119  }
120 
121  // Now let's get into the real stuff
122  // Use "dot product plus one" as test as it can be re-used later on
123  double dotProdPlus1 = 1.0 + sourceVector * targetVector;
124 
125  // Check for degenerate case of full u-turn. Use epsilon for detection
126  if (dotProdPlus1 < 1e-7) {
127 
128  // Get an orthogonal vector of the given vector
129  // in a plane with maximum vector coordinates.
130  // Then use it as quaternion axis with pi angle
131  // Trick is to realize one value at least is >0.6 for a normalized vector.
132  if (fabs(sourceVector.x) < 0.6) {
133  const double norm = sqrt(1.0 - sourceVector.x * sourceVector.x);
134  theVector[0] = 0.0;
135  theVector[1] = sourceVector.z / norm;
136  theVector[2] = -sourceVector.y / norm;
137  theVector[3] = 0.0;
138  } else if (fabs(sourceVector.y) < 0.6) {
139  const double norm = sqrt(1.0 - sourceVector.y * sourceVector.y);
140  theVector[0] = -sourceVector.z / norm;
141  theVector[1] = 0.0;
142  theVector[2] = sourceVector.x / norm;
143  theVector[3] = 0.0;
144  } else {
145  const double norm = sqrt(1.0 - sourceVector.z * sourceVector.z);
146  theVector[0] = sourceVector.y / norm;
147  theVector[1] = -sourceVector.x / norm;
148  theVector[2] = 0.0;
149  theVector[3] = 0.0;
150  }
151  }
152 
153  else {
154  // Find the shortest angle quaternion that transforms normalized vectors
155  // into one other. Formula is still valid when vectors are colinear
156  const double s = sqrt(0.5 * dotProdPlus1);
157  const ossimDpt3d tmp = sourceVector ^ targetVector / (2.0*s);
158  theVector[0] = tmp.x;
159  theVector[1] = tmp.y;
160  theVector[2] = tmp.z;
161  theVector[3] = s;
162  }
163 }
164 
165 
166 // Make a rotation Quat which will rotate vec1 to vec2
167 // Generally take adot product to get the angle between these
168 // and then use a cross product to get the rotation axis
169 // Watch out for the two special cases of when the vectors
170 // are co-incident or opposite in direction.
171 // void ossim::Quaternion::makeRotate_original( const Vec3d& from, const Vec3d& to )
172 // {
173 // const value_type epsilon = 0.0000001;
174 
175 // value_type length1 = from.length();
176 // value_type length2 = to.length();
177 
178 // // dot product vec1*vec2
179 // value_type cosangle = from*to/(length1*length2);
180 
181 // if ( fabs(cosangle - 1) < epsilon )
182 // {
183 // osg::notify(osg::INFO)<<"*** ossim::Quaternion::makeRotate(from,to) with near co-linear vectors, epsilon= "<<fabs(cosangle-1)<<std::endl;
184 
185 // // cosangle is close to 1, so the vectors are close to being coincident
186 // // Need to generate an angle of zero with any vector we like
187 // // We'll choose (1,0,0)
188 // makeRotate( 0.0, 0.0, 0.0, 1.0 );
189 // }
190 // else
191 // if ( fabs(cosangle + 1.0) < epsilon )
192 // {
193 // // vectors are close to being opposite, so will need to find a
194 // // vector orthongonal to from to rotate about.
195 // Vec3d tmp;
196 // if (fabs(from.x())<fabs(from.y()))
197 // if (fabs(from.x())<fabs(from.z())) tmp.set(1.0,0.0,0.0); // use x axis.
198 // else tmp.set(0.0,0.0,1.0);
199 // else if (fabs(from.y())<fabs(from.z())) tmp.set(0.0,1.0,0.0);
200 // else tmp.set(0.0,0.0,1.0);
201 
202 // Vec3d fromd(from.x(),from.y(),from.z());
203 
204 // // find orthogonal axis.
205 // Vec3d axis(fromd^tmp);
206 // axis.normalize();
207 
208 // theVector[0] = axis[0]; // sin of half angle of PI is 1.0.
209 // theVector[1] = axis[1]; // sin of half angle of PI is 1.0.
210 // theVector[2] = axis[2]; // sin of half angle of PI is 1.0.
211 // theVector[3] = 0; // cos of half angle of PI is zero.
212 
213 // }
214 // else
215 // {
216 // // This is the usual situation - take a cross-product of vec1 and vec2
217 // // and that is the axis around which to rotate.
218 // Vec3d axis(from^to);
219 // value_type angle = acos( cosangle );
220 // makeRotate( angle, axis );
221 // }
222 // }
223 
224 // void ossim::Quaternion::getRotate( value_type& angle, Vec3f& vec ) const
225 // {
226 // value_type x,y,z;
227 // getRotate(angle,x,y,z);
228 // vec[0]=x;
229 // vec[1]=y;
230 // vec[2]=z;
231 // }
232 // void ossim::Quaternion::getRotate( value_type& angle, Vec3d& vec ) const
233 // {
234 // value_type x,y,z;
235 // getRotate(angle,x,y,z);
236 // vec[0]=x;
237 // vec[1]=y;
238 // vec[2]=z;
239 // }
240 
241 
242 // Get the angle of rotation and axis of this Quat object.
243 // Won't give very meaningful results if the Quat is not associated
244 // with a rotation!
246 {
247  value_type sinhalfangle = sqrt( theVector[0]*theVector[0] + theVector[1]*theVector[1] + theVector[2]*theVector[2] );
248 
249  angle = 2.0 * atan2( sinhalfangle, theVector[3] );
250  if(sinhalfangle)
251  {
252  x = theVector[0] / sinhalfangle;
253  y = theVector[1] / sinhalfangle;
254  z = theVector[2] / sinhalfangle;
255  }
256  else
257  {
258  x = 0.0;
259  y = 0.0;
260  z = 1.0;
261  }
262  angle = ossim::radiansToDegrees(angle);
263 }
264 
265 #if 0
266 void ossim::Quaternion::slerp( value_type t, const ossim::Quaternion& from, const ossim::Quaternion& to )
272 {
273  const double epsilon = 0.00001;
274  double omega, cosomega, sinomega, scale_from, scale_to ;
275 
276  osg::Quat quatTo(to);
277  // this is a dot product
278 
279  cosomega = from.asVec4() * to.asVec4();
280 
281  if ( cosomega <0.0 )
282  {
283  cosomega = -cosomega;
284  quatTo = -to;
285  }
286 
287  if( (1.0 - cosomega) > epsilon )
288  {
289  omega= acos(cosomega) ; // 0 <= omega <= Pi (see man acos)
290  sinomega = sin(omega) ; // this sinomega should always be +ve so
291  // could try sinomega=sqrt(1-cosomega*cosomega) to avoid a sin()?
292  scale_from = sin((1.0-t)*omega)/sinomega ;
293  scale_to = sin(t*omega)/sinomega ;
294  }
295  else
296  {
297  /* --------------------------------------------------
298  The ends of the vectors are very close
299  we can use simple linear interpolation - no need
300  to worry about the "spherical" interpolation
301  -------------------------------------------------- */
302  scale_from = 1.0 - t ;
303  scale_to = t ;
304  }
305 
306  *this = (from*scale_from) + (quatTo*scale_to);
307 
308  // so that we get a Vec4
309 }
310 #endif
ossim_uint32 x
void slerp(value_type t, const Quaternion &from, const Quaternion &to)
Spherical Linear Interpolation.
double length2() const
Definition: ossimDpt3d.h:72
ossim::Quaternion getRotate() const
ossim_uint32 y
A quaternion class.
double radiansToDegrees(double x)
Definition: ossimCommon.h:257
void makeRotate(const ossim::Quaternion &quat)
double z
Definition: ossimDpt3d.h:145
double degreesToRadians(double x)
Definition: ossimCommon.h:258
void set(value_type x, value_type y, value_type z, value_type w)
ossim_float64 value_type
void getRotate(value_type &angle, value_type &x, value_type &y, value_type &z) const
Return the angle and vector components represented by the quaternion.
void makeRotate(value_type angle, value_type x, value_type y, value_type z)
Set the elements of the Quat to represent a rotation of angle (radians) around the axis (x...
double x
Definition: ossimDpt3d.h:143
double y
Definition: ossimDpt3d.h:144
void get(ossimMatrix4x4 &matrix) const