Qt 6.x
The Qt SDK
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
13
14#ifndef QT_NO_QUATERNION
15
50#ifndef QT_NO_VECTOR3D
51
77#endif
78
87#ifndef QT_NO_VECTOR4D
88
101#endif
102
200{
201 return qHypot(xp, yp, zp, wp);
202}
203
213{
214 return xp * xp + yp * yp + zp * zp + wp * wp;
215}
216
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
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
282{
283 return (*this * QQuaternion(0, vector) * conjugated()).vector();
284}
285
329#ifndef QT_NO_VECTOR3D
330
349{
350 // Algorithm from:
351 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56
352 // We normalize the result just in case the values are close
353 // to zero, as suggested in the above FAQ.
354 float a = qDegreesToRadians(angle / 2.0f);
355 float s = std::sin(a);
356 float c = std::cos(a);
357 QVector3D ax = axis.normalized();
358 return QQuaternion(c, ax.x() * s, ax.y() * s, ax.z() * s).normalized();
359}
360
361#endif
362
371void QQuaternion::getAxisAndAngle(float *x, float *y, float *z, float *angle) const
372{
373 Q_ASSERT(x && y && z && angle);
374
375 // The quaternion representing the rotation is
376 // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)
377
378 const float length = qHypot(xp, yp, zp);
379 if (!qFuzzyIsNull(length)) {
380 if (qFuzzyCompare(length, 1.0f)) {
381 *x = xp;
382 *y = yp;
383 *z = zp;
384 } else {
385 *x = xp / length;
386 *y = yp / length;
387 *z = zp / length;
388 }
389 *angle = qRadiansToDegrees(2.0f * std::acos(wp));
390 } else {
391 // angle is 0 (mod 2*pi), so any axis will fit
392 *x = *y = *z = *angle = 0.0f;
393 }
394}
395
403 (float x, float y, float z, float angle)
404{
405 float length = qHypot(x, y, z);
406 if (!qFuzzyCompare(length, 1.0f) && !qFuzzyIsNull(length)) {
407 x /= length;
408 y /= length;
409 z /= length;
410 }
411 float a = qDegreesToRadians(angle / 2.0f);
412 float s = std::sin(a);
413 float c = std::cos(a);
414 return QQuaternion(c, x * s, y * s, z * s).normalized();
415}
416
417#ifndef QT_NO_VECTOR3D
418
442#endif // QT_NO_VECTOR3D
443
452void QQuaternion::getEulerAngles(float *pitch, float *yaw, float *roll) const
453{
454 Q_ASSERT(pitch && yaw && roll);
455
456 // Algorithm adapted from:
457 // https://ingmec.ual.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf
458 // "A tutorial on SE(3) transformation parameterizations and on-manifold optimization".
459
460 // We can only detect Gimbal lock when we normalize, which we can't do when
461 // length is nearly zero. Do so before multiplying coordinates, to avoid
462 // underflow.
463 const float len = length();
464 const bool rescale = !qFuzzyIsNull(len);
465 const float xps = rescale ? xp / len : xp;
466 const float yps = rescale ? yp / len : yp;
467 const float zps = rescale ? zp / len : zp;
468 const float wps = rescale ? wp / len : wp;
469
470 const float xx = xps * xps;
471 const float xy = xps * yps;
472 const float xz = xps * zps;
473 const float xw = xps * wps;
474 const float yy = yps * yps;
475 const float yz = yps * zps;
476 const float yw = yps * wps;
477 const float zz = zps * zps;
478 const float zw = zps * wps;
479
480 // For the common case, we have a hidden division by cos(pitch) to calculate
481 // yaw and roll: atan2(a / cos(pitch), b / cos(pitch)) = atan2(a, b). This equation
482 // wouldn't work if cos(pitch) is close to zero (i.e. abs(sin(pitch)) =~ 1.0).
483 // This threshold is copied from qFuzzyIsNull() to avoid the hidden division by zero.
484 constexpr float epsilon = 0.00001f;
485
486 const float sinp = -2.0f * (yz - xw);
487 if (std::abs(sinp) < 1.0f - epsilon) {
488 *pitch = std::asin(sinp);
489 *yaw = std::atan2(2.0f * (xz + yw), 1.0f - 2.0f * (xx + yy));
490 *roll = std::atan2(2.0f * (xy + zw), 1.0f - 2.0f * (xx + zz));
491 } else {
492 // Gimbal lock case, which doesn't have a unique solution. We just use
493 // XY rotation.
494 *pitch = std::copysign(static_cast<float>(M_PI_2), sinp);
495 *yaw = 2.0f * std::atan2(yps, wps);
496 *roll = 0.0f;
497 }
498
499 *pitch = qRadiansToDegrees(*pitch);
500 *yaw = qRadiansToDegrees(*yaw);
501 *roll = qRadiansToDegrees(*roll);
502}
503
513QQuaternion QQuaternion::fromEulerAngles(float pitch, float yaw, float roll)
514{
515 // Algorithm from:
516 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60
517
518 pitch = qDegreesToRadians(pitch);
519 yaw = qDegreesToRadians(yaw);
520 roll = qDegreesToRadians(roll);
521
522 pitch *= 0.5f;
523 yaw *= 0.5f;
524 roll *= 0.5f;
525
526 const float c1 = std::cos(yaw);
527 const float s1 = std::sin(yaw);
528 const float c2 = std::cos(roll);
529 const float s2 = std::sin(roll);
530 const float c3 = std::cos(pitch);
531 const float s3 = std::sin(pitch);
532 const float c1c2 = c1 * c2;
533 const float s1s2 = s1 * s2;
534
535 const float w = c1c2 * c3 + s1s2 * s3;
536 const float x = c1c2 * s3 + s1s2 * c3;
537 const float y = s1 * c2 * c3 - c1 * s2 * s3;
538 const float z = c1 * s2 * c3 - s1 * c2 * s3;
539
540 return QQuaternion(w, x, y, z);
541}
542
554{
555 // Algorithm from:
556 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54
557
559
560 const float f2x = xp + xp;
561 const float f2y = yp + yp;
562 const float f2z = zp + zp;
563 const float f2xw = f2x * wp;
564 const float f2yw = f2y * wp;
565 const float f2zw = f2z * wp;
566 const float f2xx = f2x * xp;
567 const float f2xy = f2x * yp;
568 const float f2xz = f2x * zp;
569 const float f2yy = f2y * yp;
570 const float f2yz = f2y * zp;
571 const float f2zz = f2z * zp;
572
573 rot3x3(0, 0) = 1.0f - (f2yy + f2zz);
574 rot3x3(0, 1) = f2xy - f2zw;
575 rot3x3(0, 2) = f2xz + f2yw;
576 rot3x3(1, 0) = f2xy + f2zw;
577 rot3x3(1, 1) = 1.0f - (f2xx + f2zz);
578 rot3x3(1, 2) = f2yz - f2xw;
579 rot3x3(2, 0) = f2xz - f2yw;
580 rot3x3(2, 1) = f2yz + f2xw;
581 rot3x3(2, 2) = 1.0f - (f2xx + f2yy);
582
583 return rot3x3;
584}
585
597{
598 // Algorithm from:
599 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55
600
601 float scalar;
602 float axis[3];
603
604 const float trace = rot3x3(0, 0) + rot3x3(1, 1) + rot3x3(2, 2);
605 if (trace > 0.00000001f) {
606 const float s = 2.0f * std::sqrt(trace + 1.0f);
607 scalar = 0.25f * s;
608 axis[0] = (rot3x3(2, 1) - rot3x3(1, 2)) / s;
609 axis[1] = (rot3x3(0, 2) - rot3x3(2, 0)) / s;
610 axis[2] = (rot3x3(1, 0) - rot3x3(0, 1)) / s;
611 } else {
612 static int s_next[3] = { 1, 2, 0 };
613 int i = 0;
614 if (rot3x3(1, 1) > rot3x3(0, 0))
615 i = 1;
616 if (rot3x3(2, 2) > rot3x3(i, i))
617 i = 2;
618 int j = s_next[i];
619 int k = s_next[j];
620
621 const float s = 2.0f * std::sqrt(rot3x3(i, i) - rot3x3(j, j) - rot3x3(k, k) + 1.0f);
622 axis[i] = 0.25f * s;
623 scalar = (rot3x3(k, j) - rot3x3(j, k)) / s;
624 axis[j] = (rot3x3(j, i) + rot3x3(i, j)) / s;
625 axis[k] = (rot3x3(k, i) + rot3x3(i, k)) / s;
626 }
627
628 return QQuaternion(scalar, axis[0], axis[1], axis[2]);
629}
630
631#ifndef QT_NO_VECTOR3D
632
640void QQuaternion::getAxes(QVector3D *xAxis, QVector3D *yAxis, QVector3D *zAxis) const
641{
642 Q_ASSERT(xAxis && yAxis && zAxis);
643
644 const QMatrix3x3 rot3x3(toRotationMatrix());
645
646 *xAxis = QVector3D(rot3x3(0, 0), rot3x3(1, 0), rot3x3(2, 0));
647 *yAxis = QVector3D(rot3x3(0, 1), rot3x3(1, 1), rot3x3(2, 1));
648 *zAxis = QVector3D(rot3x3(0, 2), rot3x3(1, 2), rot3x3(2, 2));
649}
650
660QQuaternion QQuaternion::fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis)
661{
663 rot3x3(0, 0) = xAxis.x();
664 rot3x3(1, 0) = xAxis.y();
665 rot3x3(2, 0) = xAxis.z();
666 rot3x3(0, 1) = yAxis.x();
667 rot3x3(1, 1) = yAxis.y();
668 rot3x3(2, 1) = yAxis.z();
669 rot3x3(0, 2) = zAxis.x();
670 rot3x3(1, 2) = zAxis.y();
671 rot3x3(2, 2) = zAxis.z();
672
673 return QQuaternion::fromRotationMatrix(rot3x3);
674}
675
687{
689 return QQuaternion();
690
691 const QVector3D zAxis(direction.normalized());
692 QVector3D xAxis(QVector3D::crossProduct(up, zAxis));
693 if (qFuzzyIsNull(xAxis.lengthSquared())) {
694 // collinear or invalid up vector; derive shortest arc to new direction
695 return QQuaternion::rotationTo(QVector3D(0.0f, 0.0f, 1.0f), zAxis);
696 }
697
698 xAxis.normalize();
699 const QVector3D yAxis(QVector3D::crossProduct(zAxis, xAxis));
700
701 return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
702}
703
713{
714 // Based on Stan Melax's article in Game Programming Gems
715
716 const QVector3D v0(from.normalized());
717 const QVector3D v1(to.normalized());
718
719 float d = QVector3D::dotProduct(v0, v1) + 1.0f;
720
721 // if dest vector is close to the inverse of source vector, ANY axis of rotation is valid
722 if (qFuzzyIsNull(d)) {
723 QVector3D axis = QVector3D::crossProduct(QVector3D(1.0f, 0.0f, 0.0f), v0);
724 if (qFuzzyIsNull(axis.lengthSquared()))
725 axis = QVector3D::crossProduct(QVector3D(0.0f, 1.0f, 0.0f), v0);
726 axis.normalize();
727
728 // same as QQuaternion::fromAxisAndAngle(axis, 180.0f)
729 return QQuaternion(0.0f, axis.x(), axis.y(), axis.z());
730 }
731
732 d = std::sqrt(2.0f * d);
733 const QVector3D axis(QVector3D::crossProduct(v0, v1) / d);
734
735 return QQuaternion(d * 0.5f, axis).normalized();
736}
737
738#endif // QT_NO_VECTOR3D
739
826#ifndef QT_NO_VECTOR3D
827
836#endif
837
858 (const QQuaternion& q1, const QQuaternion& q2, float t)
859{
860 // Handle the easy cases first.
861 if (t <= 0.0f)
862 return q1;
863 else if (t >= 1.0f)
864 return q2;
865
866 // Determine the angle between the two quaternions.
867 QQuaternion q2b(q2);
868 float dot = QQuaternion::dotProduct(q1, q2);
869 if (dot < 0.0f) {
870 q2b = -q2b;
871 dot = -dot;
872 }
873
874 // Get the scale factors. If they are too small,
875 // then revert to simple linear interpolation.
876 float factor1 = 1.0f - t;
877 float factor2 = t;
878 if ((1.0f - dot) > 0.0000001) {
879 float angle = std::acos(dot);
880 float sinOfAngle = std::sin(angle);
881 if (sinOfAngle > 0.0000001) {
882 factor1 = std::sin((1.0f - t) * angle) / sinOfAngle;
883 factor2 = std::sin(t * angle) / sinOfAngle;
884 }
885 }
886
887 // Construct the result quaternion.
888 return q1 * factor1 + q2b * factor2;
889}
890
907 (const QQuaternion& q1, const QQuaternion& q2, float t)
908{
909 // Handle the easy cases first.
910 if (t <= 0.0f)
911 return q1;
912 else if (t >= 1.0f)
913 return q2;
914
915 // Determine the angle between the two quaternions.
916 QQuaternion q2b(q2);
917 float dot = QQuaternion::dotProduct(q1, q2);
918 if (dot < 0.0f)
919 q2b = -q2b;
920
921 // Perform the linear interpolation.
922 return (q1 * (1.0f - t) + q2b * t).normalized();
923}
924
928QQuaternion::operator QVariant() const
929{
930 return QVariant::fromValue(*this);
931}
932
933#ifndef QT_NO_DEBUG_STREAM
934
936{
937 QDebugStateSaver saver(dbg);
938 dbg.nospace() << "QQuaternion(scalar:" << q.scalar()
939 << ", vector:(" << q.x() << ", "
940 << q.y() << ", " << q.z() << "))";
941 return dbg;
942}
943
944#endif
945
946#ifndef QT_NO_DATASTREAM
947
959{
960 stream << quaternion.scalar() << quaternion.x()
961 << quaternion.y() << quaternion.z();
962 return stream;
963}
964
976{
977 float scalar, x, y, z;
978 stream >> scalar;
979 stream >> x;
980 stream >> y;
981 stream >> z;
982 quaternion.setScalar(scalar);
983 quaternion.setX(x);
984 quaternion.setY(y);
985 quaternion.setZ(z);
986 return stream;
987}
988
989#endif // QT_NO_DATASTREAM
990
991#endif
992
\inmodule QtCore\reentrant
Definition qdatastream.h:30
\inmodule QtCore
\inmodule QtCore
The QQuaternion class represents a quaternion consisting of a vector and scalar.
Definition qquaternion.h:21
float lengthSquared() const
Returns the squared length of the quaternion.
void getEulerAngles(float *pitch, float *yaw, float *roll) const
QQuaternion conjugated() const
void getAxes(QVector3D *xAxis, QVector3D *yAxis, QVector3D *zAxis) const
static QQuaternion fromEulerAngles(const QVector3D &eulerAngles)
void normalize()
Normalizes the current quaternion in place.
static QQuaternion rotationTo(const QVector3D &from, const QVector3D &to)
QQuaternion()
Constructs an identity quaternion (1, 0, 0, 0), i.e.
static QQuaternion fromDirection(const QVector3D &direction, const QVector3D &up)
friend bool qFuzzyCompare(const QQuaternion &q1, const QQuaternion &q2)
Returns true if q1 and q2 are equal, allowing for a small fuzziness factor for floating-point compari...
QVector3D vector() const
Returns the vector component of this quaternion.
static QQuaternion slerp(const QQuaternion &q1, const QQuaternion &q2, float t)
Interpolates along the shortest spherical path between the rotational positions q1 and q2.
static QQuaternion fromRotationMatrix(const QMatrix3x3 &rot3x3)
static QQuaternion fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis)
float z() const
Returns the z coordinate of this quaternion's vector.
void setScalar(float scalar)
Sets the scalar component of this quaternion to scalar.
float scalar() const
Returns the scalar component of this quaternion.
void getAxisAndAngle(QVector3D *axis, float *angle) const
void setX(float x)
Sets the x coordinate of this quaternion's vector to the given x coordinate.
void setZ(float z)
Sets the z coordinate of this quaternion's vector to the given z coordinate.
float x() const
Returns the x coordinate of this quaternion's vector.
static QQuaternion nlerp(const QQuaternion &q1, const QQuaternion &q2, float t)
Interpolates along the shortest linear path between the rotational positions q1 and q2.
QQuaternion normalized() const
Returns the normalized unit form of this quaternion.
float y() const
Returns the y coordinate of this quaternion's vector.
static constexpr float dotProduct(const QQuaternion &q1, const QQuaternion &q2)
void setY(float y)
Sets the y coordinate of this quaternion's vector to the given y coordinate.
QMatrix3x3 toRotationMatrix() const
QVector3D rotatedVector(const QVector3D &vector) const
Rotates vector with this quaternion to produce a new vector in 3D space.
static QQuaternion fromAxisAndAngle(const QVector3D &axis, float angle)
Creates a normalized quaternion that corresponds to rotating through angle degrees about the specifie...
float length() const
Returns the length of the quaternion.
\inmodule QtCore
Definition qvariant.h:64
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
QVector3D normalized() const noexcept
Returns the normalized unit vector form of this vector.
Definition qvectornd.h:695
constexpr float lengthSquared() const noexcept
Returns the squared length of the vector from the origin.
Definition qvectornd.h:713
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
static constexpr float dotProduct(QVector3D v1, QVector3D v2) noexcept
Returns the dot product of v1 and v2.
Definition qvectornd.h:770
static constexpr QVector3D crossProduct(QVector3D v1, QVector3D v2) noexcept
Returns the cross-product of vectors v1 and v2, which is normal to the plane spanned by v1 and v2.
Definition qvectornd.h:775
void normalize() noexcept
Normalizes the current vector in place.
Definition qvectornd.h:702
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:672
direction
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
Definition qctf_p.h:77
EGLStreamKHR stream
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
auto qHypot(qfloat16 x, qfloat16 y)
Definition qfloat16.h:397
constexpr float qRadiansToDegrees(float radians)
Definition qmath.h:281
#define M_PI_2
Definition qmath.h:213
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLenum GLuint GLenum GLsizei length
GLfloat angle
GLint GLfloat v0
GLint GLfloat GLfloat v1
GLint y
const GLubyte * c
GLenum GLsizei len
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLdouble s
[6]
Definition qopenglext.h:235
GLenum GLenum GLenum GLenum GLenum scale
static qreal dot(const QPointF &a, const QPointF &b)
QDebug operator<<(QDebug dbg, const QQuaternion &q)
QDataStream & operator>>(QDataStream &stream, QQuaternion &quaternion)
static const qreal epsilon
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define s3
#define s2
#define v1
#define v0
QList< int > vector
[14]
MyCustomStruct c2