/*========================================================================= Program: Visualization Toolkit Module: TestQuaternion.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkSetGet.h" #include "vtkQuaternion.h" #include "vtkMathUtilities.h" // Pre-declarations of the test functions static int TestQuaternionSetGet(); static int TestQuaternionNormalization(); static int TestQuaternionConjugationAndInversion(); static int TestQuaternionRotation(); static int TestQuaternionMatrixConversions(); static int TestQuaternionConversions(); static int TestQuaternionSlerp(); //---------------------------------------------------------------------------- int TestQuaternion(int, char*[]) { // Store up any errors, return non-zero if something fails. int retVal = 0; retVal += TestQuaternionSetGet(); retVal += TestQuaternionNormalization(); retVal += TestQuaternionConjugationAndInversion(); retVal += TestQuaternionRotation(); retVal += TestQuaternionMatrixConversions(); retVal += TestQuaternionConversions(); retVal += TestQuaternionSlerp(); return retVal; } // Test if the access and set methods are valids //---------------------------------------------------------------------------- int TestQuaternionSetGet() //use of vtkQuaternionf for this test { int retVal = 0; // // Test out the general vector data types, give nice API and great memory use vtkQuaternionf qf(1.0f); float zeroArrayf[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; qf.Set(zeroArrayf[0], zeroArrayf[1], zeroArrayf[2], zeroArrayf[3]); if (sizeof(qf) != sizeof(zeroArrayf)) { // The two should be the same size and memory layout - error out if not std::cerr << "vtkQuaternionf should be the same size as float[4]." << std::endl << "sizeof(vtkQuaternionf) = " << sizeof(qf) << std::endl << "sizeof(float[4]) = " << sizeof(zeroArrayf) << std::endl; ++retVal; } if (qf.GetSize() != 4) { std::cerr << "Incorrect size of vtkQuaternionf, should be 4, but is " << qf.GetSize() << std::endl; ++retVal; } // // Test out vtkQuaternionf and ensure the various access methods are the same qf.Set(0.0f, 6.0f, 9.0f, 15.0f); if (qf.GetW() != qf[0] || !vtkMathUtilities::FuzzyCompare(qf.GetW(), 0.0f)) { std::cerr << "qf.GetW() should equal qf.GetData()[0] which should equal 0." << "\nqf.W() = " << qf.GetW() << std::endl << "qf[0] = " << qf[0] << std::endl; ++retVal; } if (qf.GetX() != qf[1] || !vtkMathUtilities::FuzzyCompare(qf.GetX(), 6.0f)) { std::cerr << "qf.GetX() should equal qf.GetData()[1] " << "which should equal 6.0. \nqf.GetX() = " << qf.GetX() << std::endl << "qf[1] = " << qf[1] << std::endl; ++retVal; } if (qf.GetY() != qf[2] || !vtkMathUtilities::FuzzyCompare(qf.GetY(), 9.0f)) { std::cerr << "qf.GetY() should equal qf.GetData()[2]" <<" which should equal 9.0.\nqf.GetY() = " << qf.GetY() < q1; // quaternion which represents a small rotation vtkQuaternion dq; // q2 is obtained by doing dq*q1 vtkQuaternion q2; // dqt is the rotation to multiply with q1 // to obtained the SLERP interpolation of q1 and q2 vtkQuaternion dqt; // qTruth is the result of dqt*q1 vtkQuaternion qTruth; // qSlerp is the result of the SLERP interpolation // it should be equal to qTruth vtkQuaternion qSlerp; // exhaustive test : 250000 operations // Control the sampling of rotation's axis const int M = 5; // Control the sampling of the rotation's angle const int L = 10; // Control the sampling of the interpolation const int N = 20; // axis coordinates step double dAxis = 1.0 / static_cast(M); // angle step double dAngle = 360.0 / static_cast(L); // interpolation step double dt = 1.0 / static_cast(N); double x, y, z, angle, t, distance, angleShort; double axis[3]; double axisNorme; // loop over x-coordinates for (int i = 1; i <= M; ++i) { x = static_cast(i) * dAxis; // loop over y-coordinates for (int j = 1; j <= M; ++j) { y = static_cast(j) * dAxis; // loop over z-coordinates for (int k = 1; k <= M; ++k) { z = static_cast(k) * dAxis; axisNorme = sqrt(x * x + y * y + z * z); axis[0] = x / axisNorme; axis[1] = y / axisNorme; axis[2] = z / axisNorme; // loop over the angle of q1 for (int u = 1; u <= L; ++u) { angle = static_cast(u) * dAngle; q1.SetRotationAngleAndAxis(vtkMath::RadiansFromDegrees(angle), axis[0], axis[1], axis[2]); // loop over the angle of dq for (int v = 1; v < L; ++v) { angleShort = (static_cast(v) * dAngle) / 2; dq.SetRotationAngleAndAxis(vtkMath::RadiansFromDegrees(angleShort), axis[0], axis[1], axis[2]); q2 = dq * q1; // loop over the interpolation step for (int w = 0; w <= N; ++w) { t = static_cast(w) * dt; dqt.SetRotationAngleAndAxis(vtkMath::RadiansFromDegrees(t * angleShort), axis[0], axis[1], axis[2]); qTruth = dqt * q1; qSlerp = q1.Slerp(t, q2); distance = (qSlerp - qTruth).Norm(); if (distance > 1e-12) { ++retVal; } } } } } } } // Particular test : we test that the SLERP take the // short path double u[3] = {-0.54, -0.0321, 1}; double normU = sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]); u[0] /= normU; u[1] /= normU; u[2] /= normU; // interpolation step const int N2 = 1000; double dtheta = 3.0; // Set q1 close to the angle boundary q1.SetRotationAngleAndAxis(vtkMath::RadiansFromDegrees(359.5), u[0], u[1], u[2]); // dq represents a small rotation dq.SetRotationAngleAndAxis(vtkMath::RadiansFromDegrees(dtheta), u[0], u[1], u[2]); // q2 is a rotation close to q1 but the quaternion representant is far q2 = dq * q1; dt = 1.0 / static_cast(N2); for (int i = 0; i <= N2; ++i) { t = static_cast(i) * dt; dqt.SetRotationAngleAndAxis(vtkMath::RadiansFromDegrees(t * dtheta), u[0], u[1], u[2]); qTruth = dqt * q1; qSlerp = q1.Slerp(t, q2); distance = (qSlerp - qTruth).Norm(); if (distance > 1e-12) { ++retVal; } } if (retVal != 0 ) { std::cerr << "Error TestQuaternionSlerp() failed" << std::endl; } return retVal; }