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