Coverage Report

Created: 2026-01-25 07:18

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
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
12
QT_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
*/
199
float QQuaternion::length() const
200
0
{
201
0
    return qHypot(xp, yp, zp, wp);
202
0
}
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
*/
212
float QQuaternion::lengthSquared() const
213
0
{
214
0
    return xp * xp + yp * yp + zp * zp + wp * wp;
215
0
}
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
*/
227
QQuaternion QQuaternion::normalized() const
228
0
{
229
0
    const float scale = length();
230
0
    if (qFuzzyIsNull(scale))
231
0
        return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
232
0
    return *this / scale;
233
0
}
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
*/
241
void QQuaternion::normalize()
242
0
{
243
0
    const float len = length();
244
0
    if (qFuzzyIsNull(len))
245
0
        return;
246
247
0
    xp /= len;
248
0
    yp /= len;
249
0
    zp /= len;
250
0
    wp /= len;
251
0
}
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
*/
281
QVector3D QQuaternion::rotatedVector(const QVector3D &vector) const
282
0
{
283
0
    return (*this * QQuaternion(0, vector) * conjugated()).vector();
284
0
}
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
*/
351
QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D &axis, float angle)
352
0
{
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
0
    float a = qDegreesToRadians(angle / 2.0f);
358
0
    float s = std::sin(a);
359
0
    float c = std::cos(a);
360
0
    QVector3D ax = axis.normalized();
361
0
    return QQuaternion(c, ax.x() * s, ax.y() * s, ax.z() * s).normalized();
362
0
}
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
*/
377
void QQuaternion::getAxisAndAngle(float *x, float *y, float *z, float *angle) const
378
0
{
379
0
    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
0
    const float length = qHypot(xp, yp, zp);
385
0
    if (!qFuzzyIsNull(length)) {
386
0
        if (qFuzzyCompare(length, 1.0f)) {
387
0
            *x = xp;
388
0
            *y = yp;
389
0
            *z = zp;
390
0
        } else {
391
0
            *x = xp / length;
392
0
            *y = yp / length;
393
0
            *z = zp / length;
394
0
        }
395
0
        *angle = qRadiansToDegrees(2.0f * std::atan2(length, wp));
396
0
    } else {
397
        // angle is 0 (mod 2*pi), so any axis will fit
398
0
        *x = *y = *z = *angle = 0.0f;
399
0
    }
400
0
}
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
*/
408
QQuaternion QQuaternion::fromAxisAndAngle
409
        (float x, float y, float z, float angle)
410
0
{
411
0
    float length = qHypot(x, y, z);
412
0
    if (!qFuzzyIsNull(length) && !qFuzzyCompare(length, 1.0f)) {
413
0
        x /= length;
414
0
        y /= length;
415
0
        z /= length;
416
0
    }
417
0
    float a = qDegreesToRadians(angle / 2.0f);
418
0
    float s = std::sin(a);
419
0
    float c = std::cos(a);
420
0
    return QQuaternion(c, x * s, y * s, z * s).normalized();
421
0
}
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
*/
508
auto QQuaternion::eulerAngles() const -> EulerAngles<float>
509
0
{
510
0
    EulerAngles<float> result;
511
512
    // to avoid churn
513
0
    auto pitch = &result.pitch;
514
0
    auto yaw = &result.yaw;
515
0
    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
0
    const float len = length();
525
0
    const bool rescale = !qFuzzyIsNull(len);
526
0
    const float xps = rescale ? xp / len : xp;
527
0
    const float yps = rescale ? yp / len : yp;
528
0
    const float zps = rescale ? zp / len : zp;
529
0
    const float wps = rescale ? wp / len : wp;
530
531
0
    const float xx = xps * xps;
532
0
    const float xy = xps * yps;
533
0
    const float xz = xps * zps;
534
0
    const float xw = xps * wps;
535
0
    const float yy = yps * yps;
536
0
    const float yz = yps * zps;
537
0
    const float yw = yps * wps;
538
0
    const float zz = zps * zps;
539
0
    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
0
    constexpr float epsilon = 0.00001f;
546
547
0
    const float sinp = -2.0f * (yz - xw);
548
0
    if (std::abs(sinp) < 1.0f - epsilon) {
549
0
        *pitch = std::asin(sinp);
550
0
        *yaw = std::atan2(2.0f * (xz + yw), 1.0f - 2.0f * (xx + yy));
551
0
        *roll = std::atan2(2.0f * (xy + zw), 1.0f - 2.0f * (xx + zz));
552
0
    } else {
553
        // Gimbal lock case, which doesn't have a unique solution. We just use
554
        // XY rotation.
555
0
        *pitch = std::copysign(static_cast<float>(M_PI_2), sinp);
556
0
        *yaw = 2.0f * std::atan2(yps, wps);
557
0
        *roll = 0.0f;
558
0
    }
559
560
0
    *pitch = qRadiansToDegrees(*pitch);
561
0
    *yaw = qRadiansToDegrees(*yaw);
562
0
    *roll = qRadiansToDegrees(*roll);
563
564
0
    return result;
565
0
}
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
*/
576
QQuaternion QQuaternion::fromEulerAngles(float pitch, float yaw, float roll)
577
0
{
578
    // Algorithm from:
579
    // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60
580
581
0
    pitch = qDegreesToRadians(pitch);
582
0
    yaw = qDegreesToRadians(yaw);
583
0
    roll = qDegreesToRadians(roll);
584
585
0
    pitch *= 0.5f;
586
0
    yaw *= 0.5f;
587
0
    roll *= 0.5f;
588
589
0
    const float c1 = std::cos(yaw);
590
0
    const float s1 = std::sin(yaw);
591
0
    const float c2 = std::cos(roll);
592
0
    const float s2 = std::sin(roll);
593
0
    const float c3 = std::cos(pitch);
594
0
    const float s3 = std::sin(pitch);
595
0
    const float c1c2 = c1 * c2;
596
0
    const float s1s2 = s1 * s2;
597
598
0
    const float w = c1c2 * c3 + s1s2 * s3;
599
0
    const float x = c1c2 * s3 + s1s2 * c3;
600
0
    const float y = s1 * c2 * c3 - c1 * s2 * s3;
601
0
    const float z = c1 * s2 * c3 - s1 * c2 * s3;
602
603
0
    return QQuaternion(w, x, y, z);
604
0
}
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
*/
629
QMatrix3x3 QQuaternion::toRotationMatrix() const
630
0
{
631
    // Algorithm from:
632
    // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54
633
634
0
    QMatrix3x3 rot3x3(Qt::Uninitialized);
635
636
0
    const float f2x = xp + xp;
637
0
    const float f2y = yp + yp;
638
0
    const float f2z = zp + zp;
639
0
    const float f2xw = f2x * wp;
640
0
    const float f2yw = f2y * wp;
641
0
    const float f2zw = f2z * wp;
642
0
    const float f2xx = f2x * xp;
643
0
    const float f2xy = f2x * yp;
644
0
    const float f2xz = f2x * zp;
645
0
    const float f2yy = f2y * yp;
646
0
    const float f2yz = f2y * zp;
647
0
    const float f2zz = f2z * zp;
648
649
0
    rot3x3(0, 0) = 1.0f - (f2yy + f2zz);
650
0
    rot3x3(0, 1) =         f2xy - f2zw;
651
0
    rot3x3(0, 2) =         f2xz + f2yw;
652
0
    rot3x3(1, 0) =         f2xy + f2zw;
653
0
    rot3x3(1, 1) = 1.0f - (f2xx + f2zz);
654
0
    rot3x3(1, 2) =         f2yz - f2xw;
655
0
    rot3x3(2, 0) =         f2xz - f2yw;
656
0
    rot3x3(2, 1) =         f2yz + f2xw;
657
0
    rot3x3(2, 2) = 1.0f - (f2xx + f2yy);
658
659
0
    return rot3x3;
660
0
}
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
*/
672
QQuaternion QQuaternion::fromRotationMatrix(const QMatrix3x3 &rot3x3)
673
0
{
674
    // Algorithm from:
675
    // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55
676
677
0
    float scalar;
678
0
    float axis[3];
679
680
0
    const float trace = rot3x3(0, 0) + rot3x3(1, 1) + rot3x3(2, 2);
681
0
    if (trace > 0.00000001f) {
682
0
        const float s = 2.0f * std::sqrt(trace + 1.0f);
683
0
        scalar = 0.25f * s;
684
0
        axis[0] = (rot3x3(2, 1) - rot3x3(1, 2)) / s;
685
0
        axis[1] = (rot3x3(0, 2) - rot3x3(2, 0)) / s;
686
0
        axis[2] = (rot3x3(1, 0) - rot3x3(0, 1)) / s;
687
0
    } else {
688
0
        constexpr int s_next[3] = { 1, 2, 0 };
689
0
        int i = 0;
690
0
        if (rot3x3(1, 1) > rot3x3(0, 0))
691
0
            i = 1;
692
0
        if (rot3x3(2, 2) > rot3x3(i, i))
693
0
            i = 2;
694
0
        int j = s_next[i];
695
0
        int k = s_next[j];
696
697
0
        const float s = 2.0f * std::sqrt(rot3x3(i, i) - rot3x3(j, j) - rot3x3(k, k) + 1.0f);
698
0
        axis[i] = 0.25f * s;
699
0
        scalar = (rot3x3(k, j) - rot3x3(j, k)) / s;
700
0
        axis[j] = (rot3x3(j, i) + rot3x3(i, j)) / s;
701
0
        axis[k] = (rot3x3(k, i) + rot3x3(i, k)) / s;
702
0
    }
703
704
0
    return QQuaternion(scalar, axis[0], axis[1], axis[2]);
705
0
}
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
*/
750
auto QQuaternion::toAxes() const -> Axes
751
0
{
752
0
    const QMatrix3x3 rot3x3(toRotationMatrix());
753
754
0
    return { {rot3x3(0, 0), rot3x3(1, 0), rot3x3(2, 0)},
755
0
             {rot3x3(0, 1), rot3x3(1, 1), rot3x3(2, 1)},
756
0
             {rot3x3(0, 2), rot3x3(1, 2), rot3x3(2, 2)} };
757
0
}
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
*/
784
QQuaternion QQuaternion::fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis)
785
0
{
786
0
    QMatrix3x3 rot3x3(Qt::Uninitialized);
787
0
    rot3x3(0, 0) = xAxis.x();
788
0
    rot3x3(1, 0) = xAxis.y();
789
0
    rot3x3(2, 0) = xAxis.z();
790
0
    rot3x3(0, 1) = yAxis.x();
791
0
    rot3x3(1, 1) = yAxis.y();
792
0
    rot3x3(2, 1) = yAxis.z();
793
0
    rot3x3(0, 2) = zAxis.x();
794
0
    rot3x3(1, 2) = zAxis.y();
795
0
    rot3x3(2, 2) = zAxis.z();
796
797
0
    return QQuaternion::fromRotationMatrix(rot3x3);
798
0
}
799
800
/*!
801
    \since 6.11
802
    \overload
803
804
    \sa toAxes(), fromRotationMatrix()
805
*/
806
QQuaternion QQuaternion::fromAxes(Axes axes) // clazy:exclude=function-args-by-ref
807
0
{
808
0
    return fromAxes(axes.x, axes.y, axes.z);
809
0
}
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
*/
821
QQuaternion QQuaternion::fromDirection(const QVector3D &direction, const QVector3D &up)
822
0
{
823
0
    if (qFuzzyIsNull(direction.x()) && qFuzzyIsNull(direction.y()) && qFuzzyIsNull(direction.z()))
824
0
        return QQuaternion();
825
826
0
    const QVector3D zAxis(direction.normalized());
827
0
    QVector3D xAxis(QVector3D::crossProduct(up, zAxis));
828
0
    if (qFuzzyIsNull(xAxis.lengthSquared())) {
829
        // collinear or invalid up vector; derive shortest arc to new direction
830
0
        return QQuaternion::rotationTo(QVector3D(0.0f, 0.0f, 1.0f), zAxis);
831
0
    }
832
833
0
    xAxis.normalize();
834
0
    const QVector3D yAxis(QVector3D::crossProduct(zAxis, xAxis));
835
836
0
    return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
837
0
}
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
*/
847
QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
848
0
{
849
    // Based on Stan Melax's article in Game Programming Gems
850
851
0
    const QVector3D v0(from.normalized());
852
0
    const QVector3D v1(to.normalized());
853
854
0
    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
0
    if (qFuzzyIsNull(d)) {
858
0
        QVector3D axis = QVector3D::crossProduct(QVector3D(1.0f, 0.0f, 0.0f), v0);
859
0
        if (qFuzzyIsNull(axis.lengthSquared()))
860
0
            axis = QVector3D::crossProduct(QVector3D(0.0f, 1.0f, 0.0f), v0);
861
0
        axis.normalize();
862
863
        // same as QQuaternion::fromAxisAndAngle(axis, 180.0f)
864
0
        return QQuaternion(0.0f, axis.x(), axis.y(), axis.z());
865
0
    }
866
867
0
    d = std::sqrt(2.0f * d);
868
0
    const QVector3D axis(QVector3D::crossProduct(v0, v1) / d);
869
870
0
    return QQuaternion(d * 0.5f, axis).normalized();
871
0
}
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
*/
992
QQuaternion QQuaternion::slerp
993
    (const QQuaternion &q1, const QQuaternion &q2, float t)
994
0
{
995
    // Handle the easy cases first.
996
0
    if (t <= 0.0f)
997
0
        return q1;
998
0
    else if (t >= 1.0f)
999
0
        return q2;
1000
1001
    // Determine the angle between the two quaternions.
1002
0
    QQuaternion q2b(q2);
1003
0
    float dot = QQuaternion::dotProduct(q1, q2);
1004
0
    if (dot < 0.0f) {
1005
0
        q2b = -q2b;
1006
0
        dot = -dot;
1007
0
    }
1008
1009
    // Get the scale factors.  If they are too small,
1010
    // then revert to simple linear interpolation.
1011
0
    float factor1 = 1.0f - t;
1012
0
    float factor2 = t;
1013
0
    if ((1.0f - dot) > 0.0000001) {
1014
0
        float angle = std::acos(dot);
1015
0
        float sinOfAngle = std::sin(angle);
1016
0
        if (sinOfAngle > 0.0000001) {
1017
0
            factor1 = std::sin((1.0f - t) * angle) / sinOfAngle;
1018
0
            factor2 = std::sin(t * angle) / sinOfAngle;
1019
0
        }
1020
0
    }
1021
1022
    // Construct the result quaternion.
1023
0
    return q1 * factor1 + q2b * factor2;
1024
0
}
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
*/
1041
QQuaternion QQuaternion::nlerp
1042
    (const QQuaternion &q1, const QQuaternion &q2, float t)
1043
0
{
1044
    // Handle the easy cases first.
1045
0
    if (t <= 0.0f)
1046
0
        return q1;
1047
0
    else if (t >= 1.0f)
1048
0
        return q2;
1049
1050
    // Determine the angle between the two quaternions.
1051
0
    QQuaternion q2b(q2);
1052
0
    float dot = QQuaternion::dotProduct(q1, q2);
1053
0
    if (dot < 0.0f)
1054
0
        q2b = -q2b;
1055
1056
    // Perform the linear interpolation.
1057
0
    return (q1 * (1.0f - t) + q2b * t).normalized();
1058
0
}
1059
1060
/*!
1061
    Returns the quaternion as a QVariant.
1062
*/
1063
QQuaternion::operator QVariant() const
1064
0
{
1065
0
    return QVariant::fromValue(*this);
1066
0
}
1067
1068
#ifndef QT_NO_DEBUG_STREAM
1069
1070
QDebug operator<<(QDebug dbg, const QQuaternion &q)
1071
0
{
1072
0
    QDebugStateSaver saver(dbg);
1073
0
    dbg.nospace() << "QQuaternion(scalar:" << q.scalar()
1074
0
        << ", vector:(" << q.x() << ", "
1075
0
        << q.y() << ", " << q.z() << "))";
1076
0
    return dbg;
1077
0
}
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
1093
QDataStream &operator<<(QDataStream &stream, const QQuaternion &quaternion)
1094
0
{
1095
0
    stream << quaternion.scalar() << quaternion.x()
1096
0
           << quaternion.y() << quaternion.z();
1097
0
    return stream;
1098
0
}
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
1110
QDataStream &operator>>(QDataStream &stream, QQuaternion &quaternion)
1111
0
{
1112
0
    float scalar, x, y, z;
1113
0
    stream >> scalar;
1114
0
    stream >> x;
1115
0
    stream >> y;
1116
0
    stream >> z;
1117
0
    quaternion.setScalar(scalar);
1118
0
    quaternion.setX(x);
1119
0
    quaternion.setY(y);
1120
0
    quaternion.setZ(z);
1121
0
    return stream;
1122
0
}
1123
1124
#endif // QT_NO_DATASTREAM
1125
1126
#endif
1127
1128
QT_END_NAMESPACE