Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquaternion.cpp
Go to the documentation of this file.
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(xp, yp, zp, 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(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(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(angle / 2.0f);
358 float s = std::sin(a);
359 float c = std::cos(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(xp, yp, zp);
385 if (!qFuzzyIsNull(length)) {
386 if (qFuzzyCompare(length, 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(2.0f * std::atan2(length, 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 (!qFuzzyIsNull(length) && !qFuzzyCompare(length, 1.0f)) {
413 x /= length;
414 y /= length;
415 z /= length;
416 }
417 float a = qDegreesToRadians(angle / 2.0f);
418 float s = std::sin(a);
419 float c = std::cos(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 \fn void QQuaternion::getEulerAngles(float *pitch, float *yaw, float *roll) const
452 \since 5.5
453
454 \obsolete
455
456 Use eulerAngles() instead.
457
458 Calculates \a roll, \a pitch, and \a yaw Euler angles (in degrees)
459 that corresponds to this quaternion.
460
461 All of \a pitch, \a yaw, and \a roll must be valid, non-\nullptr pointers,
462 otherwise the behavior is undefined.
463
464 \sa eulerAngles(), fromEulerAngles()
465*/
466
467/*!
468 \since 6.11
469 \class QQuaternion::EulerAngles
470 \ingroup painting-3D
471 \inmodule QtGui
472
473 EulerAngles<T> is a struct template where \a T specifies the floating-point
474 type used for the angle values (typically \c float).
475
476 A struct containing three fields \l{pitch}, \l{yaw}, and \l{roll},
477 representing the three Euler angles that define a
478 \l{QQuaternion}{quaternion}.
479
480 Consult the documentation of functions taking or returning an EulerAngles
481 object for the order in which the rotations are applied.
482
483 \sa QQuaternion::eulerAngles(), QQuaternion::fromEulerAngles(QQuaternion::EulerAngles<float>)
484*/
485
486/*!
487 \variable QQuaternion::EulerAngles::pitch
488
489 The pitch represents the rotation around the x-axis.
490*/
491
492/*!
493 \variable QQuaternion::EulerAngles::yaw
494
495 The yaw represents the rotation around the y-axis.
496*/
497
498/*!
499 \variable QQuaternion::EulerAngles::roll
500
501 The roll represents the rotation around the z-axis.
502*/
503
504/*!
505 \since 6.11
506
507 Returns the Euler angles (in degrees) that correspond to this quaternion.
508
509 \sa fromEulerAngles()
510*/
511auto QQuaternion::eulerAngles() const -> EulerAngles<float>
512{
513 EulerAngles<float> result;
514
515 // to avoid churn
516 auto pitch = &result.pitch;
517 auto yaw = &result.yaw;
518 auto roll = &result.roll;
519
520 // Algorithm adapted from:
521 // https://ingmec.ual.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf
522 // "A tutorial on SE(3) transformation parameterizations and on-manifold optimization".
523
524 // We can only detect Gimbal lock when we normalize, which we can't do when
525 // length is nearly zero. Do so before multiplying coordinates, to avoid
526 // underflow.
527 const float len = length();
528 const bool rescale = !qFuzzyIsNull(len);
529 const float xps = rescale ? xp / len : xp;
530 const float yps = rescale ? yp / len : yp;
531 const float zps = rescale ? zp / len : zp;
532 const float wps = rescale ? wp / len : wp;
533
534 const float xx = xps * xps;
535 const float xy = xps * yps;
536 const float xz = xps * zps;
537 const float xw = xps * wps;
538 const float yy = yps * yps;
539 const float yz = yps * zps;
540 const float yw = yps * wps;
541 const float zz = zps * zps;
542 const float zw = zps * wps;
543
544 // For the common case, we have a hidden division by cos(pitch) to calculate
545 // yaw and roll: atan2(a / cos(pitch), b / cos(pitch)) = atan2(a, b). This equation
546 // wouldn't work if cos(pitch) is close to zero (i.e. abs(sin(pitch)) =~ 1.0).
547 // This threshold is copied from qFuzzyIsNull() to avoid the hidden division by zero.
548 constexpr float epsilon = 0.00001f;
549
550 const float sinp = -2.0f * (yz - xw);
551 if (std::abs(sinp) < 1.0f - epsilon) {
552 *pitch = std::asin(sinp);
553 *yaw = std::atan2(2.0f * (xz + yw), 1.0f - 2.0f * (xx + yy));
554 *roll = std::atan2(2.0f * (xy + zw), 1.0f - 2.0f * (xx + zz));
555 } else {
556 // Gimbal lock case, which doesn't have a unique solution. We just use
557 // XY rotation.
558 *pitch = std::copysign(static_cast<float>(M_PI_2), sinp);
559 *yaw = 2.0f * std::atan2(yps, wps);
560 *roll = 0.0f;
561 }
562
563 *pitch = qRadiansToDegrees(*pitch);
564 *yaw = qRadiansToDegrees(*yaw);
565 *roll = qRadiansToDegrees(*roll);
566
567 return result;
568}
569
570/*!
571 \since 5.5
572
573 Creates a quaternion that corresponds to a rotation of
574 \a roll degrees around the z axis, \a pitch degrees around the x axis,
575 and \a yaw degrees around the y axis (in that order).
576
577 \sa eulerAngles(), toEulerAngles(), fromEulerAngles(QQuaternion::EulerAngles<float>)
578*/
579QQuaternion QQuaternion::fromEulerAngles(float pitch, float yaw, float roll)
580{
581 // Algorithm from:
582 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60
583
584 pitch = qDegreesToRadians(pitch);
585 yaw = qDegreesToRadians(yaw);
586 roll = qDegreesToRadians(roll);
587
588 pitch *= 0.5f;
589 yaw *= 0.5f;
590 roll *= 0.5f;
591
592 const float c1 = std::cos(yaw);
593 const float s1 = std::sin(yaw);
594 const float c2 = std::cos(roll);
595 const float s2 = std::sin(roll);
596 const float c3 = std::cos(pitch);
597 const float s3 = std::sin(pitch);
598 const float c1c2 = c1 * c2;
599 const float s1s2 = s1 * s2;
600
601 const float w = c1c2 * c3 + s1s2 * s3;
602 const float x = c1c2 * s3 + s1s2 * c3;
603 const float y = s1 * c2 * c3 - c1 * s2 * s3;
604 const float z = c1 * s2 * c3 - s1 * c2 * s3;
605
606 return QQuaternion(w, x, y, z);
607}
608
609/*!
610 \fn QQuaternion QQuaternion::fromEulerAngles(EulerAngles<float> angles)
611 \since 6.11
612 \overload
613
614 Equivalent to
615 \code
616 fromEulerAngles(angles.pitch, angles.yaw, angles.roll);
617 \endcode
618
619 \sa eulerAngles(), toEulerAngles(), fromEulerAngles()
620*/
621
622/*!
623 \since 5.5
624
625 Creates a rotation matrix that corresponds to this quaternion.
626
627 \note If this quaternion is not normalized,
628 the resulting rotation matrix will contain scaling information.
629
630 \sa fromRotationMatrix(), toAxes()
631*/
632QMatrix3x3 QQuaternion::toRotationMatrix() const
633{
634 // Algorithm from:
635 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54
636
637 QMatrix3x3 rot3x3(Qt::Uninitialized);
638
639 const float f2x = xp + xp;
640 const float f2y = yp + yp;
641 const float f2z = zp + zp;
642 const float f2xw = f2x * wp;
643 const float f2yw = f2y * wp;
644 const float f2zw = f2z * wp;
645 const float f2xx = f2x * xp;
646 const float f2xy = f2x * yp;
647 const float f2xz = f2x * zp;
648 const float f2yy = f2y * yp;
649 const float f2yz = f2y * zp;
650 const float f2zz = f2z * zp;
651
652 rot3x3(0, 0) = 1.0f - (f2yy + f2zz);
653 rot3x3(0, 1) = f2xy - f2zw;
654 rot3x3(0, 2) = f2xz + f2yw;
655 rot3x3(1, 0) = f2xy + f2zw;
656 rot3x3(1, 1) = 1.0f - (f2xx + f2zz);
657 rot3x3(1, 2) = f2yz - f2xw;
658 rot3x3(2, 0) = f2xz - f2yw;
659 rot3x3(2, 1) = f2yz + f2xw;
660 rot3x3(2, 2) = 1.0f - (f2xx + f2yy);
661
662 return rot3x3;
663}
664
665/*!
666 \since 5.5
667
668 Creates a quaternion that corresponds to the rotation matrix \a rot3x3.
669
670 \note If the given rotation matrix is not normalized,
671 the resulting quaternion will contain scaling information.
672
673 \sa toRotationMatrix(), fromAxes()
674*/
675QQuaternion QQuaternion::fromRotationMatrix(const QMatrix3x3 &rot3x3)
676{
677 // Algorithm from:
678 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55
679
680 float scalar;
681 float axis[3];
682
683 const float trace = rot3x3(0, 0) + rot3x3(1, 1) + rot3x3(2, 2);
684 if (trace > 0.00000001f) {
685 const float s = 2.0f * std::sqrt(trace + 1.0f);
686 scalar = 0.25f * s;
687 axis[0] = (rot3x3(2, 1) - rot3x3(1, 2)) / s;
688 axis[1] = (rot3x3(0, 2) - rot3x3(2, 0)) / s;
689 axis[2] = (rot3x3(1, 0) - rot3x3(0, 1)) / s;
690 } else {
691 constexpr int s_next[3] = { 1, 2, 0 };
692 int i = 0;
693 if (rot3x3(1, 1) > rot3x3(0, 0))
694 i = 1;
695 if (rot3x3(2, 2) > rot3x3(i, i))
696 i = 2;
697 int j = s_next[i];
698 int k = s_next[j];
699
700 const float s = 2.0f * std::sqrt(rot3x3(i, i) - rot3x3(j, j) - rot3x3(k, k) + 1.0f);
701 axis[i] = 0.25f * s;
702 scalar = (rot3x3(k, j) - rot3x3(j, k)) / s;
703 axis[j] = (rot3x3(j, i) + rot3x3(i, j)) / s;
704 axis[k] = (rot3x3(k, i) + rot3x3(i, k)) / s;
705 }
706
707 return QQuaternion(scalar, axis[0], axis[1], axis[2]);
708}
709
710#ifndef QT_NO_VECTOR3D
711
712/*!
713 \since 6.11
714 \class QQuaternion::Axes
715 \ingroup painting-3D
716 \inmodule QtGui
717
718 A struct containing the three orthonormal axes that define a
719 \l{QQuaternion}{quaternion}.
720
721
722 \sa QQuaternion::toAxes(), QQuaternion::fromAxes(QQuaternion::Axes)
723*/
724
725/*!
726 \variable QQuaternion::Axes::x
727
728 The x orthonormal axis that, together with \l{y} and \l{z}, defines a
729 quaternion.
730*/
731
732/*!
733 \variable QQuaternion::Axes::y
734
735 The y orthonormal axis that, together with \l{x} and \l{z}, defines a
736 quaternion.
737*/
738
739/*!
740 \variable QQuaternion::Axes::z
741
742 The z orthonormal axis that, together with \l{x} and \l{y}, defines a
743 quaternion.
744*/
745
746/*!
747 \since 6.11
748
749 Returns the three orthonormal axes that define this quaternion.
750
751 \sa QQuaternion::Axes, fromAxes(QQuaternion::Axes), toRotationMatrix()
752*/
753auto QQuaternion::toAxes() const -> Axes
754{
755 const QMatrix3x3 rot3x3(toRotationMatrix());
756
757 return { {rot3x3(0, 0), rot3x3(1, 0), rot3x3(2, 0)},
758 {rot3x3(0, 1), rot3x3(1, 1), rot3x3(2, 1)},
759 {rot3x3(0, 2), rot3x3(1, 2), rot3x3(2, 2)} };
760}
761
762
763/*!
764 \fn void QQuaternion::getAxes(QVector3D *xAxis, QVector3D *yAxis, QVector3D *zAxis) const
765 \since 5.5
766
767 \obsolete
768 Use toAxes() instead.
769
770 Returns the 3 orthonormal axes (\a xAxis, \a yAxis, \a zAxis) defining the quaternion.
771
772 All of \a xAxis, \a yAxis, and \a zAxis must be valid, non-\nullptr pointers,
773 otherwise the behavior is undefined.
774
775 \sa fromAxes(), toRotationMatrix()
776*/
777
778/*!
779 \since 5.5
780
781 Constructs the quaternion using 3 axes (\a xAxis, \a yAxis, \a zAxis).
782
783 \note The axes are assumed to be orthonormal.
784
785 \sa toAxes(), fromRotationMatrix()
786*/
787QQuaternion QQuaternion::fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis)
788{
789 QMatrix3x3 rot3x3(Qt::Uninitialized);
790 rot3x3(0, 0) = xAxis.x();
791 rot3x3(1, 0) = xAxis.y();
792 rot3x3(2, 0) = xAxis.z();
793 rot3x3(0, 1) = yAxis.x();
794 rot3x3(1, 1) = yAxis.y();
795 rot3x3(2, 1) = yAxis.z();
796 rot3x3(0, 2) = zAxis.x();
797 rot3x3(1, 2) = zAxis.y();
798 rot3x3(2, 2) = zAxis.z();
799
800 return QQuaternion::fromRotationMatrix(rot3x3);
801}
802
803/*!
804 \since 6.11
805 \overload
806
807 \sa toAxes(), fromRotationMatrix()
808*/
809QQuaternion QQuaternion::fromAxes(Axes axes) // clazy:exclude=function-args-by-ref
810{
811 return fromAxes(axes.x, axes.y, axes.z);
812}
813
814/*!
815 \since 5.5
816
817 Constructs the quaternion using specified forward direction \a direction
818 and upward direction \a up.
819 If the upward direction was not specified or the forward and upward
820 vectors are collinear, a new orthonormal upward direction will be generated.
821
822 \sa fromAxes(), rotationTo()
823*/
824QQuaternion QQuaternion::fromDirection(const QVector3D &direction, const QVector3D &up)
825{
826 if (qFuzzyIsNull(direction.x()) && qFuzzyIsNull(direction.y()) && qFuzzyIsNull(direction.z()))
827 return QQuaternion();
828
829 const QVector3D zAxis(direction.normalized());
830 QVector3D xAxis(QVector3D::crossProduct(up, zAxis));
831 if (qFuzzyIsNull(xAxis.lengthSquared())) {
832 // collinear or invalid up vector; derive shortest arc to new direction
833 return QQuaternion::rotationTo(QVector3D(0.0f, 0.0f, 1.0f), zAxis);
834 }
835
836 xAxis.normalize();
837 const QVector3D yAxis(QVector3D::crossProduct(zAxis, xAxis));
838
839 return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
840}
841
842/*!
843 \since 5.5
844
845 Returns the shortest arc quaternion to rotate from the direction described by the vector \a from
846 to the direction described by the vector \a to.
847
848 \sa fromDirection()
849*/
850QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
851{
852 // Based on Stan Melax's article in Game Programming Gems
853
854 const QVector3D v0(from.normalized());
855 const QVector3D v1(to.normalized());
856
857 float d = QVector3D::dotProduct(v0, v1) + 1.0f;
858
859 // if dest vector is close to the inverse of source vector, ANY axis of rotation is valid
860 if (qFuzzyIsNull(d)) {
861 QVector3D axis = QVector3D::crossProduct(QVector3D(1.0f, 0.0f, 0.0f), v0);
862 if (qFuzzyIsNull(axis.lengthSquared()))
863 axis = QVector3D::crossProduct(QVector3D(0.0f, 1.0f, 0.0f), v0);
864 axis.normalize();
865
866 // same as QQuaternion::fromAxisAndAngle(axis, 180.0f)
867 return QQuaternion(0.0f, axis.x(), axis.y(), axis.z());
868 }
869
870 d = std::sqrt(2.0f * d);
871 const QVector3D axis(QVector3D::crossProduct(v0, v1) / d);
872
873 return QQuaternion(d * 0.5f, axis).normalized();
874}
875
876#endif // QT_NO_VECTOR3D
877
878/*!
879 \fn bool QQuaternion::operator==(const QQuaternion &q1, const QQuaternion &q2) noexcept
880
881 Returns \c true if \a q1 is equal to \a q2; otherwise returns \c false.
882 This operator uses an exact floating-point comparison.
883*/
884
885/*!
886 \fn bool QQuaternion::operator!=(const QQuaternion &q1, const QQuaternion &q2) noexcept
887
888 Returns \c true if \a q1 is not equal to \a q2; otherwise returns \c false.
889 This operator uses an exact floating-point comparison.
890*/
891
892/*!
893 \fn const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) noexcept
894 \relates QQuaternion
895
896 Returns a QQuaternion object that is the sum of the given quaternions,
897 \a q1 and \a q2; each component is added separately.
898
899 \sa QQuaternion::operator+=()
900*/
901
902/*!
903 \fn const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) noexcept
904 \relates QQuaternion
905
906 Returns a QQuaternion object that is formed by subtracting
907 \a q2 from \a q1; each component is subtracted separately.
908
909 \sa QQuaternion::operator-=()
910*/
911
912/*!
913 \fn const QQuaternion operator*(float factor, const QQuaternion &quaternion) noexcept
914 \relates QQuaternion
915
916 Returns a copy of the given \a quaternion, multiplied by the
917 given \a factor.
918
919 \sa QQuaternion::operator*=()
920*/
921
922/*!
923 \fn const QQuaternion operator*(const QQuaternion &quaternion, float factor) noexcept
924 \relates QQuaternion
925
926 Returns a copy of the given \a quaternion, multiplied by the
927 given \a factor.
928
929 \sa QQuaternion::operator*=()
930*/
931
932/*!
933 \fn const QQuaternion operator*(const QQuaternion &q1, const QQuaternion &q2) noexcept
934 \relates QQuaternion
935
936 Multiplies \a q1 and \a q2 using quaternion multiplication.
937 The result corresponds to applying both of the rotations specified
938 by \a q1 and \a q2.
939
940 \sa QQuaternion::operator*=()
941*/
942
943/*!
944 \fn const QQuaternion operator-(const QQuaternion &quaternion) noexcept
945 \relates QQuaternion
946 \overload
947
948 Returns a QQuaternion object that is formed by changing the sign of
949 all three components of the given \a quaternion.
950
951 Equivalent to \c {QQuaternion(0,0,0,0) - quaternion}.
952*/
953
954/*!
955 \fn const QQuaternion operator/(const QQuaternion &quaternion, float divisor)
956 \relates QQuaternion
957
958 Returns the QQuaternion object formed by dividing all components of
959 the given \a quaternion by the given \a divisor.
960
961 \sa QQuaternion::operator/=()
962*/
963
964#ifndef QT_NO_VECTOR3D
965
966/*!
967 \fn QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec) noexcept
968 \since 5.5
969 \relates QQuaternion
970
971 Rotates a vector \a vec with a quaternion \a quaternion to produce a new vector in 3D space.
972*/
973
974#endif
975
976/*!
977 \fn bool qFuzzyCompare(const QQuaternion &q1, const QQuaternion &q2) noexcept
978 \relates QQuaternion
979
980 Returns \c true if \a q1 and \a q2 are equal, allowing for a small
981 fuzziness factor for floating-point comparisons; false otherwise.
982*/
983
984/*!
985 Interpolates along the shortest spherical path between the
986 rotational positions \a q1 and \a q2. The value \a t should
987 be between 0 and 1, indicating the spherical distance to travel
988 between \a q1 and \a q2.
989
990 If \a t is less than or equal to 0, then \a q1 will be returned.
991 If \a t is greater than or equal to 1, then \a q2 will be returned.
992
993 \sa nlerp()
994*/
995QQuaternion QQuaternion::slerp
996 (const QQuaternion &q1, const QQuaternion &q2, float t)
997{
998 // Handle the easy cases first.
999 if (t <= 0.0f)
1000 return q1;
1001 else if (t >= 1.0f)
1002 return q2;
1003
1004 // Determine the angle between the two quaternions.
1005 QQuaternion q2b(q2);
1006 float dot = QQuaternion::dotProduct(q1, q2);
1007 if (dot < 0.0f) {
1008 q2b = -q2b;
1009 dot = -dot;
1010 }
1011
1012 // Get the scale factors. If they are too small,
1013 // then revert to simple linear interpolation.
1014 float factor1 = 1.0f - t;
1015 float factor2 = t;
1016 if ((1.0f - dot) > 0.0000001) {
1017 float angle = std::acos(dot);
1018 float sinOfAngle = std::sin(angle);
1019 if (sinOfAngle > 0.0000001) {
1020 factor1 = std::sin((1.0f - t) * angle) / sinOfAngle;
1021 factor2 = std::sin(t * angle) / sinOfAngle;
1022 }
1023 }
1024
1025 // Construct the result quaternion.
1026 return q1 * factor1 + q2b * factor2;
1027}
1028
1029/*!
1030 Interpolates along the shortest linear path between the rotational
1031 positions \a q1 and \a q2. The value \a t should be between 0 and 1,
1032 indicating the distance to travel between \a q1 and \a q2.
1033 The result will be normalized().
1034
1035 If \a t is less than or equal to 0, then \a q1 will be returned.
1036 If \a t is greater than or equal to 1, then \a q2 will be returned.
1037
1038 The nlerp() function is typically faster than slerp() and will
1039 give approximate results to spherical interpolation that are
1040 good enough for some applications.
1041
1042 \sa slerp()
1043*/
1044QQuaternion QQuaternion::nlerp
1045 (const QQuaternion &q1, const QQuaternion &q2, float t)
1046{
1047 // Handle the easy cases first.
1048 if (t <= 0.0f)
1049 return q1;
1050 else if (t >= 1.0f)
1051 return q2;
1052
1053 // Determine the angle between the two quaternions.
1054 QQuaternion q2b(q2);
1055 float dot = QQuaternion::dotProduct(q1, q2);
1056 if (dot < 0.0f)
1057 q2b = -q2b;
1058
1059 // Perform the linear interpolation.
1060 return (q1 * (1.0f - t) + q2b * t).normalized();
1061}
1062
1063/*!
1064 Returns the quaternion as a QVariant.
1065*/
1066QQuaternion::operator QVariant() const
1067{
1068 return QVariant::fromValue(*this);
1069}
1070
1071#ifndef QT_NO_DEBUG_STREAM
1072
1073QDebug operator<<(QDebug dbg, const QQuaternion &q)
1074{
1075 QDebugStateSaver saver(dbg);
1076 dbg.nospace() << "QQuaternion(scalar:" << q.scalar()
1077 << ", vector:(" << q.x() << ", "
1078 << q.y() << ", " << q.z() << "))";
1079 return dbg;
1080}
1081
1082#endif
1083
1084#ifndef QT_NO_DATASTREAM
1085
1086/*!
1087 \fn QDataStream &operator<<(QDataStream &stream, const QQuaternion &quaternion)
1088 \relates QQuaternion
1089
1090 Writes the given \a quaternion to the given \a stream and returns a
1091 reference to the stream.
1092
1093 \sa {Serializing Qt Data Types}
1094*/
1095
1096QDataStream &operator<<(QDataStream &stream, const QQuaternion &quaternion)
1097{
1098 stream << quaternion.scalar() << quaternion.x()
1099 << quaternion.y() << quaternion.z();
1100 return stream;
1101}
1102
1103/*!
1104 \fn QDataStream &operator>>(QDataStream &stream, QQuaternion &quaternion)
1105 \relates QQuaternion
1106
1107 Reads a quaternion from the given \a stream into the given \a quaternion
1108 and returns a reference to the stream.
1109
1110 \sa {Serializing Qt Data Types}
1111*/
1112
1113QDataStream &operator>>(QDataStream &stream, QQuaternion &quaternion)
1114{
1115 float scalar, x, y, z;
1116 stream >> scalar;
1117 stream >> x;
1118 stream >> y;
1119 stream >> z;
1120 quaternion.setScalar(scalar);
1121 quaternion.setX(x);
1122 quaternion.setY(y);
1123 quaternion.setZ(z);
1124 return stream;
1125}
1126
1127#endif // QT_NO_DATASTREAM
1128
1129#endif
1130
1131QT_END_NAMESPACE
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
#define M_PI_2
Definition qmath.h:204
QDataStream & operator<<(QDataStream &stream, const QImage &image)
[0]
Definition qimage.cpp:4009
QDataStream & operator>>(QDataStream &stream, QImage &image)
Definition qimage.cpp:4035