1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquaternion.h"
5#include <QtCore/qdatastream.h>
6#include <QtCore/qmath.h>
7#include <QtCore/qvariant.h>
8#include <QtCore/qdebug.h>
9
10#include <cmath>
11
12QT_BEGIN_NAMESPACE
13
14#ifndef QT_NO_QUATERNION
15
16/*!
17 \class QQuaternion
18 \brief The QQuaternion class represents a quaternion consisting of a vector and scalar.
19 \since 4.6
20 \ingroup painting-3D
21 \inmodule QtGui
22
23 Quaternions are used to represent rotations in 3D space, and
24 consist of a 3D rotation axis specified by the x, y, and z
25 coordinates, and a scalar representing the rotation angle.
26*/
27
28/*!
29 \fn QQuaternion::QQuaternion() noexcept
30
31 Constructs an identity quaternion (1, 0, 0, 0), i.e. with the vector (0, 0, 0)
32 and scalar 1.
33*/
34
35/*!
36 \fn QQuaternion::QQuaternion(Qt::Initialization) noexcept
37 \since 5.5
38 \internal
39
40 Constructs a quaternion without initializing the contents.
41*/
42
43/*!
44 \fn QQuaternion::QQuaternion(float scalar, float xpos, float ypos, float zpos) noexcept
45
46 Constructs a quaternion with the vector (\a xpos, \a ypos, \a zpos)
47 and \a scalar.
48*/
49
50#ifndef QT_NO_VECTOR3D
51
52/*!
53 \fn QQuaternion::QQuaternion(float scalar, const QVector3D &vector) noexcept
54
55 Constructs a quaternion vector from the specified \a vector and
56 \a scalar.
57
58 \sa vector(), scalar()
59*/
60
61/*!
62 \fn QVector3D QQuaternion::vector() const noexcept
63
64 Returns the vector component of this quaternion.
65
66 \sa setVector(), scalar()
67*/
68
69/*!
70 \fn void QQuaternion::setVector(const QVector3D &vector) noexcept
71
72 Sets the vector component of this quaternion to \a vector.
73
74 \sa vector(), setScalar()
75*/
76
77#endif
78
79/*!
80 \fn void QQuaternion::setVector(float x, float y, float z) noexcept
81
82 Sets the vector component of this quaternion to (\a x, \a y, \a z).
83
84 \sa vector(), setScalar()
85*/
86
87#ifndef QT_NO_VECTOR4D
88
89/*!
90 \fn QQuaternion::QQuaternion(const QVector4D &vector) noexcept
91
92 Constructs a quaternion from the components of \a vector.
93*/
94
95/*!
96 \fn QVector4D QQuaternion::toVector4D() const noexcept
97
98 Returns this quaternion as a 4D vector.
99*/
100
101#endif
102
103/*!
104 \fn bool QQuaternion::isNull() const noexcept
105
106 Returns \c true if the x, y, z, and scalar components of this
107 quaternion are set to 0.0; otherwise returns \c false.
108*/
109
110/*!
111 \fn bool QQuaternion::isIdentity() const noexcept
112
113 Returns \c true if the x, y, and z components of this
114 quaternion are set to 0.0, and the scalar component is set
115 to 1.0; otherwise returns \c false.
116*/
117
118/*!
119 \fn float QQuaternion::x() const noexcept
120
121 Returns the x coordinate of this quaternion's vector.
122
123 \sa setX(), y(), z(), scalar()
124*/
125
126/*!
127 \fn float QQuaternion::y() const noexcept
128
129 Returns the y coordinate of this quaternion's vector.
130
131 \sa setY(), x(), z(), scalar()
132*/
133
134/*!
135 \fn float QQuaternion::z() const noexcept
136
137 Returns the z coordinate of this quaternion's vector.
138
139 \sa setZ(), x(), y(), scalar()
140*/
141
142/*!
143 \fn float QQuaternion::scalar() const noexcept
144
145 Returns the scalar component of this quaternion.
146
147 \sa setScalar(), x(), y(), z()
148*/
149
150/*!
151 \fn void QQuaternion::setX(float x) noexcept
152
153 Sets the x coordinate of this quaternion's vector to the given
154 \a x coordinate.
155
156 \sa x(), setY(), setZ(), setScalar()
157*/
158
159/*!
160 \fn void QQuaternion::setY(float y) noexcept
161
162 Sets the y coordinate of this quaternion's vector to the given
163 \a y coordinate.
164
165 \sa y(), setX(), setZ(), setScalar()
166*/
167
168/*!
169 \fn void QQuaternion::setZ(float z) noexcept
170
171 Sets the z coordinate of this quaternion's vector to the given
172 \a z coordinate.
173
174 \sa z(), setX(), setY(), setScalar()
175*/
176
177/*!
178 \fn void QQuaternion::setScalar(float scalar) noexcept
179
180 Sets the scalar component of this quaternion to \a scalar.
181
182 \sa scalar(), setX(), setY(), setZ()
183*/
184
185/*!
186 \fn float QQuaternion::dotProduct(const QQuaternion &q1, const QQuaternion &q2) noexcept
187 \since 5.5
188
189 Returns the dot product of \a q1 and \a q2.
190
191 \sa length()
192*/
193
194/*!
195 Returns the length of the quaternion. This is also called the "norm".
196
197 \sa lengthSquared(), normalized(), dotProduct()
198*/
199float QQuaternion::length() const
200{
201 return qHypot(first: xp, rest: yp, rest: zp, rest: wp);
202}
203
204/*!
205 Returns the squared length of the quaternion.
206
207 \note Though cheap to compute, this is susceptible to overflow and underflow
208 that length() avoids in many cases.
209
210 \sa length(), dotProduct()
211*/
212float QQuaternion::lengthSquared() const
213{
214 return xp * xp + yp * yp + zp * zp + wp * wp;
215}
216
217/*!
218 Returns the normalized unit form of this quaternion.
219
220 If this quaternion is null, then a null quaternion is returned.
221 If the length of the quaternion is very close to 1, then the quaternion
222 will be returned as-is. Otherwise the normalized form of the
223 quaternion of length 1 will be returned.
224
225 \sa normalize(), length(), dotProduct()
226*/
227QQuaternion QQuaternion::normalized() const
228{
229 const float scale = length();
230 if (qFuzzyIsNull(f: scale))
231 return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
232 return *this / scale;
233}
234
235/*!
236 Normalizes the current quaternion in place. Nothing happens if this
237 is a null quaternion or the length of the quaternion is very close to 1.
238
239 \sa length(), normalized()
240*/
241void QQuaternion::normalize()
242{
243 const float len = length();
244 if (qFuzzyIsNull(f: len))
245 return;
246
247 xp /= len;
248 yp /= len;
249 zp /= len;
250 wp /= len;
251}
252
253/*!
254 \fn QQuaternion QQuaternion::inverted() const noexcept
255 \since 5.5
256
257 Returns the inverse of this quaternion.
258 If this quaternion is null, then a null quaternion is returned.
259
260 \sa isNull(), length()
261*/
262
263/*!
264 \fn QQuaternion QQuaternion::conjugated() const noexcept
265 \since 5.5
266
267 Returns the conjugate of this quaternion, which is
268 (-x, -y, -z, scalar).
269*/
270
271/*!
272 Rotates \a vector with this quaternion to produce a new vector
273 in 3D space. The following code:
274
275 \snippet code/src_gui_math3d_qquaternion.cpp 0
276
277 is equivalent to the following:
278
279 \snippet code/src_gui_math3d_qquaternion.cpp 1
280*/
281QVector3D QQuaternion::rotatedVector(const QVector3D &vector) const
282{
283 return (*this * QQuaternion(0, vector) * conjugated()).vector();
284}
285
286/*!
287 \fn QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion) noexcept
288
289 Adds the given \a quaternion to this quaternion and returns a reference to
290 this quaternion.
291
292 \sa operator-=()
293*/
294
295/*!
296 \fn QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion) noexcept
297
298 Subtracts the given \a quaternion from this quaternion and returns a
299 reference to this quaternion.
300
301 \sa operator+=()
302*/
303
304/*!
305 \fn QQuaternion &QQuaternion::operator*=(float factor) noexcept
306
307 Multiplies this quaternion's components by the given \a factor, and
308 returns a reference to this quaternion.
309
310 \sa operator/=()
311*/
312
313/*!
314 \fn QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion) noexcept
315
316 Multiplies this quaternion by \a quaternion and returns a reference
317 to this quaternion.
318*/
319
320/*!
321 \fn QQuaternion &QQuaternion::operator/=(float divisor)
322
323 Divides this quaternion's components by the given \a divisor, and
324 returns a reference to this quaternion.
325
326 \sa operator*=()
327*/
328
329#ifndef QT_NO_VECTOR3D
330
331/*!
332 \fn void QQuaternion::getAxisAndAngle(QVector3D *axis, float *angle) const noexcept
333 \since 5.5
334 \overload
335
336 Extracts a 3D axis \a axis and a rotating angle \a angle (in degrees)
337 that corresponds to this quaternion.
338
339 Both \a axis and \a angle must be valid, non-\nullptr pointers,
340 otherwise the behavior is undefined.
341
342 \sa fromAxisAndAngle()
343*/
344
345/*!
346 Creates a normalized quaternion that corresponds to rotating through
347 \a angle degrees about the specified 3D \a axis.
348
349 \sa getAxisAndAngle()
350*/
351QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D &axis, float angle)
352{
353 // Algorithm from:
354 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56
355 // We normalize the result just in case the values are close
356 // to zero, as suggested in the above FAQ.
357 float a = qDegreesToRadians(degrees: angle / 2.0f);
358 float s = std::sin(x: a);
359 float c = std::cos(x: a);
360 QVector3D ax = axis.normalized();
361 return QQuaternion(c, ax.x() * s, ax.y() * s, ax.z() * s).normalized();
362}
363
364#endif
365
366/*!
367 \since 5.5
368
369 Extracts a 3D axis (\a x, \a y, \a z) and a rotating angle \a angle (in degrees)
370 that corresponds to this quaternion.
371
372 All of \a x, \a y, \a z, and \a angle must be valid, non-\nullptr pointers,
373 otherwise the behavior is undefined.
374
375 \sa fromAxisAndAngle()
376*/
377void QQuaternion::getAxisAndAngle(float *x, float *y, float *z, float *angle) const
378{
379 Q_ASSERT(x && y && z && angle);
380
381 // The quaternion representing the rotation is
382 // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)
383
384 const float length = qHypot(x: xp, y: yp, z: zp);
385 if (!qFuzzyIsNull(f: length)) {
386 if (qFuzzyCompare(p1: length, p2: 1.0f)) {
387 *x = xp;
388 *y = yp;
389 *z = zp;
390 } else {
391 *x = xp / length;
392 *y = yp / length;
393 *z = zp / length;
394 }
395 *angle = qRadiansToDegrees(radians: 2.0f * std::atan2(y: length, x: wp));
396 } else {
397 // angle is 0 (mod 2*pi), so any axis will fit
398 *x = *y = *z = *angle = 0.0f;
399 }
400}
401
402/*!
403 Creates a normalized quaternion that corresponds to rotating through
404 \a angle degrees about the 3D axis (\a x, \a y, \a z).
405
406 \sa getAxisAndAngle()
407*/
408QQuaternion QQuaternion::fromAxisAndAngle
409 (float x, float y, float z, float angle)
410{
411 float length = qHypot(x, y, z);
412 if (!qFuzzyCompare(p1: length, p2: 1.0f) && !qFuzzyIsNull(f: length)) {
413 x /= length;
414 y /= length;
415 z /= length;
416 }
417 float a = qDegreesToRadians(degrees: angle / 2.0f);
418 float s = std::sin(x: a);
419 float c = std::cos(x: a);
420 return QQuaternion(c, x * s, y * s, z * s).normalized();
421}
422
423#ifndef QT_NO_VECTOR3D
424
425/*!
426 \fn QVector3D QQuaternion::toEulerAngles() const
427 \since 5.5
428
429 Calculates roll, pitch, and yaw Euler angles (in degrees)
430 that correspond to this quaternion.
431
432 \sa fromEulerAngles()
433*/
434
435/*!
436 \fn QQuaternion QQuaternion::fromEulerAngles(const QVector3D &angles)
437 \since 5.5
438 \overload
439
440 Creates a quaternion that corresponds to a rotation of \a angles:
441 angles.\l{QVector3D::}{z()} degrees around the z axis,
442 angles.\l{QVector3D::}{x()} degrees around the x axis, and
443 angles.\l{QVector3D::}{y()} degrees around the y axis (in that order).
444
445 \sa toEulerAngles()
446*/
447
448#endif // QT_NO_VECTOR3D
449
450/*!
451 \since 5.5
452
453 Calculates \a roll, \a pitch, and \a yaw Euler angles (in degrees)
454 that corresponds to this quaternion.
455
456 All of \a pitch, \a yaw, and \a roll must be valid, non-\nullptr pointers,
457 otherwise the behavior is undefined.
458
459 \sa fromEulerAngles()
460*/
461void QQuaternion::getEulerAngles(float *pitch, float *yaw, float *roll) const
462{
463 Q_ASSERT(pitch && yaw && roll);
464
465 // Algorithm adapted from:
466 // https://ingmec.ual.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf
467 // "A tutorial on SE(3) transformation parameterizations and on-manifold optimization".
468
469 // We can only detect Gimbal lock when we normalize, which we can't do when
470 // length is nearly zero. Do so before multiplying coordinates, to avoid
471 // underflow.
472 const float len = length();
473 const bool rescale = !qFuzzyIsNull(f: len);
474 const float xps = rescale ? xp / len : xp;
475 const float yps = rescale ? yp / len : yp;
476 const float zps = rescale ? zp / len : zp;
477 const float wps = rescale ? wp / len : wp;
478
479 const float xx = xps * xps;
480 const float xy = xps * yps;
481 const float xz = xps * zps;
482 const float xw = xps * wps;
483 const float yy = yps * yps;
484 const float yz = yps * zps;
485 const float yw = yps * wps;
486 const float zz = zps * zps;
487 const float zw = zps * wps;
488
489 // For the common case, we have a hidden division by cos(pitch) to calculate
490 // yaw and roll: atan2(a / cos(pitch), b / cos(pitch)) = atan2(a, b). This equation
491 // wouldn't work if cos(pitch) is close to zero (i.e. abs(sin(pitch)) =~ 1.0).
492 // This threshold is copied from qFuzzyIsNull() to avoid the hidden division by zero.
493 constexpr float epsilon = 0.00001f;
494
495 const float sinp = -2.0f * (yz - xw);
496 if (std::abs(x: sinp) < 1.0f - epsilon) {
497 *pitch = std::asin(x: sinp);
498 *yaw = std::atan2(y: 2.0f * (xz + yw), x: 1.0f - 2.0f * (xx + yy));
499 *roll = std::atan2(y: 2.0f * (xy + zw), x: 1.0f - 2.0f * (xx + zz));
500 } else {
501 // Gimbal lock case, which doesn't have a unique solution. We just use
502 // XY rotation.
503 *pitch = std::copysign(x: static_cast<float>(M_PI_2), y: sinp);
504 *yaw = 2.0f * std::atan2(y: yps, x: wps);
505 *roll = 0.0f;
506 }
507
508 *pitch = qRadiansToDegrees(radians: *pitch);
509 *yaw = qRadiansToDegrees(radians: *yaw);
510 *roll = qRadiansToDegrees(radians: *roll);
511}
512
513/*!
514 \since 5.5
515
516 Creates a quaternion that corresponds to a rotation of
517 \a roll degrees around the z axis, \a pitch degrees around the x axis,
518 and \a yaw degrees around the y axis (in that order).
519
520 \sa getEulerAngles()
521*/
522QQuaternion QQuaternion::fromEulerAngles(float pitch, float yaw, float roll)
523{
524 // Algorithm from:
525 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60
526
527 pitch = qDegreesToRadians(degrees: pitch);
528 yaw = qDegreesToRadians(degrees: yaw);
529 roll = qDegreesToRadians(degrees: roll);
530
531 pitch *= 0.5f;
532 yaw *= 0.5f;
533 roll *= 0.5f;
534
535 const float c1 = std::cos(x: yaw);
536 const float s1 = std::sin(x: yaw);
537 const float c2 = std::cos(x: roll);
538 const float s2 = std::sin(x: roll);
539 const float c3 = std::cos(x: pitch);
540 const float s3 = std::sin(x: pitch);
541 const float c1c2 = c1 * c2;
542 const float s1s2 = s1 * s2;
543
544 const float w = c1c2 * c3 + s1s2 * s3;
545 const float x = c1c2 * s3 + s1s2 * c3;
546 const float y = s1 * c2 * c3 - c1 * s2 * s3;
547 const float z = c1 * s2 * c3 - s1 * c2 * s3;
548
549 return QQuaternion(w, x, y, z);
550}
551
552/*!
553 \since 5.5
554
555 Creates a rotation matrix that corresponds to this quaternion.
556
557 \note If this quaternion is not normalized,
558 the resulting rotation matrix will contain scaling information.
559
560 \sa fromRotationMatrix(), getAxes()
561*/
562QMatrix3x3 QQuaternion::toRotationMatrix() const
563{
564 // Algorithm from:
565 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54
566
567 QMatrix3x3 rot3x3(Qt::Uninitialized);
568
569 const float f2x = xp + xp;
570 const float f2y = yp + yp;
571 const float f2z = zp + zp;
572 const float f2xw = f2x * wp;
573 const float f2yw = f2y * wp;
574 const float f2zw = f2z * wp;
575 const float f2xx = f2x * xp;
576 const float f2xy = f2x * yp;
577 const float f2xz = f2x * zp;
578 const float f2yy = f2y * yp;
579 const float f2yz = f2y * zp;
580 const float f2zz = f2z * zp;
581
582 rot3x3(0, 0) = 1.0f - (f2yy + f2zz);
583 rot3x3(0, 1) = f2xy - f2zw;
584 rot3x3(0, 2) = f2xz + f2yw;
585 rot3x3(1, 0) = f2xy + f2zw;
586 rot3x3(1, 1) = 1.0f - (f2xx + f2zz);
587 rot3x3(1, 2) = f2yz - f2xw;
588 rot3x3(2, 0) = f2xz - f2yw;
589 rot3x3(2, 1) = f2yz + f2xw;
590 rot3x3(2, 2) = 1.0f - (f2xx + f2yy);
591
592 return rot3x3;
593}
594
595/*!
596 \since 5.5
597
598 Creates a quaternion that corresponds to the rotation matrix \a rot3x3.
599
600 \note If the given rotation matrix is not normalized,
601 the resulting quaternion will contain scaling information.
602
603 \sa toRotationMatrix(), fromAxes()
604*/
605QQuaternion QQuaternion::fromRotationMatrix(const QMatrix3x3 &rot3x3)
606{
607 // Algorithm from:
608 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55
609
610 float scalar;
611 float axis[3];
612
613 const float trace = rot3x3(0, 0) + rot3x3(1, 1) + rot3x3(2, 2);
614 if (trace > 0.00000001f) {
615 const float s = 2.0f * std::sqrt(x: trace + 1.0f);
616 scalar = 0.25f * s;
617 axis[0] = (rot3x3(2, 1) - rot3x3(1, 2)) / s;
618 axis[1] = (rot3x3(0, 2) - rot3x3(2, 0)) / s;
619 axis[2] = (rot3x3(1, 0) - rot3x3(0, 1)) / s;
620 } else {
621 constexpr int s_next[3] = { 1, 2, 0 };
622 int i = 0;
623 if (rot3x3(1, 1) > rot3x3(0, 0))
624 i = 1;
625 if (rot3x3(2, 2) > rot3x3(i, i))
626 i = 2;
627 int j = s_next[i];
628 int k = s_next[j];
629
630 const float s = 2.0f * std::sqrt(x: rot3x3(i, i) - rot3x3(j, j) - rot3x3(k, k) + 1.0f);
631 axis[i] = 0.25f * s;
632 scalar = (rot3x3(k, j) - rot3x3(j, k)) / s;
633 axis[j] = (rot3x3(j, i) + rot3x3(i, j)) / s;
634 axis[k] = (rot3x3(k, i) + rot3x3(i, k)) / s;
635 }
636
637 return QQuaternion(scalar, axis[0], axis[1], axis[2]);
638}
639
640#ifndef QT_NO_VECTOR3D
641
642/*!
643 \since 5.5
644
645 Returns the 3 orthonormal axes (\a xAxis, \a yAxis, \a zAxis) defining the quaternion.
646
647 All of \a xAxis, \a yAxis, and \a zAxis must be valid, non-\nullptr pointers,
648 otherwise the behavior is undefined.
649
650 \sa fromAxes(), toRotationMatrix()
651*/
652void QQuaternion::getAxes(QVector3D *xAxis, QVector3D *yAxis, QVector3D *zAxis) const
653{
654 Q_ASSERT(xAxis && yAxis && zAxis);
655
656 const QMatrix3x3 rot3x3(toRotationMatrix());
657
658 *xAxis = QVector3D(rot3x3(0, 0), rot3x3(1, 0), rot3x3(2, 0));
659 *yAxis = QVector3D(rot3x3(0, 1), rot3x3(1, 1), rot3x3(2, 1));
660 *zAxis = QVector3D(rot3x3(0, 2), rot3x3(1, 2), rot3x3(2, 2));
661}
662
663/*!
664 \since 5.5
665
666 Constructs the quaternion using 3 axes (\a xAxis, \a yAxis, \a zAxis).
667
668 \note The axes are assumed to be orthonormal.
669
670 \sa getAxes(), fromRotationMatrix()
671*/
672QQuaternion QQuaternion::fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis)
673{
674 QMatrix3x3 rot3x3(Qt::Uninitialized);
675 rot3x3(0, 0) = xAxis.x();
676 rot3x3(1, 0) = xAxis.y();
677 rot3x3(2, 0) = xAxis.z();
678 rot3x3(0, 1) = yAxis.x();
679 rot3x3(1, 1) = yAxis.y();
680 rot3x3(2, 1) = yAxis.z();
681 rot3x3(0, 2) = zAxis.x();
682 rot3x3(1, 2) = zAxis.y();
683 rot3x3(2, 2) = zAxis.z();
684
685 return QQuaternion::fromRotationMatrix(rot3x3);
686}
687
688/*!
689 \since 5.5
690
691 Constructs the quaternion using specified forward direction \a direction
692 and upward direction \a up.
693 If the upward direction was not specified or the forward and upward
694 vectors are collinear, a new orthonormal upward direction will be generated.
695
696 \sa fromAxes(), rotationTo()
697*/
698QQuaternion QQuaternion::fromDirection(const QVector3D &direction, const QVector3D &up)
699{
700 if (qFuzzyIsNull(f: direction.x()) && qFuzzyIsNull(f: direction.y()) && qFuzzyIsNull(f: direction.z()))
701 return QQuaternion();
702
703 const QVector3D zAxis(direction.normalized());
704 QVector3D xAxis(QVector3D::crossProduct(v1: up, v2: zAxis));
705 if (qFuzzyIsNull(f: xAxis.lengthSquared())) {
706 // collinear or invalid up vector; derive shortest arc to new direction
707 return QQuaternion::rotationTo(from: QVector3D(0.0f, 0.0f, 1.0f), to: zAxis);
708 }
709
710 xAxis.normalize();
711 const QVector3D yAxis(QVector3D::crossProduct(v1: zAxis, v2: xAxis));
712
713 return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
714}
715
716/*!
717 \since 5.5
718
719 Returns the shortest arc quaternion to rotate from the direction described by the vector \a from
720 to the direction described by the vector \a to.
721
722 \sa fromDirection()
723*/
724QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
725{
726 // Based on Stan Melax's article in Game Programming Gems
727
728 const QVector3D v0(from.normalized());
729 const QVector3D v1(to.normalized());
730
731 float d = QVector3D::dotProduct(v1: v0, v2: v1) + 1.0f;
732
733 // if dest vector is close to the inverse of source vector, ANY axis of rotation is valid
734 if (qFuzzyIsNull(f: d)) {
735 QVector3D axis = QVector3D::crossProduct(v1: QVector3D(1.0f, 0.0f, 0.0f), v2: v0);
736 if (qFuzzyIsNull(f: axis.lengthSquared()))
737 axis = QVector3D::crossProduct(v1: QVector3D(0.0f, 1.0f, 0.0f), v2: v0);
738 axis.normalize();
739
740 // same as QQuaternion::fromAxisAndAngle(axis, 180.0f)
741 return QQuaternion(0.0f, axis.x(), axis.y(), axis.z());
742 }
743
744 d = std::sqrt(x: 2.0f * d);
745 const QVector3D axis(QVector3D::crossProduct(v1: v0, v2: v1) / d);
746
747 return QQuaternion(d * 0.5f, axis).normalized();
748}
749
750#endif // QT_NO_VECTOR3D
751
752/*!
753 \fn bool QQuaternion::operator==(const QQuaternion &q1, const QQuaternion &q2) noexcept
754
755 Returns \c true if \a q1 is equal to \a q2; otherwise returns \c false.
756 This operator uses an exact floating-point comparison.
757*/
758
759/*!
760 \fn bool QQuaternion::operator!=(const QQuaternion &q1, const QQuaternion &q2) noexcept
761
762 Returns \c true if \a q1 is not equal to \a q2; otherwise returns \c false.
763 This operator uses an exact floating-point comparison.
764*/
765
766/*!
767 \fn const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) noexcept
768 \relates QQuaternion
769
770 Returns a QQuaternion object that is the sum of the given quaternions,
771 \a q1 and \a q2; each component is added separately.
772
773 \sa QQuaternion::operator+=()
774*/
775
776/*!
777 \fn const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) noexcept
778 \relates QQuaternion
779
780 Returns a QQuaternion object that is formed by subtracting
781 \a q2 from \a q1; each component is subtracted separately.
782
783 \sa QQuaternion::operator-=()
784*/
785
786/*!
787 \fn const QQuaternion operator*(float factor, const QQuaternion &quaternion) noexcept
788 \relates QQuaternion
789
790 Returns a copy of the given \a quaternion, multiplied by the
791 given \a factor.
792
793 \sa QQuaternion::operator*=()
794*/
795
796/*!
797 \fn const QQuaternion operator*(const QQuaternion &quaternion, float factor) noexcept
798 \relates QQuaternion
799
800 Returns a copy of the given \a quaternion, multiplied by the
801 given \a factor.
802
803 \sa QQuaternion::operator*=()
804*/
805
806/*!
807 \fn const QQuaternion operator*(const QQuaternion &q1, const QQuaternion &q2) noexcept
808 \relates QQuaternion
809
810 Multiplies \a q1 and \a q2 using quaternion multiplication.
811 The result corresponds to applying both of the rotations specified
812 by \a q1 and \a q2.
813
814 \sa QQuaternion::operator*=()
815*/
816
817/*!
818 \fn const QQuaternion operator-(const QQuaternion &quaternion) noexcept
819 \relates QQuaternion
820 \overload
821
822 Returns a QQuaternion object that is formed by changing the sign of
823 all three components of the given \a quaternion.
824
825 Equivalent to \c {QQuaternion(0,0,0,0) - quaternion}.
826*/
827
828/*!
829 \fn const QQuaternion operator/(const QQuaternion &quaternion, float divisor)
830 \relates QQuaternion
831
832 Returns the QQuaternion object formed by dividing all components of
833 the given \a quaternion by the given \a divisor.
834
835 \sa QQuaternion::operator/=()
836*/
837
838#ifndef QT_NO_VECTOR3D
839
840/*!
841 \fn QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec) noexcept
842 \since 5.5
843 \relates QQuaternion
844
845 Rotates a vector \a vec with a quaternion \a quaternion to produce a new vector in 3D space.
846*/
847
848#endif
849
850/*!
851 \fn bool qFuzzyCompare(const QQuaternion &q1, const QQuaternion &q2) noexcept
852 \relates QQuaternion
853
854 Returns \c true if \a q1 and \a q2 are equal, allowing for a small
855 fuzziness factor for floating-point comparisons; false otherwise.
856*/
857
858/*!
859 Interpolates along the shortest spherical path between the
860 rotational positions \a q1 and \a q2. The value \a t should
861 be between 0 and 1, indicating the spherical distance to travel
862 between \a q1 and \a q2.
863
864 If \a t is less than or equal to 0, then \a q1 will be returned.
865 If \a t is greater than or equal to 1, then \a q2 will be returned.
866
867 \sa nlerp()
868*/
869QQuaternion QQuaternion::slerp
870 (const QQuaternion &q1, const QQuaternion &q2, float t)
871{
872 // Handle the easy cases first.
873 if (t <= 0.0f)
874 return q1;
875 else if (t >= 1.0f)
876 return q2;
877
878 // Determine the angle between the two quaternions.
879 QQuaternion q2b(q2);
880 float dot = QQuaternion::dotProduct(q1, q2);
881 if (dot < 0.0f) {
882 q2b = -q2b;
883 dot = -dot;
884 }
885
886 // Get the scale factors. If they are too small,
887 // then revert to simple linear interpolation.
888 float factor1 = 1.0f - t;
889 float factor2 = t;
890 if ((1.0f - dot) > 0.0000001) {
891 float angle = std::acos(x: dot);
892 float sinOfAngle = std::sin(x: angle);
893 if (sinOfAngle > 0.0000001) {
894 factor1 = std::sin(x: (1.0f - t) * angle) / sinOfAngle;
895 factor2 = std::sin(x: t * angle) / sinOfAngle;
896 }
897 }
898
899 // Construct the result quaternion.
900 return q1 * factor1 + q2b * factor2;
901}
902
903/*!
904 Interpolates along the shortest linear path between the rotational
905 positions \a q1 and \a q2. The value \a t should be between 0 and 1,
906 indicating the distance to travel between \a q1 and \a q2.
907 The result will be normalized().
908
909 If \a t is less than or equal to 0, then \a q1 will be returned.
910 If \a t is greater than or equal to 1, then \a q2 will be returned.
911
912 The nlerp() function is typically faster than slerp() and will
913 give approximate results to spherical interpolation that are
914 good enough for some applications.
915
916 \sa slerp()
917*/
918QQuaternion QQuaternion::nlerp
919 (const QQuaternion &q1, const QQuaternion &q2, float t)
920{
921 // Handle the easy cases first.
922 if (t <= 0.0f)
923 return q1;
924 else if (t >= 1.0f)
925 return q2;
926
927 // Determine the angle between the two quaternions.
928 QQuaternion q2b(q2);
929 float dot = QQuaternion::dotProduct(q1, q2);
930 if (dot < 0.0f)
931 q2b = -q2b;
932
933 // Perform the linear interpolation.
934 return (q1 * (1.0f - t) + q2b * t).normalized();
935}
936
937/*!
938 Returns the quaternion as a QVariant.
939*/
940QQuaternion::operator QVariant() const
941{
942 return QVariant::fromValue(value: *this);
943}
944
945#ifndef QT_NO_DEBUG_STREAM
946
947QDebug operator<<(QDebug dbg, const QQuaternion &q)
948{
949 QDebugStateSaver saver(dbg);
950 dbg.nospace() << "QQuaternion(scalar:" << q.scalar()
951 << ", vector:(" << q.x() << ", "
952 << q.y() << ", " << q.z() << "))";
953 return dbg;
954}
955
956#endif
957
958#ifndef QT_NO_DATASTREAM
959
960/*!
961 \fn QDataStream &operator<<(QDataStream &stream, const QQuaternion &quaternion)
962 \relates QQuaternion
963
964 Writes the given \a quaternion to the given \a stream and returns a
965 reference to the stream.
966
967 \sa {Serializing Qt Data Types}
968*/
969
970QDataStream &operator<<(QDataStream &stream, const QQuaternion &quaternion)
971{
972 stream << quaternion.scalar() << quaternion.x()
973 << quaternion.y() << quaternion.z();
974 return stream;
975}
976
977/*!
978 \fn QDataStream &operator>>(QDataStream &stream, QQuaternion &quaternion)
979 \relates QQuaternion
980
981 Reads a quaternion from the given \a stream into the given \a quaternion
982 and returns a reference to the stream.
983
984 \sa {Serializing Qt Data Types}
985*/
986
987QDataStream &operator>>(QDataStream &stream, QQuaternion &quaternion)
988{
989 float scalar, x, y, z;
990 stream >> scalar;
991 stream >> x;
992 stream >> y;
993 stream >> z;
994 quaternion.setScalar(scalar);
995 quaternion.setX(x);
996 quaternion.setY(y);
997 quaternion.setZ(z);
998 return stream;
999}
1000
1001#endif // QT_NO_DATASTREAM
1002
1003#endif
1004
1005QT_END_NAMESPACE
1006

source code of qtbase/src/gui/math3d/qquaternion.cpp