1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquaternion.h"
5#include <QtCore/qdatastream.h>
6#include <QtCore/qmath.h>
7#include <QtCore/qvariant.h>
8#include <QtCore/qdebug.h>
9
10#include <cmath>
11
12QT_BEGIN_NAMESPACE
13
14#ifndef QT_NO_QUATERNION
15
16/*!
17 \class QQuaternion
18 \brief The QQuaternion class represents a quaternion consisting of a vector and scalar.
19 \since 4.6
20 \ingroup painting-3D
21 \inmodule QtGui
22
23 Quaternions are used to represent rotations in 3D space, and
24 consist of a 3D rotation axis specified by the x, y, and z
25 coordinates, and a scalar representing the rotation angle.
26*/
27
28/*!
29 \fn QQuaternion::QQuaternion()
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)
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)
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)
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
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)
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)
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)
91
92 Constructs a quaternion from the components of \a vector.
93*/
94
95/*!
96 \fn QVector4D QQuaternion::toVector4D() const
97
98 Returns this quaternion as a 4D vector.
99*/
100
101#endif
102
103/*!
104 \fn bool QQuaternion::isNull() const
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
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
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
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
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
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)
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)
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)
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)
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)
187 \since 5.5
188
189 Returns the dot product of \a q1 and \a q2.
190
191 \sa length()
192*/
193
194/*!
195 Returns the length of the quaternion. This is also called the "norm".
196
197 \sa lengthSquared(), normalized(), dotProduct()
198*/
199float QQuaternion::length() const
200{
201 return qHypot(first: xp, rest: yp, rest: zp, rest: wp);
202}
203
204/*!
205 Returns the squared length of the quaternion.
206
207 \note Though cheap to compute, this is susceptible to overflow and underflow
208 that length() avoids in many cases.
209
210 \sa length(), dotProduct()
211*/
212float QQuaternion::lengthSquared() const
213{
214 return xp * xp + yp * yp + zp * zp + wp * wp;
215}
216
217/*!
218 Returns the normalized unit form of this quaternion.
219
220 If this quaternion is null, then a null quaternion is returned.
221 If the length of the quaternion is very close to 1, then the quaternion
222 will be returned as-is. Otherwise the normalized form of the
223 quaternion of length 1 will be returned.
224
225 \sa normalize(), length(), dotProduct()
226*/
227QQuaternion QQuaternion::normalized() const
228{
229 const float scale = length();
230 if (qFuzzyIsNull(f: scale))
231 return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
232 return *this / scale;
233}
234
235/*!
236 Normalizes the current quaternion in place. Nothing happens if this
237 is a null quaternion or the length of the quaternion is very close to 1.
238
239 \sa length(), normalized()
240*/
241void QQuaternion::normalize()
242{
243 const float len = length();
244 if (qFuzzyIsNull(f: len))
245 return;
246
247 xp /= len;
248 yp /= len;
249 zp /= len;
250 wp /= len;
251}
252
253/*!
254 \fn QQuaternion QQuaternion::inverted() const
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
265 \since 5.5
266
267 Returns the conjugate of this quaternion, which is
268 (-x, -y, -z, scalar).
269*/
270
271/*!
272 Rotates \a vector with this quaternion to produce a new vector
273 in 3D space. The following code:
274
275 \snippet code/src_gui_math3d_qquaternion.cpp 0
276
277 is equivalent to the following:
278
279 \snippet code/src_gui_math3d_qquaternion.cpp 1
280*/
281QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
282{
283 return (*this * QQuaternion(0, vector) * conjugated()).vector();
284}
285
286/*!
287 \fn QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion)
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)
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)
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)
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
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 \sa fromAxisAndAngle()
340*/
341
342/*!
343 Creates a normalized quaternion that corresponds to rotating through
344 \a angle degrees about the specified 3D \a axis.
345
346 \sa getAxisAndAngle()
347*/
348QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D& axis, float angle)
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(degrees: angle / 2.0f);
355 float s = std::sin(x: a);
356 float c = std::cos(x: 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
363/*!
364 \since 5.5
365
366 Extracts a 3D axis (\a x, \a y, \a z) and a rotating angle \a angle (in degrees)
367 that corresponds to this quaternion.
368
369 \sa fromAxisAndAngle()
370*/
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(x: xp, y: yp, z: zp);
379 if (!qFuzzyIsNull(f: length)) {
380 if (qFuzzyCompare(p1: length, p2: 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(radians: 2.0f * std::acos(x: 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
396/*!
397 Creates a normalized quaternion that corresponds to rotating through
398 \a angle degrees about the 3D axis (\a x, \a y, \a z).
399
400 \sa getAxisAndAngle()
401*/
402QQuaternion QQuaternion::fromAxisAndAngle
403 (float x, float y, float z, float angle)
404{
405 float length = qHypot(x, y, z);
406 if (!qFuzzyCompare(p1: length, p2: 1.0f) && !qFuzzyIsNull(f: length)) {
407 x /= length;
408 y /= length;
409 z /= length;
410 }
411 float a = qDegreesToRadians(degrees: angle / 2.0f);
412 float s = std::sin(x: a);
413 float c = std::cos(x: a);
414 return QQuaternion(c, x * s, y * s, z * s).normalized();
415}
416
417#ifndef QT_NO_VECTOR3D
418
419/*!
420 \fn QVector3D QQuaternion::toEulerAngles() const
421 \since 5.5
422 \overload
423
424 Calculates roll, pitch, and yaw Euler angles (in degrees)
425 that corresponds to this quaternion.
426
427 \sa fromEulerAngles()
428*/
429
430/*!
431 \fn QQuaternion QQuaternion::fromEulerAngles(const QVector3D &eulerAngles)
432 \since 5.5
433 \overload
434
435 Creates a quaternion that corresponds to a rotation of \a eulerAngles:
436 eulerAngles.z() degrees around the z axis, eulerAngles.x() degrees around the x axis,
437 and eulerAngles.y() degrees around the y axis (in that order).
438
439 \sa toEulerAngles()
440*/
441
442#endif // QT_NO_VECTOR3D
443
444/*!
445 \since 5.5
446
447 Calculates \a roll, \a pitch, and \a yaw Euler angles (in degrees)
448 that corresponds to this quaternion.
449
450 \sa fromEulerAngles()
451*/
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(f: 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(x: sinp) < 1.0f - epsilon) {
488 *pitch = std::asin(x: sinp);
489 *yaw = std::atan2(y: 2.0f * (xz + yw), x: 1.0f - 2.0f * (xx + yy));
490 *roll = std::atan2(y: 2.0f * (xy + zw), x: 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(x: static_cast<float>(M_PI_2), y: sinp);
495 *yaw = 2.0f * std::atan2(y: yps, x: wps);
496 *roll = 0.0f;
497 }
498
499 *pitch = qRadiansToDegrees(radians: *pitch);
500 *yaw = qRadiansToDegrees(radians: *yaw);
501 *roll = qRadiansToDegrees(radians: *roll);
502}
503
504/*!
505 \since 5.5
506
507 Creates a quaternion that corresponds to a rotation of
508 \a roll degrees around the z axis, \a pitch degrees around the x axis,
509 and \a yaw degrees around the y axis (in that order).
510
511 \sa getEulerAngles()
512*/
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(degrees: pitch);
519 yaw = qDegreesToRadians(degrees: yaw);
520 roll = qDegreesToRadians(degrees: roll);
521
522 pitch *= 0.5f;
523 yaw *= 0.5f;
524 roll *= 0.5f;
525
526 const float c1 = std::cos(x: yaw);
527 const float s1 = std::sin(x: yaw);
528 const float c2 = std::cos(x: roll);
529 const float s2 = std::sin(x: roll);
530 const float c3 = std::cos(x: pitch);
531 const float s3 = std::sin(x: 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
543/*!
544 \since 5.5
545
546 Creates a rotation matrix that corresponds to this quaternion.
547
548 \note If this quaternion is not normalized,
549 the resulting rotation matrix will contain scaling information.
550
551 \sa fromRotationMatrix(), getAxes()
552*/
553QMatrix3x3 QQuaternion::toRotationMatrix() const
554{
555 // Algorithm from:
556 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54
557
558 QMatrix3x3 rot3x3(Qt::Uninitialized);
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
586/*!
587 \since 5.5
588
589 Creates a quaternion that corresponds to a rotation matrix \a rot3x3.
590
591 \note If a given rotation matrix is not normalized,
592 the resulting quaternion will contain scaling information.
593
594 \sa toRotationMatrix(), fromAxes()
595*/
596QQuaternion QQuaternion::fromRotationMatrix(const QMatrix3x3 &rot3x3)
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(x: 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(x: 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
633/*!
634 \since 5.5
635
636 Returns the 3 orthonormal axes (\a xAxis, \a yAxis, \a zAxis) defining the quaternion.
637
638 \sa fromAxes(), toRotationMatrix()
639*/
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
651/*!
652 \since 5.5
653
654 Constructs the quaternion using 3 axes (\a xAxis, \a yAxis, \a zAxis).
655
656 \note The axes are assumed to be orthonormal.
657
658 \sa getAxes(), fromRotationMatrix()
659*/
660QQuaternion QQuaternion::fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis)
661{
662 QMatrix3x3 rot3x3(Qt::Uninitialized);
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
676/*!
677 \since 5.5
678
679 Constructs the quaternion using specified forward direction \a direction
680 and upward direction \a up.
681 If the upward direction was not specified or the forward and upward
682 vectors are collinear, a new orthonormal upward direction will be generated.
683
684 \sa fromAxes(), rotationTo()
685*/
686QQuaternion QQuaternion::fromDirection(const QVector3D &direction, const QVector3D &up)
687{
688 if (qFuzzyIsNull(f: direction.x()) && qFuzzyIsNull(f: direction.y()) && qFuzzyIsNull(f: direction.z()))
689 return QQuaternion();
690
691 const QVector3D zAxis(direction.normalized());
692 QVector3D xAxis(QVector3D::crossProduct(v1: up, v2: zAxis));
693 if (qFuzzyIsNull(f: xAxis.lengthSquared())) {
694 // collinear or invalid up vector; derive shortest arc to new direction
695 return QQuaternion::rotationTo(from: QVector3D(0.0f, 0.0f, 1.0f), to: zAxis);
696 }
697
698 xAxis.normalize();
699 const QVector3D yAxis(QVector3D::crossProduct(v1: zAxis, v2: xAxis));
700
701 return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
702}
703
704/*!
705 \since 5.5
706
707 Returns the shortest arc quaternion to rotate from the direction described by the vector \a from
708 to the direction described by the vector \a to.
709
710 \sa fromDirection()
711*/
712QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
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(v1: v0, v2: 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(f: d)) {
723 QVector3D axis = QVector3D::crossProduct(v1: QVector3D(1.0f, 0.0f, 0.0f), v2: v0);
724 if (qFuzzyIsNull(f: axis.lengthSquared()))
725 axis = QVector3D::crossProduct(v1: QVector3D(0.0f, 1.0f, 0.0f), v2: 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(x: 2.0f * d);
733 const QVector3D axis(QVector3D::crossProduct(v1: v0, v2: v1) / d);
734
735 return QQuaternion(d * 0.5f, axis).normalized();
736}
737
738#endif // QT_NO_VECTOR3D
739
740/*!
741 \fn bool QQuaternion::operator==(const QQuaternion &q1, const QQuaternion &q2)
742
743 Returns \c true if \a q1 is equal to \a q2; otherwise returns \c false.
744 This operator uses an exact floating-point comparison.
745*/
746
747/*!
748 \fn bool QQuaternion::operator!=(const QQuaternion &q1, const QQuaternion &q2)
749
750 Returns \c true if \a q1 is not equal to \a q2; otherwise returns \c false.
751 This operator uses an exact floating-point comparison.
752*/
753
754/*!
755 \fn const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2)
756 \relates QQuaternion
757
758 Returns a QQuaternion object that is the sum of the given quaternions,
759 \a q1 and \a q2; each component is added separately.
760
761 \sa QQuaternion::operator+=()
762*/
763
764/*!
765 \fn const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2)
766 \relates QQuaternion
767
768 Returns a QQuaternion object that is formed by subtracting
769 \a q2 from \a q1; each component is subtracted separately.
770
771 \sa QQuaternion::operator-=()
772*/
773
774/*!
775 \fn const QQuaternion operator*(float factor, const QQuaternion &quaternion)
776 \relates QQuaternion
777
778 Returns a copy of the given \a quaternion, multiplied by the
779 given \a factor.
780
781 \sa QQuaternion::operator*=()
782*/
783
784/*!
785 \fn const QQuaternion operator*(const QQuaternion &quaternion, float factor)
786 \relates QQuaternion
787
788 Returns a copy of the given \a quaternion, multiplied by the
789 given \a factor.
790
791 \sa QQuaternion::operator*=()
792*/
793
794/*!
795 \fn const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2)
796 \relates QQuaternion
797
798 Multiplies \a q1 and \a q2 using quaternion multiplication.
799 The result corresponds to applying both of the rotations specified
800 by \a q1 and \a q2.
801
802 \sa QQuaternion::operator*=()
803*/
804
805/*!
806 \fn const QQuaternion operator-(const QQuaternion &quaternion)
807 \relates QQuaternion
808 \overload
809
810 Returns a QQuaternion object that is formed by changing the sign of
811 all three components of the given \a quaternion.
812
813 Equivalent to \c {QQuaternion(0,0,0,0) - quaternion}.
814*/
815
816/*!
817 \fn const QQuaternion operator/(const QQuaternion &quaternion, float divisor)
818 \relates QQuaternion
819
820 Returns the QQuaternion object formed by dividing all components of
821 the given \a quaternion by the given \a divisor.
822
823 \sa QQuaternion::operator/=()
824*/
825
826#ifndef QT_NO_VECTOR3D
827
828/*!
829 \fn QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec)
830 \since 5.5
831 \relates QQuaternion
832
833 Rotates a vector \a vec with a quaternion \a quaternion to produce a new vector in 3D space.
834*/
835
836#endif
837
838/*!
839 \fn bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2)
840 \relates QQuaternion
841
842 Returns \c true if \a q1 and \a q2 are equal, allowing for a small
843 fuzziness factor for floating-point comparisons; false otherwise.
844*/
845
846/*!
847 Interpolates along the shortest spherical path between the
848 rotational positions \a q1 and \a q2. The value \a t should
849 be between 0 and 1, indicating the spherical distance to travel
850 between \a q1 and \a q2.
851
852 If \a t is less than or equal to 0, then \a q1 will be returned.
853 If \a t is greater than or equal to 1, then \a q2 will be returned.
854
855 \sa nlerp()
856*/
857QQuaternion QQuaternion::slerp
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(x: dot);
880 float sinOfAngle = std::sin(x: angle);
881 if (sinOfAngle > 0.0000001) {
882 factor1 = std::sin(x: (1.0f - t) * angle) / sinOfAngle;
883 factor2 = std::sin(x: t * angle) / sinOfAngle;
884 }
885 }
886
887 // Construct the result quaternion.
888 return q1 * factor1 + q2b * factor2;
889}
890
891/*!
892 Interpolates along the shortest linear path between the rotational
893 positions \a q1 and \a q2. The value \a t should be between 0 and 1,
894 indicating the distance to travel between \a q1 and \a q2.
895 The result will be normalized().
896
897 If \a t is less than or equal to 0, then \a q1 will be returned.
898 If \a t is greater than or equal to 1, then \a q2 will be returned.
899
900 The nlerp() function is typically faster than slerp() and will
901 give approximate results to spherical interpolation that are
902 good enough for some applications.
903
904 \sa slerp()
905*/
906QQuaternion QQuaternion::nlerp
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
925/*!
926 Returns the quaternion as a QVariant.
927*/
928QQuaternion::operator QVariant() const
929{
930 return QVariant::fromValue(value: *this);
931}
932
933#ifndef QT_NO_DEBUG_STREAM
934
935QDebug operator<<(QDebug dbg, const QQuaternion &q)
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
948/*!
949 \fn QDataStream &operator<<(QDataStream &stream, const QQuaternion &quaternion)
950 \relates QQuaternion
951
952 Writes the given \a quaternion to the given \a stream and returns a
953 reference to the stream.
954
955 \sa {Serializing Qt Data Types}
956*/
957
958QDataStream &operator<<(QDataStream &stream, const QQuaternion &quaternion)
959{
960 stream << quaternion.scalar() << quaternion.x()
961 << quaternion.y() << quaternion.z();
962 return stream;
963}
964
965/*!
966 \fn QDataStream &operator>>(QDataStream &stream, QQuaternion &quaternion)
967 \relates QQuaternion
968
969 Reads a quaternion from the given \a stream into the given \a quaternion
970 and returns a reference to the stream.
971
972 \sa {Serializing Qt Data Types}
973*/
974
975QDataStream &operator>>(QDataStream &stream, QQuaternion &quaternion)
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
993QT_END_NAMESPACE
994

source code of qtbase/src/gui/math3d/qquaternion.cpp