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