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