tests/auto/qquaternion/tst_qquaternion.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the test suite of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <QtTest/QtTest>
       
    43 #include <QtCore/qmath.h>
       
    44 #include <QtGui/qquaternion.h>
       
    45 
       
    46 class tst_QQuaternion : public QObject
       
    47 {
       
    48     Q_OBJECT
       
    49 public:
       
    50     tst_QQuaternion() {}
       
    51     ~tst_QQuaternion() {}
       
    52 
       
    53 private slots:
       
    54     void create();
       
    55 
       
    56     void length_data();
       
    57     void length();
       
    58 
       
    59     void normalized_data();
       
    60     void normalized();
       
    61 
       
    62     void normalize_data();
       
    63     void normalize();
       
    64 
       
    65     void compare();
       
    66 
       
    67     void add_data();
       
    68     void add();
       
    69 
       
    70     void subtract_data();
       
    71     void subtract();
       
    72 
       
    73     void multiply_data();
       
    74     void multiply();
       
    75 
       
    76     void multiplyFactor_data();
       
    77     void multiplyFactor();
       
    78 
       
    79     void divide_data();
       
    80     void divide();
       
    81 
       
    82     void negate_data();
       
    83     void negate();
       
    84 
       
    85     void conjugate_data();
       
    86     void conjugate();
       
    87 
       
    88     void fromAxisAndAngle_data();
       
    89     void fromAxisAndAngle();
       
    90 
       
    91     void slerp_data();
       
    92     void slerp();
       
    93 
       
    94     void nlerp_data();
       
    95     void nlerp();
       
    96 
       
    97     void properties();
       
    98     void metaTypes();
       
    99 };
       
   100 
       
   101 // QVector3D uses float internally, which can lead to some precision
       
   102 // issues when using it with the qreal-based QQuaternion.
       
   103 static bool fuzzyCompare(qreal x, qreal y)
       
   104 {
       
   105     return qFuzzyIsNull(float(x - y));
       
   106 }
       
   107 
       
   108 // Test the creation of QQuaternion objects in various ways:
       
   109 // construct, copy, and modify.
       
   110 void tst_QQuaternion::create()
       
   111 {
       
   112     QQuaternion identity;
       
   113     QCOMPARE(identity.x(), (qreal)0.0f);
       
   114     QCOMPARE(identity.y(), (qreal)0.0f);
       
   115     QCOMPARE(identity.z(), (qreal)0.0f);
       
   116     QCOMPARE(identity.scalar(), (qreal)1.0f);
       
   117     QVERIFY(identity.isIdentity());
       
   118 
       
   119     QQuaternion v1(34.0f, 1.0f, 2.5f, -89.25f);
       
   120     QCOMPARE(v1.x(), (qreal)1.0f);
       
   121     QCOMPARE(v1.y(), (qreal)2.5f);
       
   122     QCOMPARE(v1.z(), (qreal)-89.25f);
       
   123     QCOMPARE(v1.scalar(), (qreal)34.0f);
       
   124     QVERIFY(!v1.isNull());
       
   125 
       
   126     QQuaternion v1i(34, 1, 2, -89);
       
   127     QCOMPARE(v1i.x(), (qreal)1.0f);
       
   128     QCOMPARE(v1i.y(), (qreal)2.0f);
       
   129     QCOMPARE(v1i.z(), (qreal)-89.0f);
       
   130     QCOMPARE(v1i.scalar(), (qreal)34.0f);
       
   131     QVERIFY(!v1i.isNull());
       
   132 
       
   133     QQuaternion v2(v1);
       
   134     QCOMPARE(v2.x(), (qreal)1.0f);
       
   135     QCOMPARE(v2.y(), (qreal)2.5f);
       
   136     QCOMPARE(v2.z(), (qreal)-89.25f);
       
   137     QCOMPARE(v2.scalar(), (qreal)34.0f);
       
   138     QVERIFY(!v2.isNull());
       
   139 
       
   140     QQuaternion v4;
       
   141     QCOMPARE(v4.x(), (qreal)0.0f);
       
   142     QCOMPARE(v4.y(), (qreal)0.0f);
       
   143     QCOMPARE(v4.z(), (qreal)0.0f);
       
   144     QCOMPARE(v4.scalar(), (qreal)1.0f);
       
   145     QVERIFY(v4.isIdentity());
       
   146     v4 = v1;
       
   147     QCOMPARE(v4.x(), (qreal)1.0f);
       
   148     QCOMPARE(v4.y(), (qreal)2.5f);
       
   149     QCOMPARE(v4.z(), (qreal)-89.25f);
       
   150     QCOMPARE(v4.scalar(), (qreal)34.0f);
       
   151     QVERIFY(!v4.isNull());
       
   152 
       
   153     QQuaternion v9(34, QVector3D(1.0f, 2.5f, -89.25f));
       
   154     QCOMPARE(v9.x(), (qreal)1.0f);
       
   155     QCOMPARE(v9.y(), (qreal)2.5f);
       
   156     QCOMPARE(v9.z(), (qreal)-89.25f);
       
   157     QCOMPARE(v9.scalar(), (qreal)34.0f);
       
   158     QVERIFY(!v9.isNull());
       
   159 
       
   160     v1.setX(3.0f);
       
   161     QCOMPARE(v1.x(), (qreal)3.0f);
       
   162     QCOMPARE(v1.y(), (qreal)2.5f);
       
   163     QCOMPARE(v1.z(), (qreal)-89.25f);
       
   164     QCOMPARE(v1.scalar(), (qreal)34.0f);
       
   165     QVERIFY(!v1.isNull());
       
   166 
       
   167     v1.setY(10.5f);
       
   168     QCOMPARE(v1.x(), (qreal)3.0f);
       
   169     QCOMPARE(v1.y(), (qreal)10.5f);
       
   170     QCOMPARE(v1.z(), (qreal)-89.25f);
       
   171     QCOMPARE(v1.scalar(), (qreal)34.0f);
       
   172     QVERIFY(!v1.isNull());
       
   173 
       
   174     v1.setZ(15.5f);
       
   175     QCOMPARE(v1.x(), (qreal)3.0f);
       
   176     QCOMPARE(v1.y(), (qreal)10.5f);
       
   177     QCOMPARE(v1.z(), (qreal)15.5f);
       
   178     QCOMPARE(v1.scalar(), (qreal)34.0f);
       
   179     QVERIFY(!v1.isNull());
       
   180 
       
   181     v1.setScalar(6.0f);
       
   182     QCOMPARE(v1.x(), (qreal)3.0f);
       
   183     QCOMPARE(v1.y(), (qreal)10.5f);
       
   184     QCOMPARE(v1.z(), (qreal)15.5f);
       
   185     QCOMPARE(v1.scalar(), (qreal)6.0f);
       
   186     QVERIFY(!v1.isNull());
       
   187 
       
   188     v1.setVector(2.0f, 6.5f, -1.25f);
       
   189     QCOMPARE(v1.x(), (qreal)2.0f);
       
   190     QCOMPARE(v1.y(), (qreal)6.5f);
       
   191     QCOMPARE(v1.z(), (qreal)-1.25f);
       
   192     QCOMPARE(v1.scalar(), (qreal)6.0f);
       
   193     QVERIFY(!v1.isNull());
       
   194     QVERIFY(v1.vector() == QVector3D(2.0f, 6.5f, -1.25f));
       
   195 
       
   196     v1.setVector(QVector3D(-2.0f, -6.5f, 1.25f));
       
   197     QCOMPARE(v1.x(), (qreal)-2.0f);
       
   198     QCOMPARE(v1.y(), (qreal)-6.5f);
       
   199     QCOMPARE(v1.z(), (qreal)1.25f);
       
   200     QCOMPARE(v1.scalar(), (qreal)6.0f);
       
   201     QVERIFY(!v1.isNull());
       
   202     QVERIFY(v1.vector() == QVector3D(-2.0f, -6.5f, 1.25f));
       
   203 
       
   204     v1.setX(0.0f);
       
   205     v1.setY(0.0f);
       
   206     v1.setZ(0.0f);
       
   207     v1.setScalar(0.0f);
       
   208     QCOMPARE(v1.x(), (qreal)0.0f);
       
   209     QCOMPARE(v1.y(), (qreal)0.0f);
       
   210     QCOMPARE(v1.z(), (qreal)0.0f);
       
   211     QCOMPARE(v1.scalar(), (qreal)0.0f);
       
   212     QVERIFY(v1.isNull());
       
   213 
       
   214     QVector4D v10 = v9.toVector4D();
       
   215     QCOMPARE(v10.x(), (qreal)1.0f);
       
   216     QCOMPARE(v10.y(), (qreal)2.5f);
       
   217     QCOMPARE(v10.z(), (qreal)-89.25f);
       
   218     QCOMPARE(v10.w(), (qreal)34.0f);
       
   219 }
       
   220 
       
   221 // Test length computation for quaternions.
       
   222 void tst_QQuaternion::length_data()
       
   223 {
       
   224     QTest::addColumn<qreal>("x");
       
   225     QTest::addColumn<qreal>("y");
       
   226     QTest::addColumn<qreal>("z");
       
   227     QTest::addColumn<qreal>("w");
       
   228     QTest::addColumn<qreal>("len");
       
   229 
       
   230     QTest::newRow("null") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f;
       
   231     QTest::newRow("1x") << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f;
       
   232     QTest::newRow("1y") << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f;
       
   233     QTest::newRow("1z") << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)1.0f;
       
   234     QTest::newRow("1w") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)1.0f;
       
   235     QTest::newRow("-1x") << (qreal)-1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f;
       
   236     QTest::newRow("-1y") << (qreal)0.0f << (qreal)-1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f;
       
   237     QTest::newRow("-1z") << (qreal)0.0f << (qreal)0.0f << (qreal)-1.0f << (qreal)0.0f << (qreal)1.0f;
       
   238     QTest::newRow("-1w") << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)-1.0f << (qreal)1.0f;
       
   239     QTest::newRow("two") << (qreal)2.0f << (qreal)-2.0f << (qreal)2.0f << (qreal)2.0f << (qreal)qSqrt(16.0f);
       
   240 }
       
   241 void tst_QQuaternion::length()
       
   242 {
       
   243     QFETCH(qreal, x);
       
   244     QFETCH(qreal, y);
       
   245     QFETCH(qreal, z);
       
   246     QFETCH(qreal, w);
       
   247     QFETCH(qreal, len);
       
   248 
       
   249     QQuaternion v(w, x, y, z);
       
   250     QCOMPARE(v.length(), len);
       
   251     QCOMPARE(v.lengthSquared(), x * x + y * y + z * z + w * w);
       
   252 }
       
   253 
       
   254 // Test the unit vector conversion for quaternions.
       
   255 void tst_QQuaternion::normalized_data()
       
   256 {
       
   257     // Use the same test data as the length test.
       
   258     length_data();
       
   259 }
       
   260 void tst_QQuaternion::normalized()
       
   261 {
       
   262     QFETCH(qreal, x);
       
   263     QFETCH(qreal, y);
       
   264     QFETCH(qreal, z);
       
   265     QFETCH(qreal, w);
       
   266     QFETCH(qreal, len);
       
   267 
       
   268     QQuaternion v(w, x, y, z);
       
   269     QQuaternion u = v.normalized();
       
   270     if (v.isNull())
       
   271         QVERIFY(u.isNull());
       
   272     else
       
   273         QCOMPARE(u.length(), qreal(1.0f));
       
   274     QCOMPARE(u.x() * len, v.x());
       
   275     QCOMPARE(u.y() * len, v.y());
       
   276     QCOMPARE(u.z() * len, v.z());
       
   277     QCOMPARE(u.scalar() * len, v.scalar());
       
   278 }
       
   279 
       
   280 // Test the unit vector conversion for quaternions.
       
   281 void tst_QQuaternion::normalize_data()
       
   282 {
       
   283     // Use the same test data as the length test.
       
   284     length_data();
       
   285 }
       
   286 void tst_QQuaternion::normalize()
       
   287 {
       
   288     QFETCH(qreal, x);
       
   289     QFETCH(qreal, y);
       
   290     QFETCH(qreal, z);
       
   291     QFETCH(qreal, w);
       
   292 
       
   293     QQuaternion v(w, x, y, z);
       
   294     bool isNull = v.isNull();
       
   295     v.normalize();
       
   296     if (isNull)
       
   297         QVERIFY(v.isNull());
       
   298     else
       
   299         QCOMPARE(v.length(), qreal(1.0f));
       
   300 }
       
   301 
       
   302 // Test the comparison operators for quaternions.
       
   303 void tst_QQuaternion::compare()
       
   304 {
       
   305     QQuaternion v1(8, 1, 2, 4);
       
   306     QQuaternion v2(8, 1, 2, 4);
       
   307     QQuaternion v3(8, 3, 2, 4);
       
   308     QQuaternion v4(8, 1, 3, 4);
       
   309     QQuaternion v5(8, 1, 2, 3);
       
   310     QQuaternion v6(3, 1, 2, 4);
       
   311 
       
   312     QVERIFY(v1 == v2);
       
   313     QVERIFY(v1 != v3);
       
   314     QVERIFY(v1 != v4);
       
   315     QVERIFY(v1 != v5);
       
   316     QVERIFY(v1 != v6);
       
   317 }
       
   318 
       
   319 // Test addition for quaternions.
       
   320 void tst_QQuaternion::add_data()
       
   321 {
       
   322     QTest::addColumn<qreal>("x1");
       
   323     QTest::addColumn<qreal>("y1");
       
   324     QTest::addColumn<qreal>("z1");
       
   325     QTest::addColumn<qreal>("w1");
       
   326     QTest::addColumn<qreal>("x2");
       
   327     QTest::addColumn<qreal>("y2");
       
   328     QTest::addColumn<qreal>("z2");
       
   329     QTest::addColumn<qreal>("w2");
       
   330     QTest::addColumn<qreal>("x3");
       
   331     QTest::addColumn<qreal>("y3");
       
   332     QTest::addColumn<qreal>("z3");
       
   333     QTest::addColumn<qreal>("w3");
       
   334 
       
   335     QTest::newRow("null")
       
   336         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f
       
   337         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f
       
   338         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f;
       
   339 
       
   340     QTest::newRow("xonly")
       
   341         << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f
       
   342         << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f
       
   343         << (qreal)3.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f;
       
   344 
       
   345     QTest::newRow("yonly")
       
   346         << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f
       
   347         << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f
       
   348         << (qreal)0.0f << (qreal)3.0f << (qreal)0.0f << (qreal)0.0f;
       
   349 
       
   350     QTest::newRow("zonly")
       
   351         << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f
       
   352         << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f
       
   353         << (qreal)0.0f << (qreal)0.0f << (qreal)3.0f << (qreal)0.0f;
       
   354 
       
   355     QTest::newRow("wonly")
       
   356         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f
       
   357         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f
       
   358         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)3.0f;
       
   359 
       
   360     QTest::newRow("all")
       
   361         << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f << (qreal)8.0f
       
   362         << (qreal)4.0f << (qreal)5.0f << (qreal)-6.0f << (qreal)9.0f
       
   363         << (qreal)5.0f << (qreal)7.0f << (qreal)-3.0f << (qreal)17.0f;
       
   364 }
       
   365 void tst_QQuaternion::add()
       
   366 {
       
   367     QFETCH(qreal, x1);
       
   368     QFETCH(qreal, y1);
       
   369     QFETCH(qreal, z1);
       
   370     QFETCH(qreal, w1);
       
   371     QFETCH(qreal, x2);
       
   372     QFETCH(qreal, y2);
       
   373     QFETCH(qreal, z2);
       
   374     QFETCH(qreal, w2);
       
   375     QFETCH(qreal, x3);
       
   376     QFETCH(qreal, y3);
       
   377     QFETCH(qreal, z3);
       
   378     QFETCH(qreal, w3);
       
   379 
       
   380     QQuaternion v1(w1, x1, y1, z1);
       
   381     QQuaternion v2(w2, x2, y2, z2);
       
   382     QQuaternion v3(w3, x3, y3, z3);
       
   383 
       
   384     QVERIFY((v1 + v2) == v3);
       
   385 
       
   386     QQuaternion v4(v1);
       
   387     v4 += v2;
       
   388     QVERIFY(v4 == v3);
       
   389 
       
   390     QCOMPARE(v4.x(), v1.x() + v2.x());
       
   391     QCOMPARE(v4.y(), v1.y() + v2.y());
       
   392     QCOMPARE(v4.z(), v1.z() + v2.z());
       
   393     QCOMPARE(v4.scalar(), v1.scalar() + v2.scalar());
       
   394 }
       
   395 
       
   396 // Test subtraction for quaternions.
       
   397 void tst_QQuaternion::subtract_data()
       
   398 {
       
   399     // Use the same test data as the add test.
       
   400     add_data();
       
   401 }
       
   402 void tst_QQuaternion::subtract()
       
   403 {
       
   404     QFETCH(qreal, x1);
       
   405     QFETCH(qreal, y1);
       
   406     QFETCH(qreal, z1);
       
   407     QFETCH(qreal, w1);
       
   408     QFETCH(qreal, x2);
       
   409     QFETCH(qreal, y2);
       
   410     QFETCH(qreal, z2);
       
   411     QFETCH(qreal, w2);
       
   412     QFETCH(qreal, x3);
       
   413     QFETCH(qreal, y3);
       
   414     QFETCH(qreal, z3);
       
   415     QFETCH(qreal, w3);
       
   416 
       
   417     QQuaternion v1(w1, x1, y1, z1);
       
   418     QQuaternion v2(w2, x2, y2, z2);
       
   419     QQuaternion v3(w3, x3, y3, z3);
       
   420 
       
   421     QVERIFY((v3 - v1) == v2);
       
   422     QVERIFY((v3 - v2) == v1);
       
   423 
       
   424     QQuaternion v4(v3);
       
   425     v4 -= v1;
       
   426     QVERIFY(v4 == v2);
       
   427 
       
   428     QCOMPARE(v4.x(), v3.x() - v1.x());
       
   429     QCOMPARE(v4.y(), v3.y() - v1.y());
       
   430     QCOMPARE(v4.z(), v3.z() - v1.z());
       
   431     QCOMPARE(v4.scalar(), v3.scalar() - v1.scalar());
       
   432 
       
   433     QQuaternion v5(v3);
       
   434     v5 -= v2;
       
   435     QVERIFY(v5 == v1);
       
   436 
       
   437     QCOMPARE(v5.x(), v3.x() - v2.x());
       
   438     QCOMPARE(v5.y(), v3.y() - v2.y());
       
   439     QCOMPARE(v5.z(), v3.z() - v2.z());
       
   440     QCOMPARE(v5.scalar(), v3.scalar() - v2.scalar());
       
   441 }
       
   442 
       
   443 // Test quaternion multiplication.
       
   444 void tst_QQuaternion::multiply_data()
       
   445 {
       
   446     QTest::addColumn<qreal>("x1");
       
   447     QTest::addColumn<qreal>("y1");
       
   448     QTest::addColumn<qreal>("z1");
       
   449     QTest::addColumn<qreal>("w1");
       
   450     QTest::addColumn<qreal>("x2");
       
   451     QTest::addColumn<qreal>("y2");
       
   452     QTest::addColumn<qreal>("z2");
       
   453     QTest::addColumn<qreal>("w2");
       
   454 
       
   455     QTest::newRow("null")
       
   456         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f
       
   457         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f;
       
   458 
       
   459     QTest::newRow("unitvec")
       
   460         << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f
       
   461         << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)1.0f;
       
   462 
       
   463     QTest::newRow("complex")
       
   464         << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f << (qreal)7.0f
       
   465         << (qreal)4.0f << (qreal)5.0f << (qreal)6.0f << (qreal)8.0f;
       
   466 
       
   467     for (qreal w = -1.0f; w <= 1.0f; w += 0.5f)
       
   468         for (qreal x = -1.0f; x <= 1.0f; x += 0.5f)
       
   469             for (qreal y = -1.0f; y <= 1.0f; y += 0.5f)
       
   470                 for (qreal z = -1.0f; z <= 1.0f; z += 0.5f) {
       
   471                     QTest::newRow("exhaustive")
       
   472                         << x << y << z << w
       
   473                         << z << w << y << x;
       
   474                 }
       
   475 }
       
   476 void tst_QQuaternion::multiply()
       
   477 {
       
   478     QFETCH(qreal, x1);
       
   479     QFETCH(qreal, y1);
       
   480     QFETCH(qreal, z1);
       
   481     QFETCH(qreal, w1);
       
   482     QFETCH(qreal, x2);
       
   483     QFETCH(qreal, y2);
       
   484     QFETCH(qreal, z2);
       
   485     QFETCH(qreal, w2);
       
   486 
       
   487     QQuaternion q1(w1, x1, y1, z1);
       
   488     QQuaternion q2(w2, x2, y2, z2);
       
   489 
       
   490     // Use the simple algorithm at:
       
   491     // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q53
       
   492     // to calculate the answer we expect to get.
       
   493     QVector3D v1(x1, y1, z1);
       
   494     QVector3D v2(x2, y2, z2);
       
   495     qreal scalar = w1 * w2 - QVector3D::dotProduct(v1, v2);
       
   496     QVector3D vector = w1 * v2 + w2 * v1 + QVector3D::crossProduct(v1, v2);
       
   497     QQuaternion result(scalar, vector);
       
   498 
       
   499     QVERIFY((q1 * q2) == result);
       
   500 }
       
   501 
       
   502 // Test multiplication by a factor for quaternions.
       
   503 void tst_QQuaternion::multiplyFactor_data()
       
   504 {
       
   505     QTest::addColumn<qreal>("x1");
       
   506     QTest::addColumn<qreal>("y1");
       
   507     QTest::addColumn<qreal>("z1");
       
   508     QTest::addColumn<qreal>("w1");
       
   509     QTest::addColumn<qreal>("factor");
       
   510     QTest::addColumn<qreal>("x2");
       
   511     QTest::addColumn<qreal>("y2");
       
   512     QTest::addColumn<qreal>("z2");
       
   513     QTest::addColumn<qreal>("w2");
       
   514 
       
   515     QTest::newRow("null")
       
   516         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f
       
   517         << (qreal)100.0f
       
   518         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f;
       
   519 
       
   520     QTest::newRow("xonly")
       
   521         << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f
       
   522         << (qreal)2.0f
       
   523         << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f;
       
   524 
       
   525     QTest::newRow("yonly")
       
   526         << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f
       
   527         << (qreal)2.0f
       
   528         << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f << (qreal)0.0f;
       
   529 
       
   530     QTest::newRow("zonly")
       
   531         << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f
       
   532         << (qreal)2.0f
       
   533         << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f << (qreal)0.0f;
       
   534 
       
   535     QTest::newRow("wonly")
       
   536         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f
       
   537         << (qreal)2.0f
       
   538         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)2.0f;
       
   539 
       
   540     QTest::newRow("all")
       
   541         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)4.0f
       
   542         << (qreal)2.0f
       
   543         << (qreal)2.0f << (qreal)4.0f << (qreal)-6.0f << (qreal)8.0f;
       
   544 
       
   545     QTest::newRow("allzero")
       
   546         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)4.0f
       
   547         << (qreal)0.0f
       
   548         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f;
       
   549 }
       
   550 void tst_QQuaternion::multiplyFactor()
       
   551 {
       
   552     QFETCH(qreal, x1);
       
   553     QFETCH(qreal, y1);
       
   554     QFETCH(qreal, z1);
       
   555     QFETCH(qreal, w1);
       
   556     QFETCH(qreal, factor);
       
   557     QFETCH(qreal, x2);
       
   558     QFETCH(qreal, y2);
       
   559     QFETCH(qreal, z2);
       
   560     QFETCH(qreal, w2);
       
   561 
       
   562     QQuaternion v1(w1, x1, y1, z1);
       
   563     QQuaternion v2(w2, x2, y2, z2);
       
   564 
       
   565     QVERIFY((v1 * factor) == v2);
       
   566     QVERIFY((factor * v1) == v2);
       
   567 
       
   568     QQuaternion v3(v1);
       
   569     v3 *= factor;
       
   570     QVERIFY(v3 == v2);
       
   571 
       
   572     QCOMPARE(v3.x(), v1.x() * factor);
       
   573     QCOMPARE(v3.y(), v1.y() * factor);
       
   574     QCOMPARE(v3.z(), v1.z() * factor);
       
   575     QCOMPARE(v3.scalar(), v1.scalar() * factor);
       
   576 }
       
   577 
       
   578 // Test division by a factor for quaternions.
       
   579 void tst_QQuaternion::divide_data()
       
   580 {
       
   581     // Use the same test data as the multiply test.
       
   582     multiplyFactor_data();
       
   583 }
       
   584 void tst_QQuaternion::divide()
       
   585 {
       
   586     QFETCH(qreal, x1);
       
   587     QFETCH(qreal, y1);
       
   588     QFETCH(qreal, z1);
       
   589     QFETCH(qreal, w1);
       
   590     QFETCH(qreal, factor);
       
   591     QFETCH(qreal, x2);
       
   592     QFETCH(qreal, y2);
       
   593     QFETCH(qreal, z2);
       
   594     QFETCH(qreal, w2);
       
   595 
       
   596     QQuaternion v1(w1, x1, y1, z1);
       
   597     QQuaternion v2(w2, x2, y2, z2);
       
   598 
       
   599     if (factor == (qreal)0.0f)
       
   600         return;
       
   601 
       
   602     QVERIFY((v2 / factor) == v1);
       
   603 
       
   604     QQuaternion v3(v2);
       
   605     v3 /= factor;
       
   606     QVERIFY(v3 == v1);
       
   607 
       
   608     QCOMPARE(v3.x(), v2.x() / factor);
       
   609     QCOMPARE(v3.y(), v2.y() / factor);
       
   610     QCOMPARE(v3.z(), v2.z() / factor);
       
   611     QCOMPARE(v3.scalar(), v2.scalar() / factor);
       
   612 }
       
   613 
       
   614 // Test negation for quaternions.
       
   615 void tst_QQuaternion::negate_data()
       
   616 {
       
   617     // Use the same test data as the add test.
       
   618     add_data();
       
   619 }
       
   620 void tst_QQuaternion::negate()
       
   621 {
       
   622     QFETCH(qreal, x1);
       
   623     QFETCH(qreal, y1);
       
   624     QFETCH(qreal, z1);
       
   625     QFETCH(qreal, w1);
       
   626 
       
   627     QQuaternion v1(w1, x1, y1, z1);
       
   628     QQuaternion v2(-w1, -x1, -y1, -z1);
       
   629 
       
   630     QVERIFY(-v1 == v2);
       
   631 }
       
   632 
       
   633 // Test quaternion conjugate calculations.
       
   634 void tst_QQuaternion::conjugate_data()
       
   635 {
       
   636     // Use the same test data as the add test.
       
   637     add_data();
       
   638 }
       
   639 void tst_QQuaternion::conjugate()
       
   640 {
       
   641     QFETCH(qreal, x1);
       
   642     QFETCH(qreal, y1);
       
   643     QFETCH(qreal, z1);
       
   644     QFETCH(qreal, w1);
       
   645 
       
   646     QQuaternion v1(w1, x1, y1, z1);
       
   647     QQuaternion v2(w1, -x1, -y1, -z1);
       
   648 
       
   649     QVERIFY(v1.conjugate() == v2);
       
   650 }
       
   651 
       
   652 // Test quaternion creation from an axis and an angle.
       
   653 void tst_QQuaternion::fromAxisAndAngle_data()
       
   654 {
       
   655     QTest::addColumn<qreal>("x1");
       
   656     QTest::addColumn<qreal>("y1");
       
   657     QTest::addColumn<qreal>("z1");
       
   658     QTest::addColumn<qreal>("angle");
       
   659 
       
   660     QTest::newRow("null")
       
   661         << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f;
       
   662 
       
   663     QTest::newRow("xonly")
       
   664         << (qreal)1.0f << (qreal)0.0f << (qreal)0.0f << (qreal)90.0f;
       
   665 
       
   666     QTest::newRow("yonly")
       
   667         << (qreal)0.0f << (qreal)1.0f << (qreal)0.0f << (qreal)180.0f;
       
   668 
       
   669     QTest::newRow("zonly")
       
   670         << (qreal)0.0f << (qreal)0.0f << (qreal)1.0f << (qreal)270.0f;
       
   671 
       
   672     QTest::newRow("complex")
       
   673         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)45.0f;
       
   674 }
       
   675 void tst_QQuaternion::fromAxisAndAngle()
       
   676 {
       
   677     QFETCH(qreal, x1);
       
   678     QFETCH(qreal, y1);
       
   679     QFETCH(qreal, z1);
       
   680     QFETCH(qreal, angle);
       
   681 
       
   682     // Use a straight-forward implementation of the algorithm at:
       
   683     // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56
       
   684     // to calculate the answer we expect to get.
       
   685     QVector3D vector = QVector3D(x1, y1, z1).normalized();
       
   686     qreal sin_a = qSin((angle * M_PI / 180.0) / 2.0);
       
   687     qreal cos_a = qCos((angle * M_PI / 180.0) / 2.0);
       
   688     QQuaternion result((qreal)cos_a,
       
   689                        (qreal)(vector.x() * sin_a),
       
   690                        (qreal)(vector.y() * sin_a),
       
   691                        (qreal)(vector.z() * sin_a));
       
   692     result = result.normalized();
       
   693 
       
   694     QQuaternion answer = QQuaternion::fromAxisAndAngle(QVector3D(x1, y1, z1), angle);
       
   695     QVERIFY(fuzzyCompare(answer.x(), result.x()));
       
   696     QVERIFY(fuzzyCompare(answer.y(), result.y()));
       
   697     QVERIFY(fuzzyCompare(answer.z(), result.z()));
       
   698     QVERIFY(fuzzyCompare(answer.scalar(), result.scalar()));
       
   699 
       
   700     answer = QQuaternion::fromAxisAndAngle(x1, y1, z1, angle);
       
   701     QVERIFY(fuzzyCompare(answer.x(), result.x()));
       
   702     QVERIFY(fuzzyCompare(answer.y(), result.y()));
       
   703     QVERIFY(fuzzyCompare(answer.z(), result.z()));
       
   704     QVERIFY(fuzzyCompare(answer.scalar(), result.scalar()));
       
   705 }
       
   706 
       
   707 // Test spherical interpolation of quaternions.
       
   708 void tst_QQuaternion::slerp_data()
       
   709 {
       
   710     QTest::addColumn<qreal>("x1");
       
   711     QTest::addColumn<qreal>("y1");
       
   712     QTest::addColumn<qreal>("z1");
       
   713     QTest::addColumn<qreal>("angle1");
       
   714     QTest::addColumn<qreal>("x2");
       
   715     QTest::addColumn<qreal>("y2");
       
   716     QTest::addColumn<qreal>("z2");
       
   717     QTest::addColumn<qreal>("angle2");
       
   718     QTest::addColumn<qreal>("t");
       
   719     QTest::addColumn<qreal>("x3");
       
   720     QTest::addColumn<qreal>("y3");
       
   721     QTest::addColumn<qreal>("z3");
       
   722     QTest::addColumn<qreal>("angle3");
       
   723 
       
   724     QTest::newRow("first")
       
   725         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f
       
   726         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f
       
   727         << (qreal)0.0f
       
   728         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f;
       
   729     QTest::newRow("first2")
       
   730         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f
       
   731         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f
       
   732         << (qreal)-0.5f
       
   733         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f;
       
   734     QTest::newRow("second")
       
   735         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f
       
   736         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f
       
   737         << (qreal)1.0f
       
   738         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f;
       
   739     QTest::newRow("second2")
       
   740         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f
       
   741         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f
       
   742         << (qreal)1.5f
       
   743         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f;
       
   744     QTest::newRow("middle")
       
   745         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)90.0f
       
   746         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)180.0f
       
   747         << (qreal)0.5f
       
   748         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)135.0f;
       
   749     QTest::newRow("wide angle")
       
   750         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)0.0f
       
   751         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)270.0f
       
   752         << (qreal)0.5f
       
   753         << (qreal)1.0f << (qreal)2.0f << (qreal)-3.0f << (qreal)-45.0f;
       
   754 }
       
   755 void tst_QQuaternion::slerp()
       
   756 {
       
   757     QFETCH(qreal, x1);
       
   758     QFETCH(qreal, y1);
       
   759     QFETCH(qreal, z1);
       
   760     QFETCH(qreal, angle1);
       
   761     QFETCH(qreal, x2);
       
   762     QFETCH(qreal, y2);
       
   763     QFETCH(qreal, z2);
       
   764     QFETCH(qreal, angle2);
       
   765     QFETCH(qreal, t);
       
   766     QFETCH(qreal, x3);
       
   767     QFETCH(qreal, y3);
       
   768     QFETCH(qreal, z3);
       
   769     QFETCH(qreal, angle3);
       
   770 
       
   771     QQuaternion q1 = QQuaternion::fromAxisAndAngle(x1, y1, z1, angle1);
       
   772     QQuaternion q2 = QQuaternion::fromAxisAndAngle(x2, y2, z2, angle2);
       
   773     QQuaternion q3 = QQuaternion::fromAxisAndAngle(x3, y3, z3, angle3);
       
   774 
       
   775     QQuaternion result = QQuaternion::slerp(q1, q2, t);
       
   776 
       
   777     QVERIFY(fuzzyCompare(result.x(), q3.x()));
       
   778     QVERIFY(fuzzyCompare(result.y(), q3.y()));
       
   779     QVERIFY(fuzzyCompare(result.z(), q3.z()));
       
   780     QVERIFY(fuzzyCompare(result.scalar(), q3.scalar()));
       
   781 }
       
   782 
       
   783 // Test normalized linear interpolation of quaternions.
       
   784 void tst_QQuaternion::nlerp_data()
       
   785 {
       
   786     slerp_data();
       
   787 }
       
   788 void tst_QQuaternion::nlerp()
       
   789 {
       
   790     QFETCH(qreal, x1);
       
   791     QFETCH(qreal, y1);
       
   792     QFETCH(qreal, z1);
       
   793     QFETCH(qreal, angle1);
       
   794     QFETCH(qreal, x2);
       
   795     QFETCH(qreal, y2);
       
   796     QFETCH(qreal, z2);
       
   797     QFETCH(qreal, angle2);
       
   798     QFETCH(qreal, t);
       
   799 
       
   800     QQuaternion q1 = QQuaternion::fromAxisAndAngle(x1, y1, z1, angle1);
       
   801     QQuaternion q2 = QQuaternion::fromAxisAndAngle(x2, y2, z2, angle2);
       
   802 
       
   803     QQuaternion result = QQuaternion::nlerp(q1, q2, t);
       
   804 
       
   805     qreal resultx, resulty, resultz, resultscalar;
       
   806     if (t <= 0.0f) {
       
   807         resultx = q1.x();
       
   808         resulty = q1.y();
       
   809         resultz = q1.z();
       
   810         resultscalar = q1.scalar();
       
   811     } else if (t >= 1.0f) {
       
   812         resultx = q2.x();
       
   813         resulty = q2.y();
       
   814         resultz = q2.z();
       
   815         resultscalar = q2.scalar();
       
   816     } else if (qAbs(angle1 - angle2) <= 180.f) {
       
   817         resultx = q1.x() * (1 - t) + q2.x() * t;
       
   818         resulty = q1.y() * (1 - t) + q2.y() * t;
       
   819         resultz = q1.z() * (1 - t) + q2.z() * t;
       
   820         resultscalar = q1.scalar() * (1 - t) + q2.scalar() * t;
       
   821     } else {
       
   822         // Angle greater than 180 degrees: negate q2.
       
   823         resultx = q1.x() * (1 - t) - q2.x() * t;
       
   824         resulty = q1.y() * (1 - t) - q2.y() * t;
       
   825         resultz = q1.z() * (1 - t) - q2.z() * t;
       
   826         resultscalar = q1.scalar() * (1 - t) - q2.scalar() * t;
       
   827     }
       
   828 
       
   829     QQuaternion q3 = QQuaternion(resultscalar, resultx, resulty, resultz).normalized();
       
   830 
       
   831     QVERIFY(fuzzyCompare(result.x(), q3.x()));
       
   832     QVERIFY(fuzzyCompare(result.y(), q3.y()));
       
   833     QVERIFY(fuzzyCompare(result.z(), q3.z()));
       
   834     QVERIFY(fuzzyCompare(result.scalar(), q3.scalar()));
       
   835 }
       
   836 
       
   837 class tst_QQuaternionProperties : public QObject
       
   838 {
       
   839     Q_OBJECT
       
   840     Q_PROPERTY(QQuaternion quaternion READ quaternion WRITE setQuaternion)
       
   841 public:
       
   842     tst_QQuaternionProperties(QObject *parent = 0) : QObject(parent) {}
       
   843 
       
   844     QQuaternion quaternion() const { return q; }
       
   845     void setQuaternion(const QQuaternion& value) { q = value; }
       
   846 
       
   847 private:
       
   848     QQuaternion q;
       
   849 };
       
   850 
       
   851 // Test getting and setting quaternion properties via the metaobject system.
       
   852 void tst_QQuaternion::properties()
       
   853 {
       
   854     tst_QQuaternionProperties obj;
       
   855 
       
   856     obj.setQuaternion(QQuaternion(6.0f, 7.0f, 8.0f, 9.0f));
       
   857 
       
   858     QQuaternion q = qVariantValue<QQuaternion>(obj.property("quaternion"));
       
   859     QCOMPARE(q.scalar(), (qreal)6.0f);
       
   860     QCOMPARE(q.x(), (qreal)7.0f);
       
   861     QCOMPARE(q.y(), (qreal)8.0f);
       
   862     QCOMPARE(q.z(), (qreal)9.0f);
       
   863 
       
   864     obj.setProperty("quaternion",
       
   865                     qVariantFromValue(QQuaternion(-6.0f, -7.0f, -8.0f, -9.0f)));
       
   866 
       
   867     q = qVariantValue<QQuaternion>(obj.property("quaternion"));
       
   868     QCOMPARE(q.scalar(), (qreal)-6.0f);
       
   869     QCOMPARE(q.x(), (qreal)-7.0f);
       
   870     QCOMPARE(q.y(), (qreal)-8.0f);
       
   871     QCOMPARE(q.z(), (qreal)-9.0f);
       
   872 }
       
   873 
       
   874 void tst_QQuaternion::metaTypes()
       
   875 {
       
   876     QVERIFY(QMetaType::type("QQuaternion") == QMetaType::QQuaternion);
       
   877 
       
   878     QCOMPARE(QByteArray(QMetaType::typeName(QMetaType::QQuaternion)),
       
   879              QByteArray("QQuaternion"));
       
   880 
       
   881     QVERIFY(QMetaType::isRegistered(QMetaType::QQuaternion));
       
   882 
       
   883     QVERIFY(qMetaTypeId<QQuaternion>() == QMetaType::QQuaternion);
       
   884 }
       
   885 
       
   886 QTEST_APPLESS_MAIN(tst_QQuaternion)
       
   887 
       
   888 #include "tst_qquaternion.moc"