Skip to content
Snippets Groups Projects
Select Git revision
  • 473aeebceaace8941b5f40de25b7d005a13cbab3
  • main default protected
  • Kelvinrr-patch-3
  • radius_update
  • revert-616-apollo_pan
  • vims
  • 0.10
  • Kelvinrr-patch-2
  • revert-563-minirf_fix
  • Kelvinrr-patch-1
  • 0.9
  • acpaquette-patch-3
  • acpaquette-patch-2
  • acpaquette-patch-1
  • spiceql
  • ci-coverage
  • 0.10.0
  • 0.9.1
  • 0.9.0
  • 0.8.7
  • 0.8.8
  • 0.8.6
  • 0.8.3
  • 0.8.4
  • 0.8.5
  • 0.8.2
  • 0.8.1
  • 0.8.0
  • 0.7.3
  • 0.7.2
  • 0.7.1
  • 0.7.0
  • 0.6.5
  • 0.6.4
  • 0.6.3
  • 0.6.2
36 results

RotationTests.cpp

Blame
  • RotationTests.cpp 15.16 KiB
    #include "gtest/gtest.h"
    
    #include "ale/Rotation.h"
    
    #include <cmath>
    #include <exception>
    
    using namespace std;
    using namespace ale;
    
    TEST(RotationTest, DefaultConstructor) {
      Rotation defaultRotation;
      vector<double> defaultQuat = defaultRotation.toQuaternion();
      ASSERT_EQ(defaultQuat.size(), 4);
      EXPECT_NEAR(defaultQuat[0], 1.0, 1e-10);
      EXPECT_NEAR(defaultQuat[1], 0.0, 1e-10);
      EXPECT_NEAR(defaultQuat[2], 0.0, 1e-10);
      EXPECT_NEAR(defaultQuat[3], 0.0, 1e-10);
    }
    
    TEST(RotationTest, QuaternionConstructor) {
      Rotation rotation(1.0/sqrt(2), 1.0/sqrt(2), 0.0, 0.0);
      vector<double> quat = rotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0], 1.0/sqrt(2), 1e-10);
      EXPECT_NEAR(quat[1], 1.0/sqrt(2), 1e-10);
      EXPECT_NEAR(quat[2], 0.0, 1e-10);
      EXPECT_NEAR(quat[3], 0.0, 1e-10);
    }
    
    TEST(RotationTest, UnnormalizedQuaternion) {
      Rotation rotation(1.0, 1.0, 0.0, 0.0);
      vector<double> quat = rotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      // The constructor will normalize the quaternion. 
      EXPECT_NEAR(quat[0], 1.0/sqrt(2), 1e-10);
      EXPECT_NEAR(quat[1], 1.0/sqrt(2), 1e-10);
      EXPECT_NEAR(quat[2], 0.0, 1e-10);
      EXPECT_NEAR(quat[3], 0.0, 1e-10);
    }
    
    
    TEST(RotationTest, MatrixConstructor) {
      Rotation rotation(
            {0.0, 1.0, 0.0,
             0.0, 0.0, 1.0,
             1.0, 0.0, 0.0});
      vector<double> quat = rotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0], -0.5, 1e-10);
      EXPECT_NEAR(quat[1],  0.5, 1e-10);
      EXPECT_NEAR(quat[2],  0.5, 1e-10);
      EXPECT_NEAR(quat[3],  0.5, 1e-10);
    }
    
    TEST(RotationTest, BadMatrixConstructor) {
      ASSERT_THROW(Rotation({0.0, 0.0, 1.0,
                             1.0, 0.0, 0.0,
                             0.0, 1.0, 0.0,
                             1.0, 0.0, 2.0}),
                   std::invalid_argument);
    }
    
    TEST(RotationTest, SingleAngleConstructor) {
      std::vector<double> angles;
      angles.push_back(M_PI);
      std::vector<int> axes;
      axes.push_back(0);
      Rotation rotation(angles, axes);
      vector<double> quat = rotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0], 0.0, 1e-10);
      EXPECT_NEAR(quat[1], 1.0, 1e-10);
      EXPECT_NEAR(quat[2], 0.0, 1e-10);
      EXPECT_NEAR(quat[3], 0.0, 1e-10);
    }
    
    TEST(RotationTest, MultiAngleConstructor) {
      Rotation rotation({M_PI/2, -M_PI/2, M_PI}, {0, 1, 2});
      vector<double> quat = rotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0],  0.5, 1e-10);
      EXPECT_NEAR(quat[1], -0.5, 1e-10);
      EXPECT_NEAR(quat[2], -0.5, 1e-10);
      EXPECT_NEAR(quat[3],  0.5, 1e-10);
    }
    
    TEST(RotationTest, DifferentAxisAngleCount) {
      std::vector<double> angles;
      angles.push_back(M_PI);
      std::vector<int> axes = {0, 1, 2};
      ASSERT_THROW(Rotation(angles, axes), std::invalid_argument);
    }
    
    TEST(RotationTest, EmptyAxisAngle) {
      std::vector<double> angles;
      std::vector<int> axes = {0, 1, 2};
      ASSERT_THROW(Rotation(angles, axes), std::invalid_argument);
    }
    
    TEST(RotationTest, BadAxisNumber) {
      std::vector<double> angles;
      angles.push_back(M_PI);
      std::vector<int> axes;
      axes.push_back(4);
      ASSERT_THROW(Rotation(angles, axes), std::invalid_argument);
    }
    
    TEST(RotationTest, AxisAngleConstructor) {
      Rotation rotation({1.0, 1.0, 1.0}, 2.0 / 3.0 * M_PI);
      vector<double> quat = rotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0], 0.5, 1e-10);
      EXPECT_NEAR(quat[1], 0.5, 1e-10);
      EXPECT_NEAR(quat[2], 0.5, 1e-10);
      EXPECT_NEAR(quat[3], 0.5, 1e-10);
    }
    
    TEST(RotationTest, BadAxisAngleConstructor) {
      ASSERT_THROW(Rotation({1.0, 1.0, 1.0, 1.0}, 2.0 / 3.0 * M_PI), std::invalid_argument);
    }
    
    TEST(RotationTest, ToRotationMatrix) {
      Rotation rotation(-0.5, 0.5, 0.5, 0.5);
      vector<double> mat = rotation.toRotationMatrix();
      ASSERT_EQ(mat.size(), 9);
      EXPECT_NEAR(mat[0], 0.0, 1e-10);
      EXPECT_NEAR(mat[1], 1.0, 1e-10);
      EXPECT_NEAR(mat[2], 0.0, 1e-10);
      EXPECT_NEAR(mat[3], 0.0, 1e-10);
      EXPECT_NEAR(mat[4], 0.0, 1e-10);
      EXPECT_NEAR(mat[5], 1.0, 1e-10);
      EXPECT_NEAR(mat[6], 1.0, 1e-10);
      EXPECT_NEAR(mat[7], 0.0, 1e-10);
      EXPECT_NEAR(mat[8], 0.0, 1e-10);
    }
    
    TEST(RotationTest, ToStateRotationMatrix) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      std::vector<double> av = {2.0 / 3.0 * M_PI, 2.0 / 3.0 * M_PI, 2.0 / 3.0 * M_PI};
      vector<double> mat = rotation.toStateRotationMatrix(av);
      ASSERT_EQ(mat.size(), 36);
      EXPECT_NEAR(mat[0], 0.0, 1e-10);
      EXPECT_NEAR(mat[1], 0.0, 1e-10);
      EXPECT_NEAR(mat[2], 1.0, 1e-10);
      EXPECT_NEAR(mat[3], 0.0, 1e-10);
      EXPECT_NEAR(mat[4], 0.0, 1e-10);
      EXPECT_NEAR(mat[5], 0.0, 1e-10);
    
      EXPECT_NEAR(mat[6], 1.0, 1e-10);
      EXPECT_NEAR(mat[7], 0.0, 1e-10);
      EXPECT_NEAR(mat[8], 0.0, 1e-10);
      EXPECT_NEAR(mat[9], 0.0, 1e-10);
      EXPECT_NEAR(mat[10], 0.0, 1e-10);
      EXPECT_NEAR(mat[11], 0.0, 1e-10);
    
      EXPECT_NEAR(mat[12], 0.0, 1e-10);
      EXPECT_NEAR(mat[13], 1.0, 1e-10);
      EXPECT_NEAR(mat[14], 0.0, 1e-10);
      EXPECT_NEAR(mat[15], 0.0, 1e-10);
      EXPECT_NEAR(mat[16], 0.0, 1e-10);
      EXPECT_NEAR(mat[17], 0.0, 1e-10);
    
      EXPECT_NEAR(mat[18], 2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(mat[19], -2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(mat[20], 0.0, 1e-10);
      EXPECT_NEAR(mat[21], 0.0, 1e-10);
      EXPECT_NEAR(mat[22], 0.0, 1e-10);
      EXPECT_NEAR(mat[23], 1.0, 1e-10);
    
      EXPECT_NEAR(mat[24], 0.0, 1e-10);
      EXPECT_NEAR(mat[25], 2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(mat[26], -2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(mat[27], 1.0, 1e-10);
      EXPECT_NEAR(mat[28], 0.0, 1e-10);
      EXPECT_NEAR(mat[29], 0.0, 1e-10);
    
      EXPECT_NEAR(mat[30], -2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(mat[31], 0.0, 1e-10);
      EXPECT_NEAR(mat[32], 2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(mat[33], 0.0, 1e-10);
      EXPECT_NEAR(mat[34], 1.0, 1e-10);
      EXPECT_NEAR(mat[35], 0.0, 1e-10);
    }
    
    TEST(RotationTest, BadAvVectorSize) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      std::vector<double> av = {2.0 / 3.0 * M_PI, 2.0 / 3.0 * M_PI};
      ASSERT_THROW(rotation.toStateRotationMatrix(av), std::invalid_argument);
    }
    
    TEST(RotationTest, ToEulerXYZ) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      vector<double> angles = rotation.toEuler({0, 1, 2});
      ASSERT_EQ(angles.size(), 3);
      EXPECT_NEAR(angles[0], 0.0, 1e-10);
      EXPECT_NEAR(angles[1], M_PI/2, 1e-10);
      EXPECT_NEAR(angles[2], M_PI/2, 1e-10);
    }
    
    TEST(RotationTest, ToEulerZYX) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      vector<double> angles = rotation.toEuler({2, 1, 0});
      ASSERT_EQ(angles.size(), 3);
      EXPECT_NEAR(angles[0], M_PI/2, 1e-10);
      EXPECT_NEAR(angles[1], 0.0, 1e-10);
      EXPECT_NEAR(angles[2], M_PI/2, 1e-10);
    }
    
    TEST(RotationTest, ToEulerWrongNumberOfAxes) {
      Rotation rotation;
      ASSERT_ANY_THROW(rotation.toEuler({1, 0}));
    }
    
    TEST(RotationTest, ToEulerBadAxisNumber) {
      Rotation rotation;
      ASSERT_THROW(rotation.toEuler({4, 1, 0}), std::invalid_argument);
    }
    
    TEST(RotationTest, ToAxisAngle) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      std::pair<std::vector<double>, double> axisAngle = rotation.toAxisAngle();
      ASSERT_EQ(axisAngle.first.size(), 3);
      EXPECT_NEAR(axisAngle.first[0], 1.0 / sqrt(3), 1e-10);
      EXPECT_NEAR(axisAngle.first[1], 1.0 / sqrt(3), 1e-10);
      EXPECT_NEAR(axisAngle.first[2], 1.0 / sqrt(3), 1e-10);
      EXPECT_NEAR(axisAngle.second, 2.0 / 3.0 * M_PI, 1e-10);
    }
    
    TEST(RotationTest, RotateVector) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      Vec3d unitX(1.0, 0.0, 0.0);
      Vec3d unitY(0.0, 1.0, 0.0);
      Vec3d unitZ(0.0, 0.0, 1.0);
      Vec3d rotatedX = rotation(unitX);
      Vec3d rotatedY = rotation(unitY);
      Vec3d rotatedZ = rotation(unitZ);
      EXPECT_NEAR(rotatedX.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedX.y, 1.0, 1e-10);
      EXPECT_NEAR(rotatedX.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.z, 1.0, 1e-10);
      EXPECT_NEAR(rotatedZ.x, 1.0, 1e-10);
      EXPECT_NEAR(rotatedZ.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.z, 0.0, 1e-10);
    }
    
    TEST(RotationTest, RotateState) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      Vec3d av(2.0 / 3.0 * M_PI, 2.0 / 3.0 * M_PI, 2.0 / 3.0 * M_PI);
      State unitX({1.0, 0.0, 0.0, 0.0, 0.0, 0.0});
      State unitY({0.0, 1.0, 0.0, 0.0, 0.0, 0.0});
      State unitZ({0.0, 0.0, 1.0, 0.0, 0.0, 0.0});
      State unitVX({0.0, 0.0, 0.0, 1.0, 0.0, 0.0});
      State unitVY({0.0, 0.0, 0.0, 0.0, 1.0, 0.0});
      State unitVZ({0.0, 0.0, 0.0, 0.0, 0.0, 1.0});
      State rotatedX = rotation(unitX, av);
      State rotatedY = rotation(unitY, av);
      State rotatedZ = rotation(unitZ, av);
      State rotatedVX = rotation(unitVX, av);
      State rotatedVY = rotation(unitVY, av);
      State rotatedVZ = rotation(unitVZ, av);
      EXPECT_NEAR(rotatedX.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedX.position.y, 1.0, 1e-10);
      EXPECT_NEAR(rotatedX.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedX.velocity.x, 2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(rotatedX.velocity.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedX.velocity.z, -2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(rotatedY.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.position.z, 1.0, 1e-10);
      EXPECT_NEAR(rotatedY.velocity.x, -2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(rotatedY.velocity.y, 2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(rotatedY.velocity.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.position.x, 1.0, 1e-10);
      EXPECT_NEAR(rotatedZ.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.velocity.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.velocity.y, -2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(rotatedZ.velocity.z, 2.0 / 3.0 * M_PI, 1e-10);
      EXPECT_NEAR(rotatedVX.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVX.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVX.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVX.velocity.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVX.velocity.y, 1.0, 1e-10);
      EXPECT_NEAR(rotatedVX.velocity.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.velocity.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.velocity.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.velocity.z, 1.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.velocity.x, 1.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.velocity.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.velocity.z, 0.0, 1e-10);
    }
    
    TEST(RotationTest, RotateStateNoAv) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      State unitX({1.0, 0.0, 0.0, 0.0, 0.0, 0.0});
      State unitY({0.0, 1.0, 0.0, 0.0, 0.0, 0.0});
      State unitZ({0.0, 0.0, 1.0, 0.0, 0.0, 0.0});
      State unitVX({0.0, 0.0, 0.0, 1.0, 0.0, 0.0});
      State unitVY({0.0, 0.0, 0.0, 0.0, 1.0, 0.0});
      State unitVZ({0.0, 0.0, 0.0, 0.0, 0.0, 1.0});
      State rotatedX = rotation(unitX);
      State rotatedY = rotation(unitY);
      State rotatedZ = rotation(unitZ);
      State rotatedVX = rotation(unitVX);
      State rotatedVY = rotation(unitVY);
      State rotatedVZ = rotation(unitVZ);
      EXPECT_NEAR(rotatedX.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedX.position.y, 1.0, 1e-10);
      EXPECT_NEAR(rotatedX.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedX.velocity.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedX.velocity.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedX.velocity.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.position.z, 1.0, 1e-10);
      EXPECT_NEAR(rotatedY.velocity.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.velocity.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedY.velocity.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.position.x, 1.0, 1e-10);
      EXPECT_NEAR(rotatedZ.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.velocity.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.velocity.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedZ.velocity.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVX.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVX.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVX.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVX.velocity.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVX.velocity.y, 1.0, 1e-10);
      EXPECT_NEAR(rotatedVX.velocity.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.velocity.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.velocity.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVY.velocity.z, 1.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.position.x, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.position.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.position.z, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.velocity.x, 1.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.velocity.y, 0.0, 1e-10);
      EXPECT_NEAR(rotatedVZ.velocity.z, 0.0, 1e-10);
    }
    
    TEST(RotationTest, Inverse) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      Rotation inverseRotation = rotation.inverse();
      vector<double> quat = inverseRotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0],  0.5, 1e-10);
      EXPECT_NEAR(quat[1], -0.5, 1e-10);
      EXPECT_NEAR(quat[2], -0.5, 1e-10);
      EXPECT_NEAR(quat[3], -0.5, 1e-10);
    }
    
    TEST(RotationTest, MultiplyRotation) {
      Rotation rotation(0.5, 0.5, 0.5, 0.5);
      Rotation doubleRotation = rotation * rotation;
      vector<double> quat = doubleRotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0], -0.5, 1e-10);
      EXPECT_NEAR(quat[1],  0.5, 1e-10);
      EXPECT_NEAR(quat[2],  0.5, 1e-10);
      EXPECT_NEAR(quat[3],  0.5, 1e-10);
    }
    
    TEST(RotationTest, Slerp) {
      Rotation rotationOne(0.5, 0.5, 0.5, 0.5);
      Rotation rotationTwo(-0.5, 0.5, 0.5, 0.5);
      Rotation interpRotation = rotationOne.interpolate(rotationTwo, 0.125, ale::SLERP);
      vector<double> quat = interpRotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0], cos(M_PI * 3.0/8.0), 1e-10);
      EXPECT_NEAR(quat[1], sin(M_PI * 3.0/8.0) * 1/sqrt(3.0), 1e-10);
      EXPECT_NEAR(quat[2], sin(M_PI * 3.0/8.0) * 1/sqrt(3.0), 1e-10);
      EXPECT_NEAR(quat[3], sin(M_PI * 3.0/8.0) * 1/sqrt(3.0), 1e-10);
    }
    
    TEST(RotationTest, SlerpExtrapolate) {
      Rotation rotationOne(0.5, 0.5, 0.5, 0.5);
      Rotation rotationTwo(-0.5, 0.5, 0.5, 0.5);
      Rotation interpRotation = rotationOne.interpolate(rotationTwo, 1.125, ale::SLERP);
      vector<double> quat = interpRotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0], cos(M_PI * 17.0/24.0), 1e-10);
      EXPECT_NEAR(quat[1], sin(M_PI * 17.0/24.0) * 1/sqrt(3.0), 1e-10);
      EXPECT_NEAR(quat[2], sin(M_PI * 17.0/24.0) * 1/sqrt(3.0), 1e-10);
      EXPECT_NEAR(quat[3], sin(M_PI * 17.0/24.0) * 1/sqrt(3.0), 1e-10);
    }
    
    TEST(RotationTest, Nlerp) {
      Rotation rotationOne(0.5, 0.5, 0.5, 0.5);
      Rotation rotationTwo(-0.5, 0.5, 0.5, 0.5);
      Rotation interpRotation = rotationOne.interpolate(rotationTwo, 0.125, ale::NLERP);
      double scaling = 8.0 / sqrt(57.0);
      vector<double> quat = interpRotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0], 3.0 / 8.0 * scaling, 1e-10);
      EXPECT_NEAR(quat[1], 1.0 / 2.0 * scaling, 1e-10);
      EXPECT_NEAR(quat[2], 1.0 / 2.0 * scaling, 1e-10);
      EXPECT_NEAR(quat[3], 1.0 / 2.0 * scaling, 1e-10);
    }
    
    TEST(RotationTest, NlerpExtrapolate) {
      Rotation rotationOne(0.5, 0.5, 0.5, 0.5);
      Rotation rotationTwo(-0.5, 0.5, 0.5, 0.5);
      Rotation interpRotation = rotationOne.interpolate(rotationTwo, 1.125, ale::NLERP);
      double scaling = 8.0 / sqrt(73.0);
      vector<double> quat = interpRotation.toQuaternion();
      ASSERT_EQ(quat.size(), 4);
      EXPECT_NEAR(quat[0], -5.0 / 8.0 * scaling, 1e-10);
      EXPECT_NEAR(quat[1], 1.0 / 2.0 * scaling, 1e-10);
      EXPECT_NEAR(quat[2], 1.0 / 2.0 * scaling, 1e-10);
      EXPECT_NEAR(quat[3], 1.0 / 2.0 * scaling, 1e-10);
    }