#include "CppUnitTest.h" #include "../Eule/Vector3.h" #include "../Eule/Math.h" #include "../_TestingUtilities/HandyMacros.h" #include #include using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Eule; namespace Vectors { TEST_CLASS(_Vector3) { private: std::mt19937 rng; public: // Constructor _Vector3() { rng = std::mt19937((std::random_device())()); return; } // Tests if all values are 0 after initialization via default constructor TEST_METHOD(New_Vector_All_0) { Vector3d v3; Assert::AreEqual(0.0, v3.x); Assert::AreEqual(0.0, v3.y); Assert::AreEqual(0.0, v3.z); return; } // Tests if values can be set via the constructor TEST_METHOD(Can_Set_Values_Constructor) { Vector3d v3(69, 32, 16); Assert::AreEqual(69.0, v3.x); Assert::AreEqual(32.0, v3.y); Assert::AreEqual(16.0, v3.z); return; } // Tests if values can be set via letters TEST_METHOD(Can_Set_Values_Letters) { Vector3d v3; v3.x = 69; v3.y = 32; v3.z = 16; Assert::AreEqual(69.0, v3.x); Assert::AreEqual(32.0, v3.y); Assert::AreEqual(16.0, v3.z); return; } // Tests if values can be set via array descriptors TEST_METHOD(Can_Set_Values_ArrayDescriptor) { Vector3d v3; v3[0] = 69; v3[1] = 32; v3[2] = 16; Assert::AreEqual(69.0, v3.x); Assert::AreEqual(32.0, v3.y); Assert::AreEqual(16.0, v3.z); return; } // Tests if values can be set via an initializer list TEST_METHOD(Can_Set_Values_InitializerList) { Vector3d v3 = { 69, 32, 16 }; Assert::AreEqual(69.0, v3.x); Assert::AreEqual(32.0, v3.y); Assert::AreEqual(16.0, v3.z); return; } // Tests for vectors copied via the copy constructor to have the same values TEST_METHOD(Copy_Constructor_Same_Values) { Vector3d a(69, 32, 16); Vector3d b(a); Assert::AreEqual(a.x, b.x); Assert::AreEqual(a.y, b.y); Assert::AreEqual(a.z, b.z); return; } // Tests for vectors copied via the equals operator to have the same values TEST_METHOD(Operator_Equals_Same_Values) { Vector3d a(69, 32, 16); Vector3d b = a; Assert::AreEqual(a.x, b.x); Assert::AreEqual(a.y, b.y); Assert::AreEqual(a.z, b.z); return; } // Tests for vectors copied via the copy constructor to be modifyable without modifying the original object TEST_METHOD(Copy_Constructor_Independent) { Vector3d a(69, 32, 16); Vector3d b(a); b.x = 169; b.y = 132; b.z = 116; Assert::AreEqual(69.0, a.x); Assert::AreEqual(32.0, a.y); Assert::AreEqual(16.0, a.z); Assert::AreEqual(169.0, b.x); Assert::AreEqual(132.0, b.y); Assert::AreEqual(116.0, b.z); return; } // Tests for vectors copied via the equals operator to be modifyable without modifying the original object TEST_METHOD(Operator_Equals_Independent) { Vector3d a(69, 32, 16); Vector3d b = a; b.x = 169; b.y = 132; b.z = 116; Assert::AreEqual(69.0, a.x); Assert::AreEqual(32.0, a.y); Assert::AreEqual(16.0, a.z); Assert::AreEqual(169.0, b.x); Assert::AreEqual(132.0, b.y); Assert::AreEqual(116.0, b.z); return; } // Tests if the dot product between two vectors angled 90 degrees from one another is 0. It should by definition be 0! // Dot products are commutative, so we'll check both directions. // This test tests all possible 90 degree setups with 1000x random lengths TEST_METHOD(DotProduct_90deg) { // Test 1000 times for (std::size_t i = 0; i < 100; i++) { // The length of the vectors should not matter. Only the angle should. // Let's test that! Vector3d a = Vector3d(1, 0, 0) * (rng() % 6969 + 1.0); Vector3d b = Vector3d(0, 1, 0) * (rng() % 6969 + 1.0); std::wstringstream wss; wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::AreEqual(0.0, a.DotProduct(b), wss.str().c_str()); Assert::AreEqual(0.0, b.DotProduct(a), wss.str().c_str()); wss.str(L""); a = Vector3d(0, 1, 0) * (rng() % 6969 + 1.0); b = Vector3d(1, 0, 0) * (rng() % 6969 + 1.0); wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::AreEqual(0.0, a.DotProduct(b), wss.str().c_str()); Assert::AreEqual(0.0, b.DotProduct(a), wss.str().c_str()); wss.str(L""); a = Vector3d(0, 1, 0) * (rng() % 6969 + 1.0); b = Vector3d(0, 0, 1) * (rng() % 6969 + 1.0); wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::AreEqual(0.0, a.DotProduct(b), wss.str().c_str()); Assert::AreEqual(0.0, b.DotProduct(a), wss.str().c_str()); wss.str(L""); a = Vector3d(0, 0, 1) * (rng() % 6969 + 1.0); b = Vector3d(0, 1, 0) * (rng() % 6969 + 1.0); wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::AreEqual(0.0, a.DotProduct(b), wss.str().c_str()); Assert::AreEqual(0.0, b.DotProduct(a), wss.str().c_str()); wss.str(L""); a = Vector3d(1, 0, 0) * (rng() % 6969 + 1.0); b = Vector3d(0, 0, 1) * (rng() % 6969 + 1.0); wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::AreEqual(0.0, a.DotProduct(b), wss.str().c_str()); Assert::AreEqual(0.0, b.DotProduct(a), wss.str().c_str()); wss.str(L""); a = Vector3d(0, 0, 1) * (rng() % 6969 + 1.0); b = Vector3d(1, 0, 0) * (rng() % 6969 + 1.0); wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::AreEqual(0.0, a.DotProduct(b), wss.str().c_str()); Assert::AreEqual(0.0, b.DotProduct(a), wss.str().c_str()); } return; } // Test if the dot product is positive for two vectors angled less than 90 degrees from another // Dot products are commutative, so we'll check both directions. TEST_METHOD(DotProduct_LessThan90deg) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { // The length of the vectors should not matter. Only the angle should. // Let's test that! Vector3d a = Vector3d(1, 1.0 / (rng() % 100), 69) * (rng() % 6969 + 1.0); // Don't allow the scalar to become 0 Vector3d b = Vector3d(1.0 / (rng() % 100), 1, 69) * (rng() % 6969 + 1.0); std::wstringstream wss; wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::IsTrue(a.DotProduct(b) > 0, wss.str().c_str()); Assert::IsTrue(b.DotProduct(a) > 0, wss.str().c_str()); } return; } // Test if the dot product is negative for two vectors angled greater than 90 degrees from another // Dot products are commutative, so we'll check both directions. TEST_METHOD(DotProduct_GreaterThan90deg) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { // The length of the vectors should not matter. Only the angle should. // Let's test that! Vector3d a = Vector3d(1, -1.0 / (rng() % 100), 69) * (rng() % 6969 + 1.0); // Don't allow the scalar to become 0 Vector3d b = Vector3d(-1.0 / (rng() % 100), 1, -69) * (rng() % 6969 + 1.0); std::wstringstream wss; wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::IsTrue(a.DotProduct(b) < 0, wss.str().c_str()); Assert::IsTrue(b.DotProduct(a) < 0, wss.str().c_str()); } return; } // Tests that the dot product is correct for a known value TEST_METHOD(DotProduct_Oracle) { // Setup Vector3d a(-99, 199, -32); Vector3d b(18, -1, -21); // Exercise const double dot = a.DotProduct(b); // Verify Assert::AreEqual(-1309.0, dot); return; } // Quick and dirty check if the useless int-method is working TEST_METHOD(DotProduct_Dirty_Int) { Vector3i a; Vector3i b; std::wstringstream wss; // 90 deg a = { 0, 10, 0 }; b = { 10, 0, 0 }; wss.str(L""); wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::AreEqual(0.0, a.DotProduct(b), wss.str().c_str()); Assert::AreEqual(0.0, b.DotProduct(a), wss.str().c_str()); // < 90 deg a = { 7, 10, 10 }; b = { 10, 1, 10 }; wss.str(L""); wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::IsTrue(a.DotProduct(b) > 0.0, wss.str().c_str()); Assert::IsTrue(b.DotProduct(a) > 0.0, wss.str().c_str()); // > 90 deg a = { -3, 10, -10 }; b = { 10, -4, 10 }; wss.str(L""); wss << a << L" DOT " << b << L" = " << a.DotProduct(b) << std::endl; Assert::IsTrue(a.DotProduct(b) < 0.0, wss.str().c_str()); Assert::IsTrue(b.DotProduct(a) < 0.0, wss.str().c_str()); return; } // Tests for the cross product between the same vector being 0 TEST_METHOD(CrossProduct_Same_Vector_Is_0) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = LARGE_RAND_DOUBLE; double y = LARGE_RAND_DOUBLE; double z = LARGE_RAND_DOUBLE; Vector3d a(x, y, z); std::wstringstream wss; wss << a << L" CROSS " << a << L" = " << a.CrossProduct(a) << std::endl; Assert::IsTrue(Vector3d(0,0,0) == a.CrossProduct(a), wss.str().c_str()); } return; } // Tests for the cross product between opposite vectors being 0 TEST_METHOD(CrossProduct_Opposite_Vector_Is_0) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = LARGE_RAND_DOUBLE; double y = LARGE_RAND_DOUBLE; double z = LARGE_RAND_DOUBLE; Vector3d a(x, y, z); Vector3d b(-x, -y, -z); std::wstringstream wss; wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, 0, 0) == a.CrossProduct(b), wss.str().c_str()); } return; } // Tests for known values TEST_METHOD(CrossProduct_KnownValues) { Vector3d a; Vector3d b; std::wstringstream wss; wss.str(L""); a = Vector3d(1, 0, 0); b = Vector3d(0, 0, 1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -1, 0) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3d(-1, 0, 0); b = Vector3d(0, 0, -1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -1, 0) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3d(1, 0, 0); b = Vector3d(0, 0, -1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, 1, 0) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3d(1, 0, 0); b = Vector3d(0, 1, 0); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, 0, 1) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3d(1, 0, 0); b = Vector3d(1, 0, 1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -1, 0) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3d(1, 0, 0); b = Vector3d(1, 1, 1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -1, 1) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3d(3, -1, -3); b = Vector3d(-1, 1, 3); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -6, 2) == a.CrossProduct(b), wss.str().c_str()); return; } // Tests for known values, but with int vectors TEST_METHOD(CrossProduct_KnownValues_Int) { Vector3i a; Vector3i b; std::wstringstream wss; wss.str(L""); a = Vector3i(1, 0, 0); b = Vector3i(0, 0, 1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -1, 0) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3i(-1, 0, 0); b = Vector3i(0, 0, -1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -1, 0) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3i(1, 0, 0); b = Vector3i(0, 0, -1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, 1, 0) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3i(1, 0, 0); b = Vector3i(0, 1, 0); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, 0, 1) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3i(1, 0, 0); b = Vector3i(1, 0, 1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -1, 0) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3i(1, 0, 0); b = Vector3i(1, 1, 1); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -1, 1) == a.CrossProduct(b), wss.str().c_str()); wss.str(L""); a = Vector3i(3, -1, -3); b = Vector3i(-1, 1, 3); wss << a << L" CROSS " << b << L" = " << a.CrossProduct(b) << std::endl; Assert::IsTrue(Vector3d(0, -6, 2) == a.CrossProduct(b), wss.str().c_str()); return; } // Tests the SqrMagnitude method to work as expected with random numbers TEST_METHOD(SqrMagnitude) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = (double)(rng() % 1000) - 500.0; // Too large numbers would get unaccurate decimals when using intrinsics. double y = (double)(rng() % 1000) - 500.0; double z = (double)(rng() % 1000) - 500.0; double expected = x*x + y*y + z*z; Assert::IsTrue(Math::Similar(expected, Vector3d(x, y, z).SqrMagnitude())); } return; } // Tests the SqrMagnitude method to work as expected with random numbers, but with an int-vector TEST_METHOD(SqrMagnitude_Int) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { int x = LARGE_RAND_INT; int y = LARGE_RAND_INT; int z = LARGE_RAND_INT; int expected = x*x + y*y + z*z; Assert::AreEqual((double)expected, Vector3i(x, y, z).SqrMagnitude()); } return; } // Tests for the length of the vector (0,0,0) being 0 TEST_METHOD(Magnitude_Is_0_On_Vec0) { Assert::AreEqual(0.0, Vector3d(0, 0, 0).Magnitude()); return; } // Tests for a vector of a known length to actually return that TEST_METHOD(Magnitude_One_Axis_X) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = (double)(rng() % 1000) - 500.0; // Too large numbers would get unaccurate decimals when using intrinsics. Vector3d vec(x, 0, 0); std::wstringstream wss; wss << std::endl << std::setprecision(20) << "Actual: " << vec.Magnitude() << std::endl << "Expected: " << x << std::endl; Assert::IsTrue(Math::Similar(abs(x), vec.Magnitude()), wss.str().c_str()); } return; } // Tests for a vector of a known length to actually return that TEST_METHOD(Magnitude_One_Axis_Y) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double y = (double)(rng() % 1000) - 500.0; // Too large numbers would get unaccurate decimals when using intrinsics. Vector3d vec(0, y, 0); std::wstringstream wss; wss << std::endl << std::setprecision(20) << "Actual: " << vec.Magnitude() << std::endl << "Expected: " << y << std::endl; Assert::IsTrue(Math::Similar(abs(y), vec.Magnitude()), wss.str().c_str()); } return; } // Tests for a vector of a known length to actually return that TEST_METHOD(Magnitude_One_Axis_Z) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double z = (double)(rng() % 1000) - 500.0; // Too large numbers would get unaccurate decimals when using intrinsics. Vector3d vec(0, 0, z); std::wstringstream wss; wss << std::endl << std::setprecision(20) << "Actual: " << vec.Magnitude() << std::endl << "Expected: " << z << std::endl; Assert::IsTrue(Math::Similar(abs(z), vec.Magnitude()), wss.str().c_str()); } return; } // Tests for a known result TEST_METHOD(Magnitude) { // Ya'll got more of 'dem digits? Assert::AreEqual(426.14786166306174663986894302070140838623046875, Vector3d(69, -420, 21).Magnitude()); return; } // Tests for expected lerp result 0.00 TEST_METHOD(Lerp_000) { Vector3d a(100, 1000, 10); Vector3d b(200, 4000, 100); Vector3d res = a.Lerp(b, 0.00); std::wstringstream wss; wss << res; Assert::IsTrue(a == res, wss.str().c_str()); return; } // Tests for expected lerp result 0.25 TEST_METHOD(Lerp_025) { Vector3d a(100, 1000, 10); Vector3d b(200, 4000, 100); Vector3d res = a.Lerp(b, 0.25); std::wstringstream wss; wss << res; Assert::IsTrue(Vector3d(125, 1750, 32.5) == res, wss.str().c_str()); return; } // Tests for expected lerp result 0.50 TEST_METHOD(Lerp_050) { Vector3d a(100, 1000, 10); Vector3d b(200, 4000, 100); Vector3d res = a.Lerp(b, 0.50); std::wstringstream wss; wss << res; Assert::IsTrue(Vector3d(150, 2500, 55) == res, wss.str().c_str()); return; } // Tests for expected lerp result 0.75 TEST_METHOD(Lerp_075) { Vector3d a(100, 1000, 10); Vector3d b(200, 4000, 100); Vector3d res = a.Lerp(b, 0.75); std::wstringstream wss; wss << res; Assert::IsTrue(Vector3d(175, 3250, 77.5) == res, wss.str().c_str()); return; } // Tests for expected lerp result 1.00 TEST_METHOD(Lerp_100) { Vector3d a(100, 1000, 10); Vector3d b(200, 4000, 100); Vector3d res = a.Lerp(b, 1.00); std::wstringstream wss; wss << res; Assert::IsTrue(b == res, wss.str().c_str()); return; } // Tests lerpself TEST_METHOD(LerpSelf) { Vector3d a(100, 1000, 10); Vector3d b(200, 4000, 100); a.LerpSelf(b, 0.75); std::wstringstream wss; wss << a; Assert::IsTrue(Vector3d(175, 3250, 77.5) == a, wss.str().c_str()); return; } // Tests if an input vector of length 0 is handled correctly by the normalize method TEST_METHOD(Normalize_Length_Before_Is_0) { Vector3d vec(0, 0, 0); vec.NormalizeSelf(); Assert::AreEqual(0.0, vec.Magnitude()); return; } // Tests for any normalized vector to be of length 1 TEST_METHOD(Normalize_Length_Is_1) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = LARGE_RAND_DOUBLE; double y = LARGE_RAND_DOUBLE; double z = LARGE_RAND_DOUBLE; Vector3d vec(x, y, z); // Prevent a vector of length 0 going in if (vec.SqrMagnitude() == 0) vec.x++; std::wstringstream wss; wss << vec; Assert::IsTrue(Math::Similar(vec.Normalize().Magnitude(), 1.0), wss.str().c_str()); // Account for floating point inaccuracy } return; } // Tests the normalize method with known values TEST_METHOD(Normalize_Oracle) { // Setup Vector3d v(3.2, -5.3, 9.88); // Exercise v.NormalizeSelf(); // Verify Vector3d expected(0.27445384355, -0.45456417839, 0.84737624198); Assert::IsTrue(v.Similar(expected)); } // Tests for a normalized vector to still point in the exact same direction TEST_METHOD(Normalize_Direction_Stays_Unaffected) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = LARGE_RAND_DOUBLE; double y = LARGE_RAND_DOUBLE; double z = LARGE_RAND_DOUBLE; if (x == 0) x++; if (y == 0) y++; if (z == 0) z++; Vector3d vec(x, y, z); Vector3d vec_n(x, y, z); vec_n = vec_n.Normalize(); std::wstringstream wss; wss << vec << L" | " << vec_n; // Both vectors should still point in the same direction! Assert::IsTrue( (vec.DotProduct(vec_n) > 0) && // Roughly same direction (Math::Similar(vec_n.CrossProduct(vec).Magnitude(), 0.0)), // Both vectors align wss.str().c_str()); } return; } // Kinda dumb method, but ok lol // DON'T NORMALIZE INT-VECTORS WHAT IS WRONG WITH YOU TEST_METHOD(Normalized_Int_Vector_Is_0) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { int x = LARGE_RAND_INT; int y = LARGE_RAND_INT; int z = LARGE_RAND_INT; Vector3i vec(x, y, z); vec.NormalizeSelf(); std::wstringstream wss; wss << vec; Assert::AreEqual(0.0, vec.Magnitude(), wss.str().c_str()); } } // Tests that NormalizeSelf() results in the same as Normalize() TEST_METHOD(NormalizeSelf_IsSameAs_Normalize) { // Run test 1000 times for (std::size_t i = 0; i < 1000; i++) { Vector3d vec(LARGE_RAND_DOUBLE, LARGE_RAND_DOUBLE, LARGE_RAND_DOUBLE); Vector3d nVec = vec.Normalize(); vec.NormalizeSelf(); Assert::IsTrue(nVec == vec); } return; } // Tests for the VectorScale() method to work TEST_METHOD(VectorScale) { // Run test 1000 times for (std::size_t i = 0; i < 1000; i++) { const double ax = LARGE_RAND_DOUBLE; const double ay = LARGE_RAND_DOUBLE; const double az = LARGE_RAND_DOUBLE; const double bx = LARGE_RAND_DOUBLE; const double by = LARGE_RAND_DOUBLE; const double bz = LARGE_RAND_DOUBLE; Vector3d a(ax, ay, az); Vector3d b(bx, by, bz); Vector3d target( ax * bx, ay * by, az * bz ); Assert::IsTrue(a.VectorScale(b) == target); } return; } // Tests for operator- (unary) to work TEST_METHOD(Operator_Unary_Negative) { Vector3d v(29, -5, 35); Assert::IsTrue(Vector3d(-29, 5, -35) == -v); return; } // Tests for operator+ to work as expected TEST_METHOD(Operator_Add) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double ax = LARGE_RAND_DOUBLE; double ay = LARGE_RAND_DOUBLE; double az = LARGE_RAND_DOUBLE; double bx = LARGE_RAND_DOUBLE; double by = LARGE_RAND_DOUBLE; double bz = LARGE_RAND_DOUBLE; Vector3d a(ax, ay, az); Vector3d b(bx, by, bz); Assert::IsTrue(Vector3d(ax + bx, ay + by, az + bz) == a + b); } return; } // Tests for operator+= to work as expected TEST_METHOD(Operator_Add_Equals) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double ax = LARGE_RAND_DOUBLE; double ay = LARGE_RAND_DOUBLE; double az = LARGE_RAND_DOUBLE; double bx = LARGE_RAND_DOUBLE; double by = LARGE_RAND_DOUBLE; double bz = LARGE_RAND_DOUBLE; Vector3d a(ax, ay, az); a += Vector3d(bx, by, bz); Assert::IsTrue(Vector3d(ax + bx, ay + by, az + bz) == a); } return; } // Tests for operator- to work as expected TEST_METHOD(Operator_Sub) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double ax = LARGE_RAND_DOUBLE; double ay = LARGE_RAND_DOUBLE; double az = LARGE_RAND_DOUBLE; double bx = LARGE_RAND_DOUBLE; double by = LARGE_RAND_DOUBLE; double bz = LARGE_RAND_DOUBLE; Vector3d a(ax, ay, az); Vector3d b(bx, by, bz); Assert::IsTrue(Vector3d(ax - bx, ay - by, az - bz) == a - b); } return; } // Tests for operator-= to work as expected TEST_METHOD(Operator_Sub_Equals) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double ax = LARGE_RAND_DOUBLE; double ay = LARGE_RAND_DOUBLE; double az = LARGE_RAND_DOUBLE; double bx = LARGE_RAND_DOUBLE; double by = LARGE_RAND_DOUBLE; double bz = LARGE_RAND_DOUBLE; Vector3d a(ax, ay, az); a -= Vector3d(bx, by, bz); Assert::IsTrue(Vector3d(ax - bx, ay - by, az - bz) == a); } return; } // Tests for operator* to work as expected TEST_METHOD(Operator_Mult) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = LARGE_RAND_DOUBLE; double y = LARGE_RAND_DOUBLE; double z = LARGE_RAND_DOUBLE; double scalar = LARGE_RAND_DOUBLE; Vector3d a(x, y, z); Assert::IsTrue(Vector3d(x * scalar, y * scalar, z * scalar) == a * scalar); } return; } // Tests for operator*= to work as expected TEST_METHOD(Operator_Mult_Equals) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = LARGE_RAND_DOUBLE; double y = LARGE_RAND_DOUBLE; double z = LARGE_RAND_DOUBLE; double scalar = LARGE_RAND_DOUBLE; Vector3d a(x, y, z); a *= scalar; Assert::IsTrue(Vector3d(x * scalar, y * scalar, z * scalar) == a); } return; } // Tests for operator/ to work as expected TEST_METHOD(Operator_Div) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = LARGE_RAND_DOUBLE; double y = LARGE_RAND_DOUBLE; double z = LARGE_RAND_DOUBLE; double scalar = LARGE_RAND_DOUBLE; Vector3d a(x, y, z); Assert::IsTrue(Vector3d(x / scalar, y / scalar, z / scalar) == a / scalar); } return; } // Tests for operator/= to work as expected TEST_METHOD(Operator_Div_Equals) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { double x = LARGE_RAND_DOUBLE; double y = LARGE_RAND_DOUBLE; double z = LARGE_RAND_DOUBLE; double scalar = LARGE_RAND_DOUBLE; Vector3d a(x, y, z); a /= scalar; Assert::IsTrue(Vector3d(x / scalar, y / scalar, z / scalar) == a); } return; } // Tests for operator== to work as expected TEST_METHOD(Operator_Equals) { // Test 1000 times for (std::size_t i = 0; i < 10000; i++) { double ax = (rng() % 10) - 5; double ay = (rng() % 10) - 5; double az = (rng() % 10) - 5; double bx = (rng() % 10) - 5; double by = (rng() % 10) - 5; double bz = (rng() % 10) - 5; Vector3d a(ax, ay, az); Vector3d b(bx, by, bz); Assert::IsTrue( ((ax == bx) && (ay == by) && (az == bz)) == (a == b) ); } return; } // Tests for operator!= to work as expected TEST_METHOD(Operator_Not_Equals) { // Test 1000 times for (std::size_t i = 0; i < 10000; i++) { double ax = (rng() % 10) - 5; double ay = (rng() % 10) - 5; double az = (rng() % 10) - 5; double bx = (rng() % 10) - 5; double by = (rng() % 10) - 5; double bz = (rng() % 10) - 5; Vector3d a(ax, ay, az); Vector3d b(bx, by, bz); Assert::IsTrue( ((ax != bx) || (ay != by) || (az != bz)) == (a != b) ); } return; } // Tests for matrix multiplication working regarding rotation TEST_METHOD(MatrixMult_Rotate_Yaw) { // Create vector Vector3d vec(69, 32, 16); Vector3d originalVec = vec; // Create 90deg yaw rotation matrix (Y) Matrix4x4 mat; mat[0] = { 0, 0, 1, 0 }; mat[1] = { 0, 1, 0, 0 }; mat[2] = { -1, 0, 0, 0 }; // Rotate vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << L"Rot #1 " << vec; Assert::IsTrue(Vector3d(16, 32, -69) == vec, wss.str().c_str()); // Rotate again! vec *= mat; wss.str(L""); wss << L"Rot #2 " << vec; Assert::IsTrue(Vector3d(-69, 32, -16) == vec, wss.str().c_str()); // Rotate again! vec *= mat; wss.str(L""); wss << L"Rot #3 " << vec; Assert::IsTrue(Vector3d(-16, 32, 69) == vec, wss.str().c_str()); // Rotate by 90 deg a last time, having completed a 360° rotation. vec *= mat; wss.str(L""); wss << L"Rot #4 " << vec; Assert::IsTrue(originalVec == vec, wss.str().c_str()); return; } // Tests for matrix multiplication working regarding rotation TEST_METHOD(MatrixMult_Rotate_Roll) { // Create vector Vector3d vec(69, 32, 16); Vector3d originalVec = vec; // Create 90deg roll rotation matrix (Z) Matrix4x4 mat; mat[0] = { 0, -1, 0, 0 }; mat[1] = { 1, 0, 0, 0 }; mat[2] = { 0, 0, 1, 0 }; // Rotate vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << L"Rot #1 " << vec; Assert::IsTrue(Vector3d(-32, 69, 16) == vec, wss.str().c_str()); // Rotate again! vec *= mat; wss.str(L""); wss << L"Rot #2 " << vec; Assert::IsTrue(Vector3d(-69, -32, 16) == vec, wss.str().c_str()); // Rotate again! vec *= mat; wss.str(L""); wss << L"Rot #3 " << vec; Assert::IsTrue(Vector3d(32, -69, 16) == vec, wss.str().c_str()); // Rotate by 90 deg a last time, having completed a 360° rotation. vec *= mat; wss.str(L""); wss << L"Rot #4 " << vec; Assert::IsTrue(originalVec == vec, wss.str().c_str()); return; } // Tests for matrix multiplication working regarding rotation TEST_METHOD(MatrixMult_Rotate_Pitch) { // Create vector Vector3d vec(69, 32, 16); Vector3d originalVec = vec; // Create 90deg pitch rotation matrix (X) Matrix4x4 mat; mat[0] = { 1, 0, 0, 0 }; mat[1] = { 0, 0, -1, 0 }; mat[2] = { 0, 1, 0, 0 }; // Rotate vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << L"Rot #1 " << vec; Assert::IsTrue(Vector3d(69, -16, 32) == vec, wss.str().c_str()); // Rotate again! vec *= mat; wss.str(L""); wss << L"Rot #2 " << vec; Assert::IsTrue(Vector3d(69, -32, -16) == vec, wss.str().c_str()); // Rotate again! vec *= mat; wss.str(L""); wss << L"Rot #3 " << vec; Assert::IsTrue(Vector3d(69, 16, -32) == vec, wss.str().c_str()); // Rotate by 90 deg a last time, having completed a 360° rotation. vec *= mat; wss.str(L""); wss << L"Rot #4 " << vec; Assert::IsTrue(originalVec == vec, wss.str().c_str()); return; } // Tests if rotating a vector (1,1,1) by (45,45,45) eulers works TEST_METHOD(MatrixMult_Rotate_Unit_Combined) { // Create vector Vector3d vec(1, 1, 1); // Create rotation matrix Matrix4x4 mat; mat[0] = { 0.5, -0.1465, 0.8535, 0 }; mat[1] = { 0.5, 0.8535, -0.1465, 0 }; mat[2] = { -0.7072, 0.5, 0.5, 0 }; // Rotate vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue( Math::Similar(vec.x, 1.207, 0.001) && Math::Similar(vec.y, 1.207, 0.001) && Math::Similar(vec.z, 0.2928, 0.001), wss.str().c_str()); return; } // Tests if rotating a vector (69,32,16) by (45,45,45) eulers works TEST_METHOD(MatrixMult_Rotate_HalfUnit_Combined) { // Create vector Vector3d vec(69, 32, 16); // Create rotation matrix Matrix4x4 mat; mat[0] = { 0.5, -0.1465, 0.8535, 0 }; mat[1] = { 0.5, 0.8535, -0.1465, 0 }; mat[2] = { -0.7072, 0.5, 0.5, 0 }; // Rotate vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue( Math::Similar(vec.x, 43.468, 0.001) && Math::Similar(vec.y, 59.468, 0.001) && Math::Similar(vec.z, -24.7968, 0.001), wss.str().c_str()); return; } // Tests if rotating a vector (69,32,16) by (45,45,45) eulers works TEST_METHOD(MatrixMult_Rotate_Combined) { // Create vector Vector3d vec(69, 32, 16); // Create rotation matrix Matrix4x4 mat; mat[0] = { -0.1639, -0.9837, -0.0755, 0 }; mat[1] = { 0.128, -0.097, 0.987, 0 }; mat[2] = { -0.9782, 0.152, 0.1417, 0 }; // Rotate vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue( Math::Similar(vec.x, -43.9955, 0.001) && Math::Similar(vec.y, 21.52, 0.001) && Math::Similar(vec.z, -60.3646, 0.001), wss.str().c_str()); return; } // Tests if matrix scaling works ( x axis only ) TEST_METHOD(MatrixMult_Scale_X) { // Create vector Vector3d vec(5, 6, 7); // Create scaling matrix Matrix4x4 mat; mat[0] = { 3, 0, 0, 0 }; mat[1] = { 0, 1, 0, 0 }; mat[2] = { 0, 0, 1, 0 }; // Scale vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue(Vector3d(15, 6, 7) == vec, wss.str().c_str()); return; } // Tests if matrix scaling works ( y axis only ) TEST_METHOD(MatrixMult_Scale_Y) { // Create vector Vector3d vec(5, 6, 7); // Create scaling matrix Matrix4x4 mat; mat[0] = { 1, 0, 0, 0 }; mat[1] = { 0, 3, 0, 0 }; mat[2] = { 0, 0, 1, 0 }; // Scale vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue(Vector3d(5, 18, 7) == vec, wss.str().c_str()); return; } // Tests if matrix scaling works ( z axis only ) TEST_METHOD(MatrixMult_Scale_Z) { // Create vector Vector3d vec(5, 6, 7); // Create scaling matrix Matrix4x4 mat; mat[0] = { 1, 0, 0, 0 }; mat[1] = { 0, 1, 0, 0 }; mat[2] = { 0, 0, 3, 0 }; // Scale vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue(Vector3d(5, 6, 21) == vec, wss.str().c_str()); return; } // Tests if matrix scaling works ( all axes ) TEST_METHOD(MatrixMult_Scale_Combined) { // Create vector Vector3d vec(5, 6, 7); // Create scaling matrix Matrix4x4 mat; mat[0] = { 4, 0, 0, 0 }; mat[1] = { 0, 5, 0, 0 }; mat[2] = { 0, 0, 8, 0 }; // Scale vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue(Vector3d(20, 30, 56) == vec, wss.str().c_str()); return; } // Tests if translation via matrix multiplication works TEST_METHOD(MatrixMult_Translation) { // Create vector Vector3d vec(5, 6, 7); // Create scaling matrix Matrix4x4 mat; mat[0] = { 1, 0, 0, 155 }; mat[1] = { 0, 1, 0, -23 }; mat[2] = { 0, 0, 1, 333 }; // Translate vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue(Vector3d(160, -17, 340) == vec, wss.str().c_str()); return; } // Tests the multiplication operator (*) with a simple matrix. All other tests used the * operator (without the '=') TEST_METHOD(MatrixMult_Not_Using_MultEqualsOperator) { // Create vector Vector3d vec(5.1, 6.4, 7.99); // Create scaling and translation matrix Matrix4x4 mat; mat[0] = { 3.8, 0, 0, -5.1 }; mat[1] = { 0, -1.5, 0, 15.2 }; mat[2] = { 0, 0, 3.01, 19.9 }; // Transform vector vec = vec * mat; // Did we succeed? Vector3d expected( 5.1 * 3.8 - 5.1, 6.4 * -1.5 + 15.2, 7.99 * 3.01 + 19.9 ); std::wstringstream wss; wss << std::endl; wss << "Expected: " << expected << std::endl; wss << "Actual: " << vec; Assert::IsTrue(expected == vec, wss.str().c_str()); return; } // A simple matrix multiplication tested on an int vector TEST_METHOD(MatrixMult_Dirty_Int_Check) { // Create vector Vector3i vec(5, 6, 7); // Create scaling and translation matrix Matrix4x4 mat; mat[0] = { 3, 0, 0, -5 }; mat[1] = { 0, -1, 0, 15 }; mat[2] = { 0, 0, 3, 20 }; // Transform vector vec *= mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue(Vector3i( 5*3 + -5, 6*-1 + 15, 7*3 + 20 ) == vec, wss.str().c_str()); return; } // Tests the multiplication operator (*) with a simple matrix. All other tests used the * operator (without the '=') TEST_METHOD(MatrixMult_Dirty_Int_Check_Not_Using_MultEqualsOperator) { // Create vector Vector3i vec(5, 6, 7); // Create scaling and translation matrix Matrix4x4 mat; mat[0] = { 3, 0, 0, -5 }; mat[1] = { 0, -1, 0, 15 }; mat[2] = { 0, 0, 3, 20 }; // Scale vector vec = vec * mat; // Did we succeed? std::wstringstream wss; wss << vec; Assert::IsTrue(Vector3i( 5 * 3 + -5, 6 * -1 + 15, 7 * 3 + 20 ) == vec, wss.str().c_str()); return; } //This tests the multiplication equals operator (*=) procedurally TEST_METHOD(MatrixMult_Equals_Procedural) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { // Generate parameters double initialX = LARGE_RAND_DOUBLE; double initialY = LARGE_RAND_DOUBLE; double initialZ = LARGE_RAND_DOUBLE; double scaleX = LARGE_RAND_DOUBLE; double scaleY = LARGE_RAND_DOUBLE; double scaleZ = LARGE_RAND_DOUBLE; double transX = LARGE_RAND_DOUBLE; double transY = LARGE_RAND_DOUBLE; double transZ = LARGE_RAND_DOUBLE; // Create vector Vector3d vec(initialX, initialY, initialZ); // Create matrix Matrix4x4 mat; mat[0] = { scaleX, 0, 0, transX }; mat[1] = { 0, scaleY, 0, transY }; mat[2] = { 0, 0, scaleZ, transZ }; mat[3] = { 0, 0, 0, 0 }; // Create expected vector Vector3d expected( initialX * scaleX + transX, initialY * scaleY + transY, initialZ * scaleZ + transZ ); // Transform vector vec *= mat; // Compare Assert::IsTrue(vec == expected); } return; } //This tests the multiplication operator (*) procedurally TEST_METHOD(MatrixMult_Procedural) { // Test 1000 times for (std::size_t i = 0; i < 1000; i++) { // Generate parameters double initialX = LARGE_RAND_DOUBLE; double initialY = LARGE_RAND_DOUBLE; double initialZ = LARGE_RAND_DOUBLE; double scaleX = LARGE_RAND_DOUBLE; double scaleY = LARGE_RAND_DOUBLE; double scaleZ = LARGE_RAND_DOUBLE; double transX = LARGE_RAND_DOUBLE; double transY = LARGE_RAND_DOUBLE; double transZ = LARGE_RAND_DOUBLE; // Create vector Vector3d vec(initialX, initialY, initialZ); // Create matrix Matrix4x4 mat; mat[0] = { scaleX, 0, 0, transX }; mat[1] = { 0, scaleY, 0, transY }; mat[2] = { 0, 0, scaleZ, transZ }; mat[3] = { 0, 0, 0, 0 }; // Create expected vector Vector3d expected( initialX * scaleX + transX, initialY * scaleY + transY, initialZ * scaleZ + transZ ); // Transform vector vec = vec * mat; // Compare Assert::IsTrue(vec == expected); } return; } // Tests loose comparison via Vector3d::Similar -> true TEST_METHOD(Similar_True) { Assert::IsTrue( Vector3d(0.00000000000000000000001, -6.6666666666666666666666666666, 9.9999999999999999999999999999).Similar( Vector3d(0, -6.666666667, 10) )); return; } // Tests loose comparison via Vector3d::Similar -> false TEST_METHOD(Similar_False) { Assert::IsFalse( Vector3d(0.00000000000000000000001, -6.6666666666666666666666666666, 9.9999999999999999999999999999).Similar( Vector3d(0.1, -6.7, 10.1) )); return; } // Tests that the move constructor works TEST_METHOD(Move_Constructor) { Vector3d a(1,2,3); Vector3d b(std::move(a)); Assert::AreEqual(b.x, 1.0); Assert::AreEqual(b.y, 2.0); Assert::AreEqual(b.z, 3.0); return; } // Tests that the move operator works TEST_METHOD(Move_Operator) { Vector3d a(1, 2, 3); Vector3d b = std::move(a); Assert::AreEqual(b.x, 1.0); Assert::AreEqual(b.y, 2.0); Assert::AreEqual(b.z, 3.0); return; } }; }