201 lines
5.0 KiB
C++
Raw Permalink Normal View History

2022-04-07 18:46:57 +02:00
///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2006, Industrial Light & Magic, a division of Lucas
// Digital Ltd. LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Industrial Light & Magic nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////
#include <testQuatSlerp.h>
#include "ImathQuat.h"
#include "ImathRandom.h"
#include <iostream>
#include <math.h>
#include <assert.h>
using namespace std;
using namespace IMATH_INTERNAL_NAMESPACE;
namespace {
void
compareQuats (const Quatf &q1, const Quatf &q2, float e)
{
assert (equalWithAbsError (q1.v.x, q2.v.x, e));
assert (equalWithAbsError (q1.v.y, q2.v.y, e));
assert (equalWithAbsError (q1.v.z, q2.v.z, e));
assert (equalWithAbsError (q1.r, q2.r, e));
}
Quatd
pow (const Quatd q, int n)
{
Quatd result;
for (int i = 0; i < n; ++i)
result *= q;
return result;
}
void
testSlerp (const Quatf q1, const Quatf q2, int m, int n)
{
//
// For two quaternions, q1 and q2, and the identity quaternion, qi,
//
// slerp (q1, q2, f) == q1 * slerp (qi, q1.inverse() * q2, f); (1)
//
// In addition, for integers m and n, with m >= 0, n > 0,
//
// pow (slerp (qi, q3, m/n), n) == pow (q3, m) (2)
//
// This allows us to test if slerp (q1, q2, m/n) works correctly.
// Thanks to Dan Piponi for pointing this out.
//
// Note that e2, our upper bound for the numerical error in (2) is
// fairly large. The reason for this is that testSlerp() will be
// called with m and n up to 16. Taking quaternions to the 16th
// power amplifies any inaccuracies.
//
Quatf qi;
Quatf q3 = q1.inverse() * q2;
Quatf q1q2 = slerp (q1, q2, float (m) / float (n));
Quatf qiq3 = slerp (qi, q3, float (m) / float (n));
float e1 = 60 * limits<float>::epsilon();
float e2 = 600 * limits<float>::epsilon();
compareQuats (q1q2, q1 * qiq3, e1);
compareQuats (pow (qiq3, n), pow (q3, m), e2);
}
void
testSlerp (const Quatf q1, const Quatf q2)
{
const int n = 16;
for (int m = 0; m <= n; ++m)
testSlerp (q1, q2, m, n);
}
void
specificRotations ()
{
cout << " combinations of 90-degree rotations around x, y and z" << endl;
for (int x1 = 0; x1 < 3; ++x1)
{
V3f axis1 (0, 0, 0);
axis1[x1] = 1;
for (int n1 = 0; n1 < 4; ++n1)
{
float angle1 = n1 * M_PI / 2;
Quatf q1;
q1.setAxisAngle (axis1, angle1);
for (int x2 = 0; x2 < 3; ++x2)
{
V3f axis2 (0, 0, 0);
axis2[x2] = 1;
for (int n2 = 0; n2 < 4; ++n2)
{
float angle2 = n2 * M_PI / 2;
Quatf q2;
q2.setAxisAngle (axis2, angle2);
testSlerp (q1, q2);
testSlerp (-q1, -q2);
if ((q1 ^ q2) < 0.99)
{
testSlerp (q1, -q2);
testSlerp (-q1, q2);
}
}
}
}
}
}
void
randomRotations ()
{
cout << " random rotations" << endl;
Rand48 rand (53);
for (int i = 0; i < 10000; ++i)
{
V3f axis1 = hollowSphereRand<V3f> (rand);
V3f axis2 = hollowSphereRand<V3f> (rand);
float angle1 = rand.nextf (0, M_PI);
float angle2 = rand.nextf (0, M_PI);
Quatf q1, q2;
q1.setAxisAngle (axis1, angle1);
q2.setAxisAngle (axis2, angle2);
testSlerp (q1, q2);
testSlerp (-q1, -q2);
if ((q1 ^ q2) < 0.99)
{
testSlerp (q1, -q2);
testSlerp (-q1, q2);
}
}
}
} // namespace
void
testQuatSlerp ()
{
cout << "Testing quaternion spherical linear interpolation" << endl;
specificRotations();
randomRotations();
cout << "ok\n" << endl;
}