1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 /*!
34   \class SbDPRotation SbDPRotation.h Inventor/SbDPRotation.h
35   \brief The SbDPRotation class represents a rotation in 3D space
36   using double precision data.
37 
38   \ingroup base
39 
40   SbDPRotation is used extensively throughout the Coin library.
41 
42   An SbDPRotation is stored internally as a quaternion for speed and
43   storage reasons, but inquiries can be done to get and set axis and
44   angle values for convenience.
45 
46   \sa SbDPMatrix
47 */
48 
49 #include <Inventor/SbDPRotation.h>
50 
51 #include <Inventor/SbVec3d.h>
52 #include <Inventor/SbDPMatrix.h>
53 #include <cassert>
54 #include <cfloat>
55 
56 #if COIN_DEBUG
57 #include <Inventor/errors/SoDebugError.h>
58 #endif // COIN_DEBUG
59 
60 /*!
61   The default constructor just initializes a valid rotation. The
62   actual value is unspecified, and you should not depend on it.
63 */
SbDPRotation(void)64 SbDPRotation::SbDPRotation(void)
65   // This translates to zero rotation around the positive Z axis.
66   : quat(0.0f, 0.0f, 0.0f, 1.0f)
67 {
68 }
69 
70 /*!
71   Construct a new SbDPRotation object initialized with the given
72   axis-of-rotation and rotation angle.
73  */
SbDPRotation(const SbVec3d & axis,const double radians)74 SbDPRotation::SbDPRotation(const SbVec3d & axis, const double radians)
75 {
76 #if COIN_DEBUG
77   if (axis.length()==0.0f)
78     SoDebugError::postWarning("SbDPRotation::SbDPRotation",
79                               "axis parameter has zero length => "
80                               "undefined axis.");
81 #endif // COIN_DEBUG
82   this->setValue(axis, radians);
83 }
84 
85 /*!
86   Construct a new SbDPRotation object initialized with the given quaternion
87   components.
88 
89   The array must be ordered as follows:
90 
91   q[0] = x, q[1] = y, q[2] = z and q[3] = w, where the quaternion is
92   specified by q=w+xi+yj+zk.
93  */
SbDPRotation(const double q[4])94 SbDPRotation::SbDPRotation(const double q[4])
95 {
96   this->setValue(q);
97 }
98 
99 /*!
100   Construct a new SbDPRotation object initialized with the given quaternion
101   components.
102  */
SbDPRotation(const double q0,const double q1,const double q2,const double q3)103 SbDPRotation::SbDPRotation(const double q0, const double q1,
104                        const double q2, const double q3)
105 {
106   this->setValue(q0, q1, q2, q3);
107 }
108 
109 /*!
110   Construct a new SbDPRotation object initialized with the given rotation
111   matrix.
112  */
SbDPRotation(const SbDPMatrix & m)113 SbDPRotation::SbDPRotation(const SbDPMatrix & m)
114 {
115   this->setValue(m);
116 }
117 
118 /*!
119   Construct a rotation which is the minimum rotation necessary to make vector
120   \a rotateFrom point in the direction of vector \a rotateTo.
121  */
SbDPRotation(const SbVec3d & rotateFrom,const SbVec3d & rotateTo)122 SbDPRotation::SbDPRotation(const SbVec3d & rotateFrom, const SbVec3d & rotateTo)
123 {
124   // Parameters are checked in setValue().
125 
126   this->setValue(rotateFrom, rotateTo);
127 }
128 
129 /*!
130   Return pointer to an array with the rotation expressed as four
131   quaternion values.
132 
133   \sa setValue().
134 */
135 const double *
getValue(void) const136 SbDPRotation::getValue(void) const
137 {
138   return &this->quat[0];
139 }
140 
141 /*!
142   Return the four quaternion components representing the rotation.
143 
144   \sa setValue().
145  */
146 void
getValue(double & q0,double & q1,double & q2,double & q3) const147 SbDPRotation::getValue(double & q0, double & q1, double & q2, double & q3) const
148 {
149   q0 = this->quat[0];
150   q1 = this->quat[1];
151   q2 = this->quat[2];
152   q3 = this->quat[3];
153 }
154 
155 /*!
156   Set the rotation.
157 
158   \sa getValue().
159 */
160 SbDPRotation &
setValue(const double q0,const double q1,const double q2,const double q3)161 SbDPRotation::setValue(const double q0, const double q1,
162                      const double q2, const double q3)
163 {
164   this->quat[0] = q0;
165   this->quat[1] = q1;
166   this->quat[2] = q2;
167   this->quat[3] = q3;
168   if (this->quat.normalize() == 0.0f) {
169 #if COIN_DEBUG
170     SoDebugError::postWarning("SbRotation::setValue",
171                               "Quarternion has zero length => "
172                               "undefined rotation.");
173 #endif // COIN_DEBUG
174   }
175   return *this;
176 }
177 
178 /*!
179   Return the rotation in the form of an axis-of-rotation and a rotation
180   angle.
181 
182   \sa setValue().
183  */
184 void
getValue(SbVec3d & axis,double & radians) const185 SbDPRotation::getValue(SbVec3d & axis, double & radians) const
186 {
187   if((this->quat[3] >= -1.0f) && (this->quat[3] <= 1.0f)) {
188     radians = double(acos(this->quat[3])) * 2.0f;
189     double scale = static_cast<double>(sin(radians / 2.0f));
190 
191     if(scale != 0.0f) {
192       axis[0] = this->quat[0] / scale;
193       axis[1] = this->quat[1] / scale;
194       axis[2] = this->quat[2] / scale;
195       // FIXME: why not just flip the sign on each component according
196       // to "scale" and normalize the axis instead? 20010111 mortene.
197       return;
198     }
199   }
200 
201   // Quaternion can't be converted to axis and rotation angle, so we just
202   // set up values to indicate this.
203   axis.setValue(0.0f, 0.0f, 1.0f);
204   radians = 0.0f;
205 }
206 
207 /*!
208   Return this rotation in the form of a matrix.
209 
210   \sa setValue().
211  */
212 void
getValue(SbDPMatrix & matrix) const213 SbDPRotation::getValue(SbDPMatrix & matrix) const
214 {
215   const double x = this->quat[0];
216   const double y = this->quat[1];
217   const double z = this->quat[2];
218   const double w = this->quat[3];
219 
220   matrix[0][0] = w*w + x*x - y*y - z*z;
221   matrix[0][1] = 2*x*y + 2*w*z;
222   matrix[0][2] = 2*x*z - 2*w*y;
223   matrix[0][3] = 0.0f;
224 
225   matrix[1][0] = 2*x*y-2*w*z;
226   matrix[1][1] = w*w - x*x + y*y - z*z;
227   matrix[1][2] = 2*y*z + 2*w*x;
228   matrix[1][3] = 0.0f;
229 
230   matrix[2][0] = 2*x*z + 2*w*y;
231   matrix[2][1] = 2*y*z - 2*w*x;
232   matrix[2][2] = w*w - x*x - y*y + z*z;
233   matrix[2][3] = 0.0f;
234 
235   matrix[3][0] = 0.0f;
236   matrix[3][1] = 0.0f;
237   matrix[3][2] = 0.0f;
238   matrix[3][3] = w*w + x*x + y*y + z*z;
239 }
240 
241 /*!
242   Invert the rotation. Returns reference to self.
243 
244   \sa inverse()
245  */
246 SbDPRotation &
invert(void)247 SbDPRotation::invert(void)
248 {
249   double length = this->quat.length();
250 #if COIN_DEBUG
251   if (length==0.0f)
252     SoDebugError::postWarning("SbDPRotation::invert",
253                               "Quarternion has zero length => "
254                               "undefined rotation.");
255 #endif // COIN_DEBUG
256 
257   // Optimize by doing 1 div and 4 muls instead of 4 divs.
258   double inv = 1.0f / length;
259 
260   this->quat[0] = -this->quat[0] * inv;
261   this->quat[1] = -this->quat[1] * inv;
262   this->quat[2] = -this->quat[2] * inv;
263   this->quat[3] = this->quat[3] * inv;
264   return *this;
265 }
266 
267 /*!
268   Non-destructively inverses the rotation and returns the result.
269 
270   \sa invert()
271  */
272 SbDPRotation
inverse(void) const273 SbDPRotation::inverse(void) const
274 {
275   double length = this->quat.length();
276 #if COIN_DEBUG
277   if (length==0.0f)
278     SoDebugError::postWarning("SbDPRotation::inverse",
279                               "Quaternion has zero length => "
280                               "undefined rotation.");
281 #endif // COIN_DEBUG
282 
283   // Optimize by doing 1 div and 4 muls instead of 4 divs.
284   double inv = 1.0f / length;
285 
286   SbDPRotation rot;
287   rot.quat[0] = -this->quat[0] * inv;
288   rot.quat[1] = -this->quat[1] * inv;
289   rot.quat[2] = -this->quat[2] * inv;
290   rot.quat[3] = this->quat[3] * inv;
291 
292   return rot;
293 }
294 
295 /*!
296   Reset the rotation by the four quaternions in the array.
297 
298   \sa getValue().
299  */
300 SbDPRotation&
setValue(const double q[4])301 SbDPRotation::setValue(const double q[4])
302 {
303   this->quat[0] = q[0];
304   this->quat[1] = q[1];
305   this->quat[2] = q[2];
306   this->quat[3] = q[3];
307   if (this->quat.normalize() == 0.0f) {
308 #if COIN_DEBUG
309     SoDebugError::postWarning("SbRotation::setValue",
310                               "Quarternion has zero length => "
311                               "undefined rotation.");
312 #endif // COIN_DEBUG
313   }
314   return *this;
315 }
316 
317 /*!
318   Set the rotation from the components of the given matrix. Returns
319   reference to self.
320 
321   \sa getValue().
322  */
323 SbDPRotation &
setValue(const SbDPMatrix & m)324 SbDPRotation::setValue(const SbDPMatrix & m)
325 {
326   double scalerow = m[0][0] + m[1][1] + m[2][2];
327 
328   if (scalerow > 0.0f) {
329     double s = static_cast<double>(sqrt(scalerow + m[3][3]));
330     this->quat[3] = s * 0.5f;
331     s = 0.5f / s;
332 
333     this->quat[0] = (m[1][2] - m[2][1]) * s;
334     this->quat[1] = (m[2][0] - m[0][2]) * s;
335     this->quat[2] = (m[0][1] - m[1][0]) * s;
336   }
337   else {
338     int i = 0;
339     if (m[1][1] > m[0][0]) i = 1;
340     if (m[2][2] > m[i][i]) i = 2;
341 
342     int j = (i+1)%3;
343     int k = (j+1)%3;
344 
345     double s = static_cast<double>(sqrt((m[i][i] - (m[j][j] + m[k][k])) + m[3][3]));
346 
347     this->quat[i] = s * 0.5f;
348     s = 0.5f / s;
349 
350     this->quat[3] = (m[j][k] - m[k][j]) * s;
351     this->quat[j] = (m[i][j] + m[j][i]) * s;
352     this->quat[k] = (m[i][k] + m[k][i]) * s;
353   }
354 
355   if (m[3][3] != 1.0f) this->operator*=(1.0f/static_cast<double>(sqrt(m[3][3])));
356   return *this;
357 }
358 
359 /*!
360   Reset rotation with the given axis-of-rotation and rotation angle.
361   Returns reference to self.
362 
363   Make sure \a axis is not the null vector when calling this method.
364 
365   \sa getValue().
366  */
367 SbDPRotation &
setValue(const SbVec3d & axis,const double radians)368 SbDPRotation::setValue(const SbVec3d & axis, const double radians)
369 {
370 #if COIN_DEBUG
371   if (axis.length()==0.0f)
372     SoDebugError::postWarning("SbDPRotation::setValue",
373                               "axis parameter has zero length.");
374 #endif // COIN_DEBUG
375 
376   // From <http://www.automation.hut.fi/~jaro/thesis/hyper/node9.html>.
377 
378   this->quat[3] = static_cast<double>(cos(radians/2));
379 
380   const double sineval = static_cast<double>(sin(radians/2));
381   SbVec3d a = axis;
382   // we test for a null vector above
383   (void) a.normalize();
384   this->quat[0] = a[0] * sineval;
385   this->quat[1] = a[1] * sineval;
386   this->quat[2] = a[2] * sineval;
387   return *this;
388 }
389 
390 /*!
391   Construct a rotation which is the minimum rotation necessary to make vector
392   \a rotateFrom point in the direction of vector \a rotateTo.
393 
394   Returns reference to self.
395 
396   \sa getValue().
397  */
398 SbDPRotation &
setValue(const SbVec3d & rotateFrom,const SbVec3d & rotateTo)399 SbDPRotation::setValue(const SbVec3d & rotateFrom, const SbVec3d & rotateTo)
400 {
401 #if COIN_DEBUG
402   // Check if the vectors are valid.
403   if (rotateFrom.length()==0.0f) {
404     SoDebugError::postWarning("SbDPRotation::setValue",
405                               "rotateFrom argument has zero length.");
406   }
407   if (rotateTo.length()==0.0f) {
408     SoDebugError::postWarning("SbDPRotation::setValue",
409                               "rotateTo argument has zero length.");
410   }
411 #endif // COIN_DEBUG
412 
413   SbVec3d from(rotateFrom);
414   // we test for a null vector above
415   (void) from.normalize();
416   SbVec3d to(rotateTo);
417   // we test for a null vector above
418   (void) to.normalize();
419 
420   const double dot = from.dot(to);
421   SbVec3d crossvec = from.cross(to);
422   const double crosslen = crossvec.normalize();
423 
424   if (crosslen == 0.0f) { // Parallel vectors
425     // Check if they are pointing in the same direction.
426     if (dot > 0.0) {
427       this->setValue(0.0, 0.0, 0.0, 1.0);
428     }
429     // Ok, so they are parallel and pointing in the opposite direction
430     // of each other.
431     else {
432       // Try crossing with x axis.
433       SbVec3d t = from.cross(SbVec3d(1.0f, 0.0f, 0.0f));
434       // If not ok, cross with y axis.
435       if (t.normalize() == 0.0) {
436         t = from.cross(SbVec3d(0.0f, 1.0f, 0.0f));
437         (void) t.normalize();
438       }
439       this->setValue(t[0], t[1], t[2], 0.0f);
440     }
441   }
442   else { // Vectors are not parallel
443     // The fabs() wrapping is to avoid problems when `dot' "overflows"
444     // a tiny wee bit, which can lead to sqrt() returning NaN.
445     crossvec *= static_cast<double>(sqrt(0.5f * fabs(1.0f - dot)));
446     // The fabs() wrapping is to avoid problems when `dot' "underflows"
447     // a tiny wee bit, which can lead to sqrt() returning NaN.
448     this->setValue(crossvec[0], crossvec[1], crossvec[2],
449                    static_cast<double>(sqrt(0.5 * fabs(1.0 + dot))));
450   }
451 
452   return *this;
453 }
454 
455 /*!
456   Multiplies the quaternions.
457 
458   Note that order is important when combining quaternions with the
459   multiplication operator.
460  */
461 SbDPRotation &
operator *=(const SbDPRotation & q)462 SbDPRotation::operator*=(const SbDPRotation & q)
463 {
464   // Formula from <http://www.lboro.ac.uk/departments/ma/gallery/quat/>
465 
466   double tx, ty, tz, tw;
467   this->getValue(tx, ty, tz, tw);
468   double qx, qy, qz, qw;
469   q.getValue(qx, qy, qz, qw);
470 
471   this->setValue(qw*tx + qx*tw + qy*tz - qz*ty,
472                  qw*ty - qx*tz + qy*tw + qz*tx,
473                  qw*tz + qx*ty - qy*tx + qz*tw,
474                  qw*tw - qx*tx - qy*ty - qz*tz);
475   return *this;
476 }
477 
478 /*!
479   Multiplies components of quaternion with scalar value \a s.
480   Returns reference to self.
481  */
482 SbDPRotation &
operator *=(const double s)483 SbDPRotation::operator*=(const double s)
484 {
485   this->quat *= s;
486   return *this;
487 }
488 
489 /*!
490   \relates SbDPRotation
491 
492   Check if the two rotations are equal.
493 
494   \sa equals().
495  */
496 int
operator ==(const SbDPRotation & q1,const SbDPRotation & q2)497 operator==(const SbDPRotation & q1, const SbDPRotation & q2)
498 {
499   return (q1.quat == q2.quat);
500 }
501 
502 /*!
503   \relates SbDPRotation
504 
505   Check if the two rotations are unequal.
506 
507   \sa equals().
508  */
509 int
operator !=(const SbDPRotation & q1,const SbDPRotation & q2)510 operator!=(const SbDPRotation & q1, const SbDPRotation & q2)
511 {
512   return !(q1 == q2);
513 }
514 
515 /*!
516   Check the internal quaternion representation vectors for equality
517   within the given tolerance.
518  */
519 SbBool
equals(const SbDPRotation & r,double tolerance) const520 SbDPRotation::equals(const SbDPRotation & r, double tolerance) const
521 {
522   return this->quat.equals(r.quat, tolerance);
523 }
524 
525 /*!
526   \relates SbDPRotation
527 
528   Multiplies the two rotations and returns the result.
529 
530   Note that order is important when combining quaternions with the
531   multiplication operator.
532 */
533 SbDPRotation
operator *(const SbDPRotation & q1,const SbDPRotation & q2)534 operator*(const SbDPRotation & q1, const SbDPRotation & q2)
535 {
536   SbDPRotation q(q1);
537   q *= q2;
538   return q;
539 }
540 
541 /*!
542   Rotate the \a src vector and put the result in \a dst.
543  */
544 void
multVec(const SbVec3d & src,SbVec3d & dst) const545 SbDPRotation::multVec(const SbVec3d & src, SbVec3d & dst) const
546 {
547   // FIXME: this looks amazingly ineffective. Should
548   // optimize. 20010907 mortene.
549 
550   SbDPMatrix mat;
551   mat.setRotate(*this);
552   mat.multVecMatrix(src, dst);
553 }
554 
555 /*!
556   Scale the angle of rotation by \a scaleFactor.
557  */
558 void
scaleAngle(const double scaleFactor)559 SbDPRotation::scaleAngle(const double scaleFactor)
560 {
561   SbVec3d axis;
562   double rad;
563 
564   this->getValue(axis, rad);
565   this->setValue(axis, rad * scaleFactor);
566 }
567 
568 /*!
569   \relates SbDPRotation
570 
571   Interpolates along the shortest path between the two rotation
572   positions (from \a rot0 to \a rot1).
573 
574   Returns the SbDPRotation which will rotate \a rot0 the given part \a t
575   of the spherical distance towards \a rot1, where \a t=0 will yield \a rot0
576   and \a t=1 will yield \a rot1.
577 
578   \a t should be in the interval [0, 1].
579  */
580 SbDPRotation
slerp(const SbDPRotation & rot0,const SbDPRotation & rot1,double t)581 SbDPRotation::slerp(const SbDPRotation & rot0, const SbDPRotation & rot1, double t)
582 {
583 #if COIN_DEBUG
584   if (t<0.0f || t>1.0f) {
585     SoDebugError::postWarning("SbDPRotation::slerp",
586                               "The t parameter (%f) is out of bounds [0,1]. "
587                               "Clamping to bounds.", t);
588     if (t<0.0f) t=0.0f;
589     else if (t>1.0f) t=1.0f;
590   }
591 #endif // COIN_DEBUG
592 
593   SbDPRotation from = rot0;
594   SbDPRotation to = rot1;
595 
596   double dot = from.quat.dot(to.quat);
597 
598   // Find the correct direction of the interpolation.
599   if(dot < 0.0f) {
600     dot = -dot;
601     to.quat.negate();
602   }
603 
604   // fallback to linear interpolation, in case we run out of floating
605   // point precision
606   double scale0 = 1.0 - t;
607   double scale1 = t;
608 
609   if ((1.0f - dot) > FLT_EPSILON) {
610     double angle = static_cast<double>(acos(dot));
611     double sinangle = static_cast<double>(sin(angle));
612     if (sinangle > FLT_EPSILON) {
613       // calculate spherical interpolation
614       scale0 = double(sin((1.0 - t) * angle)) / sinangle;
615       scale1 = double(sin(t * angle)) / sinangle;
616     }
617   }
618   SbVec4d vec = (scale0 * from.quat) + (scale1 * to.quat);
619   return SbDPRotation(vec[0], vec[1], vec[2], vec[3]);
620 }
621 
622 /*!
623   Returns an identity rotation.
624  */
625 SbDPRotation
identity(void)626 SbDPRotation::identity(void)
627 {
628   return SbDPRotation(0.0f, 0.0f, 0.0f, 1.0f);
629 }
630 
631 /*!
632   Dump the state of this object to the \a file stream. Only works in
633   debug version of library, method does nothing in an optimized compile.
634  */
635 void
print(FILE * fp) const636 SbDPRotation::print(FILE * fp) const
637 {
638 #if COIN_DEBUG
639   this->quat.print(fp);
640 #endif // COIN_DEBUG
641 }
642 
643 #ifdef COIN_TEST_SUITE
644 #include <Inventor/SbVec3d.h>
645 
BOOST_AUTO_TEST_CASE(tgsCompliance)646 BOOST_AUTO_TEST_CASE(tgsCompliance) {
647   SbDPRotation v = SbRotationd(SbVec3d(0,1,2),3);
648 }
649 #endif //COIN_TEST_SUITE
650