#include "gtest/gtest.h" #include "ale/Rotation.h" #include #include using namespace std; using namespace ale; TEST(RotationTest, DefaultConstructor) { Rotation defaultRotation; vector 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 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 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 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 angles; angles.push_back(M_PI); std::vector axes; axes.push_back(0); Rotation rotation(angles, axes); vector 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 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 angles; angles.push_back(M_PI); std::vector axes = {0, 1, 2}; ASSERT_THROW(Rotation(angles, axes), std::invalid_argument); } TEST(RotationTest, EmptyAxisAngle) { std::vector angles; std::vector axes = {0, 1, 2}; ASSERT_THROW(Rotation(angles, axes), std::invalid_argument); } TEST(RotationTest, BadAxisNumber) { std::vector angles; angles.push_back(M_PI); std::vector 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 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 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 av = {2.0 / 3.0 * M_PI, 2.0 / 3.0 * M_PI, 2.0 / 3.0 * M_PI}; vector 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 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 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 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, 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 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 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 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 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 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 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); }