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 SbRotation SbRotation.h Inventor/SbRotation.h
35   \brief The SbRotation class represents a rotation in 3D space.
36 
37   \ingroup base
38 
39   SbRotation is used extensively throughout the Coin library.
40 
41   An SbRotation is stored internally as a quaternion for speed and
42   storage reasons, but inquiries can be done to get and set axis and
43   angle values for convenience.
44 
45 
46   Note that there is one \e very common mistake that is easy to make
47   when setting the value of an SbRotation, and that is to
48   inadvertently use the wrong SbRotation constructor. This example
49   should clarify the problem:
50 
51   \code
52   SbRotation rotation(0, 0, 1, 1.5707963f);
53   \endcode
54 
55   The programmer clearly tries to set a PI/2 rotation around the Z
56   axis, but this will fail, as the SbRotation constructor invoked
57   above is the one that takes as arguments the 4 floats of a \e
58   quaternion. What the programmer almost certainly wanted to do was to
59   use the SbRotation constructor that takes a rotation vector and a
60   rotation angle, which is invoked like this:
61 
62   \code
63   SbRotation rotation(SbVec3f(0, 0, 1), 1.5707963f);
64   \endcode
65 
66 
67   Another common problem is to set the rotation value to exactly 0.0,
68   while wanting to store just the information about a rotation \e
69   angle: rotations are internally handled as quaternions, and when
70   converting from an angle and a rotation value to a quaternion
71   representation, the information about the angle "gets lost" if there
72   is no actual rotation.
73 
74   \sa SbMatrix
75 */
76 
77 
78 #include <Inventor/SbRotation.h>
79 #include <Inventor/SbVec3f.h>
80 #include <Inventor/SbMatrix.h>
81 #include <Inventor/fields/SoSFRotation.h>
82 #include <cassert>
83 #include <cfloat>
84 #include "coinString.h"
85 
86 #if COIN_DEBUG
87 #include <Inventor/errors/SoDebugError.h>
88 #endif // COIN_DEBUG
89 
90 /*!
91   \fn float SbRotation::operator[](size_t n) const
92 
93   \brief returns the n'th quaternion of this rotation
94 */
95 
96 /*!
97   The default constructor just initializes a valid rotation. The
98   actual value is unspecified, and you should not depend on it.
99 */
SbRotation(void)100 SbRotation::SbRotation(void)
101   // This translates to zero rotation around the positive Z axis.
102   : quat(0.0f, 0.0f, 0.0f, 1.0f)
103 {
104 }
105 
106 /*!
107   Construct a new SbRotation object initialized with the given
108   axis-of-rotation and rotation angle.
109  */
SbRotation(const SbVec3f & axis,const float radians)110 SbRotation::SbRotation(const SbVec3f & axis, const float radians)
111 {
112 #if COIN_DEBUG
113   if (axis.length()==0.0f)
114     SoDebugError::postWarning("SbRotation::SbRotation",
115                               "axis parameter has zero length => "
116                               "undefined axis.");
117 #endif // COIN_DEBUG
118   this->setValue(axis, radians);
119 }
120 
121 /*!
122   Construct a new SbRotation object initialized with the given quaternion
123   components.
124 
125   The array must be ordered as follows:
126 
127   q[0] = x, q[1] = y, q[2] = z and q[3] = w, where the quaternion is
128   specified by q=w+xi+yj+zk.
129  */
SbRotation(const float q[4])130 SbRotation::SbRotation(const float q[4])
131 {
132   this->setValue(q);
133 }
134 
135 /*!
136   Construct a new SbRotation object initialized with the given quaternion
137   components.
138  */
SbRotation(const float q0,const float q1,const float q2,const float q3)139 SbRotation::SbRotation(const float q0, const float q1,
140                        const float q2, const float q3)
141 {
142   this->setValue(q0, q1, q2, q3);
143 }
144 
145 /*!
146   Construct a new SbRotation object initialized with the given rotation
147   matrix.
148  */
SbRotation(const SbMatrix & m)149 SbRotation::SbRotation(const SbMatrix & m)
150 {
151   this->setValue(m);
152 }
153 
154 /*!
155   Construct a rotation which is the minimum rotation necessary to make
156   vector \a rotateFrom point in the direction of vector \a rotateTo.
157 
158   Example:
159 
160   \code
161   #include <Inventor/SbRotation.h>
162   #include <Inventor/SbVec3f.h>
163   #include <cstdio>
164 
165   int
166   main(void)
167   {
168     SbVec3f from(10, 0, 0);
169     SbVec3f to(0, 10, 0);
170 
171     SbRotation rot(from, to);
172 
173     SbVec3f axis;
174     float angle;
175     rot.getValue(axis, angle);
176     axis.print(stdout);
177     printf("  angle==%f\n", angle);
178 
179     return 0;
180   }
181   \endcode
182 */
SbRotation(const SbVec3f & rotateFrom,const SbVec3f & rotateTo)183 SbRotation::SbRotation(const SbVec3f & rotateFrom, const SbVec3f & rotateTo)
184 {
185   // Parameters are checked in setValue().
186 
187   this->setValue(rotateFrom, rotateTo);
188 }
189 
190 /*!
191   Return pointer to an array with the rotation expressed as four
192   quaternion values.
193 
194   \sa setValue().
195 */
196 const float *
getValue(void) const197 SbRotation::getValue(void) const
198 {
199   return &this->quat[0];
200 }
201 
202 /*!
203   Return the four quaternion components representing the rotation.
204 
205   \sa setValue().
206  */
207 void
getValue(float & q0,float & q1,float & q2,float & q3) const208 SbRotation::getValue(float & q0, float & q1, float & q2, float & q3) const
209 {
210   q0 = this->quat[0];
211   q1 = this->quat[1];
212   q2 = this->quat[2];
213   q3 = this->quat[3];
214 }
215 
216 /*!
217   Set the rotation.
218 
219   \sa getValue().
220 */
221 SbRotation &
setValue(const float q0,const float q1,const float q2,const float q3)222 SbRotation::setValue(const float q0, const float q1,
223                      const float q2, const float q3)
224 {
225   this->quat[0] = q0;
226   this->quat[1] = q1;
227   this->quat[2] = q2;
228   this->quat[3] = q3;
229   if (this->quat.normalize() == 0.0f) {
230 #if COIN_DEBUG
231     SoDebugError::postWarning("SbRotation::setValue",
232                               "Quarternion has zero length => "
233                               "undefined rotation.");
234 #endif // COIN_DEBUG
235   }
236   return *this;
237 }
238 
239 /*!
240   Return the rotation in the form of an axis-of-rotation and a rotation
241   angle.
242 
243   \sa setValue().
244  */
245 void
getValue(SbVec3f & axis,float & radians) const246 SbRotation::getValue(SbVec3f & axis, float & radians) const
247 {
248   if((this->quat[3] >= -1.0f) && (this->quat[3] <= 1.0f)) {
249     radians = float(acos(this->quat[3])) * 2.0f;
250     float scale = static_cast<float>(sin(radians / 2.0f));
251 
252     if(scale != 0.0f) {
253       axis[0] = this->quat[0] / scale;
254       axis[1] = this->quat[1] / scale;
255       axis[2] = this->quat[2] / scale;
256       // FIXME: why not just flip the sign on each component according
257       // to "scale" and normalize the axis instead? 20010111 mortene.
258       return;
259     }
260   }
261 
262   // Quaternion can't be converted to axis and rotation angle, so we just
263   // set up values to indicate this.
264   axis.setValue(0.0f, 0.0f, 1.0f);
265   radians = 0.0f;
266 }
267 
268 /*!
269   Return this rotation in the form of a matrix.
270 
271   \sa setValue().
272  */
273 void
getValue(SbMatrix & matrix) const274 SbRotation::getValue(SbMatrix & matrix) const
275 {
276   // From:
277   // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm
278   float l = this->quat.length();
279   float x,y,z,w;
280   if (l > FLT_EPSILON) {
281     // normalize it
282     x = this->quat[0] / l;
283     y = this->quat[1] / l;
284     z = this->quat[2] / l;
285     w = this->quat[3] / l;
286   }
287   else {
288     // identity
289     x = y = z = 0.0f;
290     w = 1.0f;
291   }
292   matrix[0][0] = 1.0f - 2.0f * (y * y + z * z);
293   matrix[0][1] = 2.0f * (x * y + z * w);
294   matrix[0][2] = 2.0f * (z * x - y * w);
295   matrix[0][3] = 0.0f;
296 
297   matrix[1][0] = 2.0f * (x * y - z * w);
298   matrix[1][1] = 1.0f - 2.0f * (z * z + x * x);
299   matrix[1][2] = 2.0f * (y * z + x * w);
300   matrix[1][3] = 0;
301 
302   matrix[2][0] = 2.0f * (z * x + y * w);
303   matrix[2][1] = 2.0f * (y * z - x * w);
304   matrix[2][2] = 1.0f - 2.0f * (y * y + x * x);
305   matrix[2][3] = 0.0f;
306 
307   matrix[3][0] = 0.0f;
308   matrix[3][1] = 0.0f;
309   matrix[3][2] = 0.0f;
310   matrix[3][3] = 1.0f;
311 }
312 
313 /*!
314   Invert the rotation. Returns reference to self.
315 
316   \sa inverse()
317  */
318 SbRotation &
invert(void)319 SbRotation::invert(void)
320 {
321   float length = this->quat.length();
322 #if COIN_DEBUG
323   if (length==0.0f)
324     SoDebugError::postWarning("SbRotation::invert",
325                               "Quarternion has zero length => "
326                               "undefined rotation.");
327 #endif // COIN_DEBUG
328 
329   // Optimize by doing 1 div and 4 muls instead of 4 divs.
330   float inv = 1.0f / length;
331 
332   this->quat[0] = -this->quat[0] * inv;
333   this->quat[1] = -this->quat[1] * inv;
334   this->quat[2] = -this->quat[2] * inv;
335   this->quat[3] = this->quat[3] * inv;
336   return *this;
337 }
338 
339 /*!
340   Non-destructively inverses the rotation and returns the result.
341 
342   \sa invert()
343  */
344 SbRotation
inverse(void) const345 SbRotation::inverse(void) const
346 {
347   float length = this->quat.length();
348 #if COIN_DEBUG
349   if (length==0.0f)
350     SoDebugError::postWarning("SbRotation::inverse",
351                               "Quaternion has zero length => "
352                               "undefined rotation.");
353 #endif // COIN_DEBUG
354 
355   // Optimize by doing 1 div and 4 muls instead of 4 divs.
356   float inv = 1.0f / length;
357 
358   SbRotation rot;
359   rot.quat[0] = -this->quat[0] * inv;
360   rot.quat[1] = -this->quat[1] * inv;
361   rot.quat[2] = -this->quat[2] * inv;
362   rot.quat[3] = this->quat[3] * inv;
363 
364   return rot;
365 }
366 
367 /*!
368   Reset the rotation by the four quaternions in the array.
369 
370   \sa getValue().
371  */
372 SbRotation&
setValue(const float q[4])373 SbRotation::setValue(const float q[4])
374 {
375   this->quat[0] = q[0];
376   this->quat[1] = q[1];
377   this->quat[2] = q[2];
378   this->quat[3] = q[3];
379   if (this->quat.normalize() == 0.0f) {
380 #if COIN_DEBUG
381     SoDebugError::postWarning("SbRotation::setValue",
382                               "Quarternion has zero length => "
383                               "undefined rotation.");
384 #endif // COIN_DEBUG
385   }
386   return *this;
387 }
388 
389 /*!
390   Set the rotation from the components of the given matrix. Returns
391   reference to self.
392 
393   \sa getValue().
394  */
395 SbRotation &
setValue(const SbMatrix & m)396 SbRotation::setValue(const SbMatrix & m)
397 {
398   float scalerow = m[0][0] + m[1][1] + m[2][2];
399 
400   if (scalerow > 0.0f) {
401     float s = static_cast<float>(sqrt(scalerow + m[3][3]));
402     this->quat[3] = s * 0.5f;
403     s = 0.5f / s;
404 
405     this->quat[0] = (m[1][2] - m[2][1]) * s;
406     this->quat[1] = (m[2][0] - m[0][2]) * s;
407     this->quat[2] = (m[0][1] - m[1][0]) * s;
408   }
409   else {
410     int i = 0;
411     if (m[1][1] > m[0][0]) i = 1;
412     if (m[2][2] > m[i][i]) i = 2;
413 
414     int j = (i+1)%3;
415     int k = (j+1)%3;
416 
417     float s = static_cast<float>(sqrt((m[i][i] - (m[j][j] + m[k][k])) + m[3][3]));
418 
419     this->quat[i] = s * 0.5f;
420     s = 0.5f / s;
421 
422     this->quat[3] = (m[j][k] - m[k][j]) * s;
423     this->quat[j] = (m[i][j] + m[j][i]) * s;
424     this->quat[k] = (m[i][k] + m[k][i]) * s;
425   }
426 
427   if (m[3][3] != 1.0f) this->operator*=(1.0f/static_cast<float>(sqrt(m[3][3])));
428   return *this;
429 }
430 
431 /*!
432   Reset rotation with the given axis-of-rotation and rotation angle.
433   Returns reference to self.
434 
435   Make sure \a axis is not the null vector when calling this method.
436 
437   \sa getValue().
438  */
439 SbRotation &
setValue(const SbVec3f & axis,const float radians)440 SbRotation::setValue(const SbVec3f & axis, const float radians)
441 {
442 #if COIN_DEBUG
443   if (axis.length()==0.0f)
444     SoDebugError::postWarning("SbRotation::setValue",
445                               "axis parameter has zero length.");
446 #endif // COIN_DEBUG
447 
448   // From <http://www.automation.hut.fi/~jaro/thesis/hyper/node9.html>.
449 
450   this->quat[3] = static_cast<float>(cos(radians/2));
451 
452   const float sineval = static_cast<float>(sin(radians/2));
453   SbVec3f a = axis;
454   // we test for a null vector above
455   (void) a.normalize();
456   this->quat[0] = a[0] * sineval;
457   this->quat[1] = a[1] * sineval;
458   this->quat[2] = a[2] * sineval;
459   return *this;
460 }
461 
462 /*!
463   Construct a rotation which is the minimum rotation necessary to make
464   vector \a rotateFrom point in the direction of vector \a rotateTo.
465 
466   Returns reference to self.
467 
468   See SbRotation constructor with corresponding input arguments for a
469   simple code example.
470 
471   \sa getValue().
472  */
473 SbRotation &
setValue(const SbVec3f & rotateFrom,const SbVec3f & rotateTo)474 SbRotation::setValue(const SbVec3f & rotateFrom, const SbVec3f & rotateTo)
475 {
476 #if COIN_DEBUG
477   // Check if the vectors are valid.
478   if (rotateFrom.length()==0.0f) {
479     SoDebugError::postWarning("SbRotation::setValue",
480                               "rotateFrom argument has zero length.");
481   }
482   if (rotateTo.length()==0.0f) {
483     SoDebugError::postWarning("SbRotation::setValue",
484                               "rotateTo argument has zero length.");
485   }
486 #endif // COIN_DEBUG
487   SbVec3f from(rotateFrom);
488   // we test for a null vector above
489   (void) from.normalize();
490   SbVec3f to(rotateTo);
491   // we test for a null vector above
492   (void) to.normalize();
493 
494   const float dot = from.dot(to);
495   SbVec3f crossvec = from.cross(to);
496   const float crosslen = crossvec.normalize();
497 
498   if (crosslen == 0.0f) { // Parallel vectors
499     // Check if they are pointing in the same direction.
500     if (dot > 0.0f) {
501       this->setValue(0.0f, 0.0f, 0.0f, 1.0f);
502     }
503     // Ok, so they are parallel and pointing in the opposite direction
504     // of each other.
505     else {
506       // Try crossing with x axis.
507       SbVec3f t = from.cross(SbVec3f(1.0f, 0.0f, 0.0f));
508       // If not ok, cross with y axis.
509       if (t.normalize() == 0.0f) {
510         t = from.cross(SbVec3f(0.0f, 1.0f, 0.0f));
511         (void) t.normalize();
512       }
513       this->setValue(t[0], t[1], t[2], 0.0f);
514     }
515   }
516   else { // Vectors are not parallel
517     // The fabs() wrapping is to avoid problems when `dot' "overflows"
518     // a tiny wee bit, which can lead to sqrt() returning NaN.
519     crossvec *= static_cast<float>(sqrt(0.5f * fabs(1.0f - dot)));
520     // The fabs() wrapping is to avoid problems when `dot' "underflows"
521     // a tiny wee bit, which can lead to sqrt() returning NaN.
522     this->setValue(crossvec[0], crossvec[1], crossvec[2],
523                    static_cast<float>(sqrt(0.5 * fabs(1.0 + dot))));
524   }
525 
526   return *this;
527 }
528 
529 /*!
530   Multiplies the quaternions.
531 
532   Note that order is important when combining quaternions with the
533   multiplication operator.
534  */
535 SbRotation &
operator *=(const SbRotation & q)536 SbRotation::operator*=(const SbRotation & q)
537 {
538   // Formula from <http://www.lboro.ac.uk/departments/ma/gallery/quat/>
539 
540   float tx, ty, tz, tw;
541   this->getValue(tx, ty, tz, tw);
542   float qx, qy, qz, qw;
543   q.getValue(qx, qy, qz, qw);
544 
545   this->setValue(qw*tx + qx*tw + qy*tz - qz*ty,
546                  qw*ty - qx*tz + qy*tw + qz*tx,
547                  qw*tz + qx*ty - qy*tx + qz*tw,
548                  qw*tw - qx*tx - qy*ty - qz*tz);
549   return *this;
550 }
551 
552 /*!
553   Multiplies components of quaternion with scalar value \a s.
554   Returns reference to self.
555  */
556 SbRotation &
operator *=(const float s)557 SbRotation::operator*=(const float s)
558 {
559   this->quat *= s;
560   return *this;
561 }
562 
563 /*!
564   \relates SbRotation
565 
566   Check if the two rotations are equal.
567 
568   \sa equals().
569  */
570 int
operator ==(const SbRotation & q1,const SbRotation & q2)571 operator==(const SbRotation & q1, const SbRotation & q2)
572 {
573   return (q1.quat == q2.quat);
574 }
575 
576 /*!
577   \relates SbRotation
578 
579   Check if the two rotations are unequal.
580 
581   \sa equals().
582  */
583 int
operator !=(const SbRotation & q1,const SbRotation & q2)584 operator!=(const SbRotation & q1, const SbRotation & q2)
585 {
586   return !(q1 == q2);
587 }
588 
589 /*!
590   Check the internal quaternion representation vectors for equality
591   within the given tolerance.
592  */
593 SbBool
equals(const SbRotation & r,float tolerance) const594 SbRotation::equals(const SbRotation & r, float tolerance) const
595 {
596   return this->quat.equals(r.quat, tolerance);
597 }
598 
599 /*!
600   \relates SbRotation
601 
602   Multiplies the two rotations and returns the result.
603 
604   Note that order is important when combining quaternions with the
605   multiplication operator.
606 */
607 SbRotation
operator *(const SbRotation & q1,const SbRotation & q2)608 operator*(const SbRotation & q1, const SbRotation & q2)
609 {
610   SbRotation q(q1);
611   q *= q2;
612   return q;
613 }
614 
615 /*!
616   Rotate the \a src vector and put the result in \a dst.
617 
618   It is safe to let src and dst be the same SbVec3f instance.
619 */
620 void
multVec(const SbVec3f & src,SbVec3f & dst) const621 SbRotation::multVec(const SbVec3f & src, SbVec3f & dst) const
622 {
623   SbVec3f qv(this->quat[0], this->quat[1], this->quat[2]);
624   float r = this->quat[3];
625 
626   SbVec3f a = qv.cross(src);
627   SbVec3f b = qv.cross(a);
628 
629   dst = src + 2.0f * (r * a + b);
630 }
631 
632 /*!
633   Scale the angle of rotation by \a scaleFactor.
634  */
635 void
scaleAngle(const float scaleFactor)636 SbRotation::scaleAngle(const float scaleFactor)
637 {
638   SbVec3f axis;
639   float rad;
640 
641   this->getValue(axis, rad);
642   this->setValue(axis, rad * scaleFactor);
643 }
644 
645 /*!
646   \relates SbRotation
647 
648   Interpolates along the shortest path between the two rotation
649   positions (from \a rot0 to \a rot1).
650 
651   Returns the SbRotation which will rotate \a rot0 the given part \a t
652   of the spherical distance towards \a rot1, where \a t=0 will yield \a rot0
653   and \a t=1 will yield \a rot1.
654 
655   \a t should be in the interval [0, 1].
656  */
657 SbRotation
slerp(const SbRotation & rot0,const SbRotation & rot1,float t)658 SbRotation::slerp(const SbRotation & rot0, const SbRotation & rot1, float t)
659 {
660 #if COIN_DEBUG
661   if (t<0.0f || t>1.0f) {
662     SoDebugError::postWarning("SbRotation::slerp",
663                               "The t parameter (%f) is out of bounds [0,1]. "
664                               "Clamping to bounds.", t);
665     if (t<0.0f) t=0.0f;
666     else if (t>1.0f) t=1.0f;
667   }
668 #endif // COIN_DEBUG
669 
670   SbRotation from = rot0;
671   SbRotation to = rot1;
672 
673   float dot = from.quat.dot(to.quat);
674 
675   // Find the correct direction of the interpolation.
676   if(dot < 0.0f) {
677     dot = -dot;
678     to.quat.negate();
679   }
680 
681   // fallback to linear interpolation, in case we run out of floating
682   // point precision
683   float scale0 = 1.0f - t;
684   float scale1 = t;
685 
686   if ((1.0f - dot) > FLT_EPSILON) {
687     float angle = static_cast<float>(acos(dot));
688     float sinangle = static_cast<float>(sin(angle));
689     if (sinangle > FLT_EPSILON) {
690       // calculate spherical interpolation
691       scale0 = float(sin((1.0 - t) * angle)) / sinangle;
692       scale1 = float(sin(t * angle)) / sinangle;
693     }
694   }
695   SbVec4f vec = (scale0 * from.quat) + (scale1 * to.quat);
696   return SbRotation(vec[0], vec[1], vec[2], vec[3]);
697 }
698 
699 /*!
700   Returns an identity rotation.
701  */
702 SbRotation
identity(void)703 SbRotation::identity(void)
704 {
705   return SbRotation(0.0f, 0.0f, 0.0f, 1.0f);
706 }
707 
708 /*!
709   Return a string representation of this object
710 */
711 SbString
toString() const712 SbRotation::toString() const
713 {
714   return CoinInternal::ToString(*this);
715 }
716 
717 /*!
718   Convert from a string representation, return wether this is a valid conversion
719 */
720 SbBool
fromString(const SbString & str)721 SbRotation::fromString(const SbString & str)
722 {
723   SbBool conversionOk;
724   *this = CoinInternal::FromString<SbRotation>(str,&conversionOk);
725   return conversionOk;
726 }
727 
728 /*!
729   Dump the state of this object to the \a fp file stream. Only works
730   in debug version of library, method does nothing in an optimized
731   compile.
732  */
733 void
print(FILE * fp) const734 SbRotation::print(FILE * fp) const
735 {
736 #if COIN_DEBUG
737   this->quat.print(fp);
738 #endif // COIN_DEBUG
739 }
740 
741 #ifdef COIN_TEST_SUITE
742 #include <Inventor/SbTypeInfo.h>
743 #include <Inventor/SbVec3f.h>
744 #include <Inventor/SbVec4f.h>
745 #include <boost/lexical_cast.hpp>
746 #include <cassert>
747 #include <cstdio>
748 
749 typedef SbRotation ToTest;
BOOST_AUTO_TEST_CASE(operatorBrackets)750 BOOST_AUTO_TEST_CASE(operatorBrackets)
751 {
752   const int FLOAT_SENSITIVITY = 1;
753   const float SQRT2 = sqrt(2.f)/2.f;
754   SbRotation rot(0,-SQRT2,0,SQRT2);
755 
756   for (int i=0;i<4;++i) {
757     int premultiply = ((i&0x1)*((i&0x2)-1));
758     float testVal = premultiply*SQRT2;
759     BOOST_CHECK_MESSAGE(
760                         floatEquals(testVal,rot[i],FLOAT_SENSITIVITY),
761                         std::string("Wrong value when trying to access value #")
762                         + boost::lexical_cast<std::string>(i)
763                         + ": "
764                         + boost::lexical_cast<std::string>(rot[i]) +
765                         " == "
766                         + boost::lexical_cast<std::string>(testVal)
767                         );
768   }
769 }
770 
BOOST_AUTO_TEST_CASE(toString)771 BOOST_AUTO_TEST_CASE(toString) {
772   ToTest val(SbVec3f(0, -1, 0),  1);
773   SbString str("0 -1 0  1");
774   SbVec4f expected(0.f, -1.f, 0.f, 1.f), actual;
775   sscanf(val.toString().getString(), "%f %f %f  %f", &actual[0], &actual[1], &actual[2], &actual[3]);
776   BOOST_CHECK_MESSAGE(actual.equals(expected, 0.000001f),
777                       std::string("Mismatch between ") +  val.toString().getString() + " and control string " + str.getString());
778 
779 }
780 
BOOST_AUTO_TEST_CASE(fromString)781 BOOST_AUTO_TEST_CASE(fromString) {
782   ToTest foo;
783   SbString test = "0 -1 0 1";
784   ToTest trueVal(SbVec3f(0, -1, 0),  1);
785   SbBool conversionOk = foo.fromString(test);
786   BOOST_CHECK_MESSAGE(conversionOk && trueVal == foo,
787                       std::string("Mismatch between ") +  foo.toString().getString() + " and control " + trueVal.toString().getString());
788 }
789 
BOOST_AUTO_TEST_CASE(fromInvalidString)790 BOOST_AUTO_TEST_CASE(fromInvalidString) {
791   ToTest foo;
792   SbString test = "2.- 2 3 4";
793   ToTest trueVal(1,2,3,4);
794   SbBool conversionOk = foo.fromString(test);
795   BOOST_CHECK_MESSAGE(conversionOk == FALSE,
796                       std::string("Able to convert from ") + test.getString() + " which is not a valid " + SbTypeInfo<ToTest>::getTypeName() + " representation");
797 }
798 
799 #endif //COIN_TEST_SUITE
800