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