From f0e5754abcd90375a18ecbd2f6d7ef0c76a442b2 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 2 Jan 2018 17:47:33 +0100 Subject: [PATCH] Add unit tests which reveal converting problems with euler angles Partly covered methods which converts between euler angles and quaternions. Whats still missing here is applied rotation axis and rotation orientation (counter clockwise + right hand rule) --- .../OpenTK.Tests.Math.csproj | 1 + tests/OpenTK.Tests.Math/QuaternionTests.cs | 214 ++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 tests/OpenTK.Tests.Math/QuaternionTests.cs diff --git a/tests/OpenTK.Tests.Math/OpenTK.Tests.Math.csproj b/tests/OpenTK.Tests.Math/OpenTK.Tests.Math.csproj index 9b2ccec8..07882b1d 100644 --- a/tests/OpenTK.Tests.Math/OpenTK.Tests.Math.csproj +++ b/tests/OpenTK.Tests.Math/OpenTK.Tests.Math.csproj @@ -47,6 +47,7 @@ + diff --git a/tests/OpenTK.Tests.Math/QuaternionTests.cs b/tests/OpenTK.Tests.Math/QuaternionTests.cs new file mode 100644 index 00000000..27f0a28b --- /dev/null +++ b/tests/OpenTK.Tests.Math/QuaternionTests.cs @@ -0,0 +1,214 @@ +using Xunit; +using System; +using System.Collections.Generic; + +namespace OpenTK.Tests.Math +{ + /// + /// Generates Quaternion test data. + /// + public class QuaternionTestDataGenerator + { + /// + /// Returns the single axis test cases. + /// 1. param: rotation in euler angles + /// 2. param: expected result of xyz-component of quaternion + /// 3. param: test name (Don't found a working way how to pass this to xUnit runner). I let it here for test documentation + /// + /// The single axis test cases. + public static IEnumerable SingleAxisTestCases() + { + yield return new object[] { new Vector3(1, 0, 0), Vector3.UnitX, "Rotate around x axis" }; + yield return new object[] { new Vector3(0, 1, 0), Vector3.UnitY, "Rotate around y axis" }; + yield return new object[] { new Vector3(0, 0, 1), Vector3.UnitZ, "Rotate around z axis" }; + } + + /// + /// Returns the single ToAxisAngle test cases. + /// 1. param: Quaternion which a definied value of xyz-component. + /// 2. param: expected result of xyz-component of quaternion + /// 3. param: test name (Don't found a working way how to pass this to xUnit runner). I let it here for test documentation + /// + /// The single axis test cases. + public static IEnumerable ToAxisAngleTestCases() + { + yield return new object[] { new Quaternion(Vector3.UnitX, 0), Vector3.UnitX, "Rotate around x axis" }; + yield return new object[] { new Quaternion(Vector3.UnitY, 0), Vector3.UnitY, "Rotate around y axis" }; + yield return new object[] { new Quaternion(Vector3.UnitZ, 0), Vector3.UnitZ, "Rotate around z axis" }; + } + } + + /// + /// Provides some methods which helps to verify test results + /// + internal static class QuaternionTestHelper + { + /// + /// Verifies the direction of an given . + /// + /// false: When does contain xyz values, when it should be 0, + /// or does not contain 0 when it should be + /// To test + /// Expected directions. Values getting only 0 checked + public static bool VerifyEuqalSignleDirection(Vector3 toTest, Vector3 expected) + { + //To verify the direction of an vector, just respect the 0 values and check against these. + //The length of the vectors are ignored. + if (expected.X == 0) + { + if (toTest.X != 0) + return false; + } + else + { + if (toTest.X == 0) + return false; + } + + if (expected.Y == 0) + { + if (toTest.Y != 0) + return false; + } + else + { + if (toTest.Y == 0) + return false; + } + + if (expected.Z == 0) + { + if (toTest.Z != 0) + return false; + } + else + { + if (toTest.Z == 0) + return false; + } + + return true; + } + } + + public class Quaternion_Tests + { + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void CtorEulerAnglesFloat_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = new Quaternion(eulerValues.X, eulerValues.Y, eulerValues.Z); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void CtorEulerAnglesVector3_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = new Quaternion(eulerValues.X, eulerValues.Y, eulerValues.Z); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void FromEulerAnglesFloat_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = Quaternion.FromEulerAngles(eulerValues.X, eulerValues.Y, eulerValues.Z); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void FromEulerAnglesVector3_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = Quaternion.FromEulerAngles(eulerValues); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Checks if a single given value (either pitch, yaw or roll) get converted into correct x,y,z value of quaternion. + /// + /// euler angle values + /// expected xyz component of quaternion + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.SingleAxisTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void FromEulerAnglesOut_SingleEulerAngleSet_RotateCorrectAxis(Vector3 eulerValues, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with "pitch/yaw/roll" + var cut = Quaternion.Identity; + Quaternion.FromEulerAngles(ref eulerValues, out cut); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Vector3 resultXYZ = cut.Xyz; + + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + /// + /// Check if a quaternion returns a a rotation about the correct coordinate axis + /// + /// Prepared Quaternion + /// Expected result. + /// Taken from nUnit test data. Don't know how to name data driven tests for xUnit which actually works. + [Theory] + [MemberData(nameof(QuaternionTestDataGenerator.ToAxisAngleTestCases), MemberType = typeof(QuaternionTestDataGenerator))] + public void ToAxisAngle_SingleAxisSetAndAngleIgnored_RotateCorrectAxis(Quaternion cut, Vector3 expectedResult, String testName) + { + //Arrange + Act: Create Quaternion with rotation about X/Y/Z axis + Vector3 resultXYZ; + float dontCare; + cut.ToAxisAngle(out resultXYZ, out dontCare); + + //Assert: Use helper, to check if part of the two correct axis is zero. I just want check the direction + Assert.True(QuaternionTestHelper.VerifyEuqalSignleDirection(resultXYZ, expectedResult)); + } + + //TODO: Make also checks with rotation angle + } +} +