Coverage Report

Created: 2026-05-16 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/math3d/qquaternion.cpp
Line
Count
Source
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
13
QT_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
*/
273
float QQuaternion::length() const
274
0
{
275
0
    return qHypot(xp, yp, zp, wp);
276
0
}
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
*/
286
float QQuaternion::lengthSquared() const
287
0
{
288
0
    return xp * xp + yp * yp + zp * zp + wp * wp;
289
0
}
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
*/
301
QQuaternion QQuaternion::normalized() const
302
0
{
303
0
    const float scale = length();
304
0
    if (qFuzzyIsNull(scale))
305
0
        return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
306
0
    return *this / scale;
307
0
}
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
*/
315
void QQuaternion::normalize()
316
0
{
317
0
    const float len = length();
318
0
    if (qFuzzyIsNull(len))
319
0
        return;
320
321
0
    xp /= len;
322
0
    yp /= len;
323
0
    zp /= len;
324
0
    wp /= len;
325
0
}
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
*/
355
QVector3D QQuaternion::rotatedVector(const QVector3D &vector) const
356
0
{
357
0
    return (*this * QQuaternion(0, vector) * conjugated()).vector();
358
0
}
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
*/
425
QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D &axis, float angle)
426
0
{
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
0
    float a = qDegreesToRadians(angle / 2.0f);
432
0
    float s = std::sin(a);
433
0
    float c = std::cos(a);
434
0
    QVector3D ax = axis.normalized();
435
0
    return QQuaternion(c, ax.x() * s, ax.y() * s, ax.z() * s).normalized();
436
0
}
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
*/
451
void QQuaternion::getAxisAndAngle(float *x, float *y, float *z, float *angle) const
452
0
{
453
0
    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
0
    const float length = qHypot(xp, yp, zp);
459
0
    if (!qFuzzyIsNull(length)) {
460
0
        if (qFuzzyCompare(length, 1.0f)) {
461
0
            *x = xp;
462
0
            *y = yp;
463
0
            *z = zp;
464
0
        } else {
465
0
            *x = xp / length;
466
0
            *y = yp / length;
467
0
            *z = zp / length;
468
0
        }
469
0
        *angle = qRadiansToDegrees(2.0f * std::atan2(length, wp));
470
0
    } else {
471
        // angle is 0 (mod 2*pi), so any axis will fit
472
0
        *x = *y = *z = *angle = 0.0f;
473
0
    }
474
0
}
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
*/
482
QQuaternion QQuaternion::fromAxisAndAngle
483
        (float x, float y, float z, float angle)
484
0
{
485
0
    float length = qHypot(x, y, z);
486
0
    if (!qFuzzyIsNull(length) && !qFuzzyCompare(length, 1.0f)) {
487
0
        x /= length;
488
0
        y /= length;
489
0
        z /= length;
490
0
    }
491
0
    float a = qDegreesToRadians(angle / 2.0f);
492
0
    float s = std::sin(a);
493
0
    float c = std::cos(a);
494
0
    return QQuaternion(c, x * s, y * s, z * s).normalized();
495
0
}
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
*/
585
auto QQuaternion::eulerAngles() const -> EulerAngles<float>
586
0
{
587
0
    EulerAngles<float> result;
588
589
    // to avoid churn
590
0
    auto pitch = &result.pitch;
591
0
    auto yaw = &result.yaw;
592
0
    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
0
    const float len = length();
602
0
    const bool rescale = !qFuzzyIsNull(len);
603
0
    const float xps = rescale ? xp / len : xp;
604
0
    const float yps = rescale ? yp / len : yp;
605
0
    const float zps = rescale ? zp / len : zp;
606
0
    const float wps = rescale ? wp / len : wp;
607
608
0
    const float xx = xps * xps;
609
0
    const float xy = xps * yps;
610
0
    const float xz = xps * zps;
611
0
    const float xw = xps * wps;
612
0
    const float yy = yps * yps;
613
0
    const float yz = yps * zps;
614
0
    const float yw = yps * wps;
615
0
    const float zz = zps * zps;
616
0
    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
0
    constexpr float epsilon = 0.00001f;
623
624
0
    const float sinp = -2.0f * (yz - xw);
625
0
    if (std::abs(sinp) < 1.0f - epsilon) {
626
0
        *pitch = std::asin(sinp);
627
0
        *yaw = std::atan2(2.0f * (xz + yw), 1.0f - 2.0f * (xx + yy));
628
0
        *roll = std::atan2(2.0f * (xy + zw), 1.0f - 2.0f * (xx + zz));
629
0
    } else {
630
        // Gimbal lock case, which doesn't have a unique solution. We just use
631
        // XY rotation.
632
0
        *pitch = std::copysign(static_cast<float>(M_PI_2), sinp);
633
0
        *yaw = 2.0f * std::atan2(yps, wps);
634
0
        *roll = 0.0f;
635
0
    }
636
637
0
    *pitch = qRadiansToDegrees(*pitch);
638
0
    *yaw = qRadiansToDegrees(*yaw);
639
0
    *roll = qRadiansToDegrees(*roll);
640
641
0
    return result;
642
0
}
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
*/
653
QQuaternion QQuaternion::fromEulerAngles(float pitch, float yaw, float roll)
654
0
{
655
    // Algorithm from:
656
    // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60
657
658
0
    pitch = qDegreesToRadians(pitch);
659
0
    yaw = qDegreesToRadians(yaw);
660
0
    roll = qDegreesToRadians(roll);
661
662
0
    pitch *= 0.5f;
663
0
    yaw *= 0.5f;
664
0
    roll *= 0.5f;
665
666
0
    const float c1 = std::cos(yaw);
667
0
    const float s1 = std::sin(yaw);
668
0
    const float c2 = std::cos(roll);
669
0
    const float s2 = std::sin(roll);
670
0
    const float c3 = std::cos(pitch);
671
0
    const float s3 = std::sin(pitch);
672
0
    const float c1c2 = c1 * c2;
673
0
    const float s1s2 = s1 * s2;
674
675
0
    const float w = c1c2 * c3 + s1s2 * s3;
676
0
    const float x = c1c2 * s3 + s1s2 * c3;
677
0
    const float y = s1 * c2 * c3 - c1 * s2 * s3;
678
0
    const float z = c1 * s2 * c3 - s1 * c2 * s3;
679
680
0
    return QQuaternion(w, x, y, z);
681
0
}
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
*/
706
QMatrix3x3 QQuaternion::toRotationMatrix() const
707
0
{
708
    // Algorithm from:
709
    // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54
710
711
0
    QMatrix3x3 rot3x3(Qt::Uninitialized);
712
713
0
    const float f2x = xp + xp;
714
0
    const float f2y = yp + yp;
715
0
    const float f2z = zp + zp;
716
0
    const float f2xw = f2x * wp;
717
0
    const float f2yw = f2y * wp;
718
0
    const float f2zw = f2z * wp;
719
0
    const float f2xx = f2x * xp;
720
0
    const float f2xy = f2x * yp;
721
0
    const float f2xz = f2x * zp;
722
0
    const float f2yy = f2y * yp;
723
0
    const float f2yz = f2y * zp;
724
0
    const float f2zz = f2z * zp;
725
726
0
    rot3x3(0, 0) = 1.0f - (f2yy + f2zz);
727
0
    rot3x3(0, 1) =         f2xy - f2zw;
728
0
    rot3x3(0, 2) =         f2xz + f2yw;
729
0
    rot3x3(1, 0) =         f2xy + f2zw;
730
0
    rot3x3(1, 1) = 1.0f - (f2xx + f2zz);
731
0
    rot3x3(1, 2) =         f2yz - f2xw;
732
0
    rot3x3(2, 0) =         f2xz - f2yw;
733
0
    rot3x3(2, 1) =         f2yz + f2xw;
734
0
    rot3x3(2, 2) = 1.0f - (f2xx + f2yy);
735
736
0
    return rot3x3;
737
0
}
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
*/
749
QQuaternion QQuaternion::fromRotationMatrix(const QMatrix3x3 &rot3x3)
750
0
{
751
    // Algorithm from:
752
    // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55
753
754
0
    float scalar;
755
0
    float axis[3];
756
757
0
    const float trace = rot3x3(0, 0) + rot3x3(1, 1) + rot3x3(2, 2);
758
0
    if (trace > 0.00000001f) {
759
0
        const float s = 2.0f * std::sqrt(trace + 1.0f);
760
0
        scalar = 0.25f * s;
761
0
        axis[0] = (rot3x3(2, 1) - rot3x3(1, 2)) / s;
762
0
        axis[1] = (rot3x3(0, 2) - rot3x3(2, 0)) / s;
763
0
        axis[2] = (rot3x3(1, 0) - rot3x3(0, 1)) / s;
764
0
    } else {
765
0
        constexpr int s_next[3] = { 1, 2, 0 };
766
0
        int i = 0;
767
0
        if (rot3x3(1, 1) > rot3x3(0, 0))
768
0
            i = 1;
769
0
        if (rot3x3(2, 2) > rot3x3(i, i))
770
0
            i = 2;
771
0
        int j = s_next[i];
772
0
        int k = s_next[j];
773
774
0
        const float s = 2.0f * std::sqrt(rot3x3(i, i) - rot3x3(j, j) - rot3x3(k, k) + 1.0f);
775
0
        axis[i] = 0.25f * s;
776
0
        scalar = (rot3x3(k, j) - rot3x3(j, k)) / s;
777
0
        axis[j] = (rot3x3(j, i) + rot3x3(i, j)) / s;
778
0
        axis[k] = (rot3x3(k, i) + rot3x3(i, k)) / s;
779
0
    }
780
781
0
    return QQuaternion(scalar, axis[0], axis[1], axis[2]);
782
0
}
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
*/
825
auto QQuaternion::toAxes() const -> Axes
826
0
{
827
0
    const QMatrix3x3 rot3x3(toRotationMatrix());
828
829
0
    return { {rot3x3(0, 0), rot3x3(1, 0), rot3x3(2, 0)},
830
0
             {rot3x3(0, 1), rot3x3(1, 1), rot3x3(2, 1)},
831
0
             {rot3x3(0, 2), rot3x3(1, 2), rot3x3(2, 2)} };
832
0
}
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
*/
858
QQuaternion QQuaternion::fromAxes(Axes axes) // clazy:exclude=function-args-by-ref
859
0
{
860
0
    QMatrix3x3 rot3x3(Qt::Uninitialized);
861
0
    rot3x3(0, 0) = axes.x.x;
862
0
    rot3x3(1, 0) = axes.x.y;
863
0
    rot3x3(2, 0) = axes.x.z;
864
0
    rot3x3(0, 1) = axes.y.x;
865
0
    rot3x3(1, 1) = axes.y.y;
866
0
    rot3x3(2, 1) = axes.y.z;
867
0
    rot3x3(0, 2) = axes.z.x;
868
0
    rot3x3(1, 2) = axes.z.y;
869
0
    rot3x3(2, 2) = axes.z.z;
870
871
0
    return QQuaternion::fromRotationMatrix(rot3x3);
872
0
}
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
*/
893
QQuaternion QQuaternion::fromDirection(const QVector3D &direction, const QVector3D &up)
894
0
{
895
0
    if (qFuzzyIsNull(direction.x()) && qFuzzyIsNull(direction.y()) && qFuzzyIsNull(direction.z()))
896
0
        return QQuaternion();
897
898
0
    const QVector3D zAxis(direction.normalized());
899
0
    QVector3D xAxis(QVector3D::crossProduct(up, zAxis));
900
0
    if (qFuzzyIsNull(xAxis.lengthSquared())) {
901
        // collinear or invalid up vector; derive shortest arc to new direction
902
0
        return QQuaternion::rotationTo(QVector3D(0.0f, 0.0f, 1.0f), zAxis);
903
0
    }
904
905
0
    xAxis.normalize();
906
0
    const QVector3D yAxis(QVector3D::crossProduct(zAxis, xAxis));
907
908
0
    return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
909
0
}
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
*/
919
QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
920
0
{
921
    // Based on Stan Melax's article in Game Programming Gems
922
923
0
    const QVector3D v0(from.normalized());
924
0
    const QVector3D v1(to.normalized());
925
926
0
    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
0
    if (qFuzzyIsNull(d)) {
930
0
        QVector3D axis = QVector3D::crossProduct(QVector3D(1.0f, 0.0f, 0.0f), v0);
931
0
        if (qFuzzyIsNull(axis.lengthSquared()))
932
0
            axis = QVector3D::crossProduct(QVector3D(0.0f, 1.0f, 0.0f), v0);
933
0
        axis.normalize();
934
935
        // same as QQuaternion::fromAxisAndAngle(axis, 180.0f)
936
0
        return QQuaternion(0.0f, axis.x(), axis.y(), axis.z());
937
0
    }
938
939
0
    d = std::sqrt(2.0f * d);
940
0
    const QVector3D axis(QVector3D::crossProduct(v0, v1) / d);
941
942
0
    return QQuaternion(d * 0.5f, axis).normalized();
943
0
}
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
*/
1064
QQuaternion QQuaternion::slerp
1065
    (const QQuaternion &q1, const QQuaternion &q2, float t)
1066
0
{
1067
    // Handle the easy cases first.
1068
0
    if (t <= 0.0f)
1069
0
        return q1;
1070
0
    else if (t >= 1.0f)
1071
0
        return q2;
1072
1073
    // Determine the angle between the two quaternions.
1074
0
    QQuaternion q2b(q2);
1075
0
    float dot = QQuaternion::dotProduct(q1, q2);
1076
0
    if (dot < 0.0f) {
1077
0
        q2b = -q2b;
1078
0
        dot = -dot;
1079
0
    }
1080
1081
    // Get the scale factors.  If they are too small,
1082
    // then revert to simple linear interpolation.
1083
0
    float factor1 = 1.0f - t;
1084
0
    float factor2 = t;
1085
0
    if ((1.0f - dot) > 0.0000001) {
1086
0
        float angle = std::acos(dot);
1087
0
        float sinOfAngle = std::sin(angle);
1088
0
        if (sinOfAngle > 0.0000001) {
1089
0
            factor1 = std::sin((1.0f - t) * angle) / sinOfAngle;
1090
0
            factor2 = std::sin(t * angle) / sinOfAngle;
1091
0
        }
1092
0
    }
1093
1094
    // Construct the result quaternion.
1095
0
    return q1 * factor1 + q2b * factor2;
1096
0
}
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
*/
1113
QQuaternion QQuaternion::nlerp
1114
    (const QQuaternion &q1, const QQuaternion &q2, float t)
1115
0
{
1116
    // Handle the easy cases first.
1117
0
    if (t <= 0.0f)
1118
0
        return q1;
1119
0
    else if (t >= 1.0f)
1120
0
        return q2;
1121
1122
    // Determine the angle between the two quaternions.
1123
0
    QQuaternion q2b(q2);
1124
0
    float dot = QQuaternion::dotProduct(q1, q2);
1125
0
    if (dot < 0.0f)
1126
0
        q2b = -q2b;
1127
1128
    // Perform the linear interpolation.
1129
0
    return (q1 * (1.0f - t) + q2b * t).normalized();
1130
0
}
1131
1132
/*!
1133
    Returns the quaternion as a QVariant.
1134
*/
1135
QQuaternion::operator QVariant() const
1136
0
{
1137
0
    return QVariant::fromValue(*this);
1138
0
}
1139
1140
#ifndef QT_NO_DEBUG_STREAM
1141
1142
QDebug operator<<(QDebug dbg, const QQuaternion &q)
1143
0
{
1144
0
    QDebugStateSaver saver(dbg);
1145
0
    dbg.nospace() << "QQuaternion(scalar:" << q.scalar()
1146
0
        << ", vector:(" << q.x() << ", "
1147
0
        << q.y() << ", " << q.z() << "))";
1148
0
    return dbg;
1149
0
}
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
1165
QDataStream &operator<<(QDataStream &stream, const QQuaternion &quaternion)
1166
0
{
1167
0
    stream << quaternion.scalar() << quaternion.x()
1168
0
           << quaternion.y() << quaternion.z();
1169
0
    return stream;
1170
0
}
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
1182
QDataStream &operator>>(QDataStream &stream, QQuaternion &quaternion)
1183
0
{
1184
0
    float scalar, x, y, z;
1185
0
    stream >> scalar;
1186
0
    stream >> x;
1187
0
    stream >> y;
1188
0
    stream >> z;
1189
0
    quaternion.setScalar(scalar);
1190
0
    quaternion.setX(x);
1191
0
    quaternion.setY(y);
1192
0
    quaternion.setZ(z);
1193
0
    return stream;
1194
0
}
1195
1196
#endif // QT_NO_DATASTREAM
1197
1198
#endif
1199
1200
QT_END_NAMESPACE