summaryrefslogtreecommitdiff
path: root/tests/core/math
diff options
context:
space:
mode:
Diffstat (limited to 'tests/core/math')
-rw-r--r--tests/core/math/test_aabb.h46
-rw-r--r--tests/core/math/test_basis.h225
-rw-r--r--tests/core/math/test_color.h6
-rw-r--r--tests/core/math/test_expression.h20
-rw-r--r--tests/core/math/test_geometry_2d.h26
-rw-r--r--tests/core/math/test_geometry_3d.h2
-rw-r--r--tests/core/math/test_math_funcs.h549
-rw-r--r--tests/core/math/test_plane.h23
-rw-r--r--tests/core/math/test_quaternion.h174
-rw-r--r--tests/core/math/test_rect2.h45
-rw-r--r--tests/core/math/test_rect2i.h16
-rw-r--r--tests/core/math/test_transform_2d.h34
-rw-r--r--tests/core/math/test_transform_3d.h23
-rw-r--r--tests/core/math/test_vector2.h129
-rw-r--r--tests/core/math/test_vector2i.h22
-rw-r--r--tests/core/math/test_vector3.h150
-rw-r--r--tests/core/math/test_vector3i.h20
-rw-r--r--tests/core/math/test_vector4.h104
-rw-r--r--tests/core/math/test_vector4i.h20
19 files changed, 1362 insertions, 272 deletions
diff --git a/tests/core/math/test_aabb.h b/tests/core/math/test_aabb.h
index 447420fc12..23969556be 100644
--- a/tests/core/math/test_aabb.h
+++ b/tests/core/math/test_aabb.h
@@ -91,50 +91,55 @@ TEST_CASE("[AABB] Basic setters") {
TEST_CASE("[AABB] Volume getters") {
AABB aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 5, 6));
CHECK_MESSAGE(
- Math::is_equal_approx(aabb.get_volume(), 120),
+ aabb.get_volume() == doctest::Approx(120),
"get_volume() should return the expected value with positive size.");
CHECK_MESSAGE(
- !aabb.has_no_volume(),
+ aabb.has_volume(),
"Non-empty volumetric AABB should have a volume.");
aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(-4, 5, 6));
CHECK_MESSAGE(
- Math::is_equal_approx(aabb.get_volume(), -120),
+ aabb.get_volume() == doctest::Approx(-120),
"get_volume() should return the expected value with negative size (1 component).");
aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(-4, -5, 6));
CHECK_MESSAGE(
- Math::is_equal_approx(aabb.get_volume(), 120),
+ aabb.get_volume() == doctest::Approx(120),
"get_volume() should return the expected value with negative size (2 components).");
aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(-4, -5, -6));
CHECK_MESSAGE(
- Math::is_equal_approx(aabb.get_volume(), -120),
+ aabb.get_volume() == doctest::Approx(-120),
"get_volume() should return the expected value with negative size (3 components).");
aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 0, 6));
CHECK_MESSAGE(
- aabb.has_no_volume(),
+ !aabb.has_volume(),
"Non-empty flat AABB should not have a volume.");
CHECK_MESSAGE(
- AABB().has_no_volume(),
+ !AABB().has_volume(),
"Empty AABB should not have a volume.");
}
TEST_CASE("[AABB] Surface getters") {
AABB aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 5, 6));
CHECK_MESSAGE(
- !aabb.has_no_surface(),
+ aabb.has_surface(),
"Non-empty volumetric AABB should have an surface.");
aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 0, 6));
CHECK_MESSAGE(
- !aabb.has_no_surface(),
+ aabb.has_surface(),
"Non-empty flat AABB should have a surface.");
+ aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 0, 0));
CHECK_MESSAGE(
- AABB().has_no_surface(),
+ aabb.has_surface(),
+ "Non-empty linear AABB should have a surface.");
+
+ CHECK_MESSAGE(
+ !AABB().has_surface(),
"Empty AABB should not have an surface.");
}
@@ -384,6 +389,27 @@ TEST_CASE("[AABB] Expanding") {
aabb.expand(Vector3(-20, 0, 0)).is_equal_approx(AABB(Vector3(-20, 0, -2.5), Vector3(22.5, 7, 6))),
"expand() with non-contained point should return the expected AABB.");
}
+
+TEST_CASE("[AABB] Finite number checks") {
+ const Vector3 x(0, 1, 2);
+ const Vector3 infinite(NAN, NAN, NAN);
+
+ CHECK_MESSAGE(
+ AABB(x, x).is_finite(),
+ "AABB with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ AABB(infinite, x).is_finite(),
+ "AABB with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ AABB(x, infinite).is_finite(),
+ "AABB with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ AABB(infinite, infinite).is_finite(),
+ "AABB with two components infinite should not be finite.");
+}
+
} // namespace TestAABB
#endif // TEST_AABB_H
diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h
index ae8ca4acde..dce9d5cec3 100644
--- a/tests/core/math/test_basis.h
+++ b/tests/core/math/test_basis.h
@@ -38,16 +38,7 @@
namespace TestBasis {
-enum RotOrder {
- EulerXYZ,
- EulerXZY,
- EulerYZX,
- EulerYXZ,
- EulerZXY,
- EulerZYX
-};
-
-Vector3 deg2rad(const Vector3 &p_rotation) {
+Vector3 deg_to_rad(const Vector3 &p_rotation) {
return p_rotation / 180.0 * Math_PI;
}
@@ -55,88 +46,26 @@ Vector3 rad2deg(const Vector3 &p_rotation) {
return p_rotation / Math_PI * 180.0;
}
-Basis EulerToBasis(RotOrder mode, const Vector3 &p_rotation) {
- Basis ret;
- switch (mode) {
- case EulerXYZ:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_XYZ);
- break;
-
- case EulerXZY:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY);
- break;
-
- case EulerYZX:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX);
- break;
-
- case EulerYXZ:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ);
- break;
-
- case EulerZXY:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY);
- break;
-
- case EulerZYX:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX);
- break;
-
- default:
- // If you land here, Please integrate all rotation orders.
- FAIL("This is not unreachable.");
- }
-
- return ret;
-}
-
-Vector3 BasisToEuler(RotOrder mode, const Basis &p_rotation) {
- switch (mode) {
- case EulerXYZ:
- return p_rotation.get_euler(Basis::EULER_ORDER_XYZ);
-
- case EulerXZY:
- return p_rotation.get_euler(Basis::EULER_ORDER_XZY);
-
- case EulerYZX:
- return p_rotation.get_euler(Basis::EULER_ORDER_YZX);
-
- case EulerYXZ:
- return p_rotation.get_euler(Basis::EULER_ORDER_YXZ);
-
- case EulerZXY:
- return p_rotation.get_euler(Basis::EULER_ORDER_ZXY);
-
- case EulerZYX:
- return p_rotation.get_euler(Basis::EULER_ORDER_ZYX);
-
- default:
- // If you land here, Please integrate all rotation orders.
- FAIL("This is not unreachable.");
- return Vector3();
- }
-}
-
-String get_rot_order_name(RotOrder ro) {
+String get_rot_order_name(EulerOrder ro) {
switch (ro) {
- case EulerXYZ:
+ case EulerOrder::XYZ:
return "XYZ";
- case EulerXZY:
+ case EulerOrder::XZY:
return "XZY";
- case EulerYZX:
+ case EulerOrder::YZX:
return "YZX";
- case EulerYXZ:
+ case EulerOrder::YXZ:
return "YXZ";
- case EulerZXY:
+ case EulerOrder::ZXY:
return "ZXY";
- case EulerZYX:
+ case EulerOrder::ZYX:
return "ZYX";
default:
return "[Not supported]";
}
}
-void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
+void test_rotation(Vector3 deg_original_euler, EulerOrder rot_order) {
// This test:
// 1. Converts the rotation vector from deg to rad.
// 2. Converts euler to basis.
@@ -155,12 +84,12 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
// are correct.
// Euler to rotation
- const Vector3 original_euler = deg2rad(deg_original_euler);
- const Basis to_rotation = EulerToBasis(rot_order, original_euler);
+ const Vector3 original_euler = deg_to_rad(deg_original_euler);
+ const Basis to_rotation = Basis::from_euler(original_euler, rot_order);
// Euler from rotation
- const Vector3 euler_from_rotation = BasisToEuler(rot_order, to_rotation);
- const Basis rotation_from_computed_euler = EulerToBasis(rot_order, euler_from_rotation);
+ const Vector3 euler_from_rotation = to_rotation.get_euler(rot_order);
+ const Basis rotation_from_computed_euler = Basis::from_euler(euler_from_rotation, rot_order);
Basis res = to_rotation.inverse() * rotation_from_computed_euler;
@@ -169,9 +98,8 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_column(2))).utf8().ptr());
// Double check `to_rotation` decomposing with XYZ rotation order.
- const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(Basis::EULER_ORDER_XYZ);
- Basis rotation_from_xyz_computed_euler;
- rotation_from_xyz_computed_euler.set_euler(euler_xyz_from_rotation, Basis::EULER_ORDER_XYZ);
+ const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(EulerOrder::XYZ);
+ Basis rotation_from_xyz_computed_euler = Basis::from_euler(euler_xyz_from_rotation, EulerOrder::XYZ);
res = to_rotation.inverse() * rotation_from_xyz_computed_euler;
@@ -185,13 +113,13 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
}
TEST_CASE("[Basis] Euler conversions") {
- Vector<RotOrder> rotorder_to_test;
- rotorder_to_test.push_back(EulerXYZ);
- rotorder_to_test.push_back(EulerXZY);
- rotorder_to_test.push_back(EulerYZX);
- rotorder_to_test.push_back(EulerYXZ);
- rotorder_to_test.push_back(EulerZXY);
- rotorder_to_test.push_back(EulerZYX);
+ Vector<EulerOrder> euler_order_to_test;
+ euler_order_to_test.push_back(EulerOrder::XYZ);
+ euler_order_to_test.push_back(EulerOrder::XZY);
+ euler_order_to_test.push_back(EulerOrder::YZX);
+ euler_order_to_test.push_back(EulerOrder::YXZ);
+ euler_order_to_test.push_back(EulerOrder::ZXY);
+ euler_order_to_test.push_back(EulerOrder::ZYX);
Vector<Vector3> vectors_to_test;
@@ -249,21 +177,21 @@ TEST_CASE("[Basis] Euler conversions") {
vectors_to_test.push_back(Vector3(120.0, 150.0, -130.0));
vectors_to_test.push_back(Vector3(120.0, 150.0, 130.0));
- for (int h = 0; h < rotorder_to_test.size(); h += 1) {
+ for (int h = 0; h < euler_order_to_test.size(); h += 1) {
for (int i = 0; i < vectors_to_test.size(); i += 1) {
- test_rotation(vectors_to_test[i], rotorder_to_test[h]);
+ test_rotation(vectors_to_test[i], euler_order_to_test[h]);
}
}
}
TEST_CASE("[Stress][Basis] Euler conversions") {
- Vector<RotOrder> rotorder_to_test;
- rotorder_to_test.push_back(EulerXYZ);
- rotorder_to_test.push_back(EulerXZY);
- rotorder_to_test.push_back(EulerYZX);
- rotorder_to_test.push_back(EulerYXZ);
- rotorder_to_test.push_back(EulerZXY);
- rotorder_to_test.push_back(EulerZYX);
+ Vector<EulerOrder> euler_order_to_test;
+ euler_order_to_test.push_back(EulerOrder::XYZ);
+ euler_order_to_test.push_back(EulerOrder::XZY);
+ euler_order_to_test.push_back(EulerOrder::YZX);
+ euler_order_to_test.push_back(EulerOrder::YXZ);
+ euler_order_to_test.push_back(EulerOrder::ZXY);
+ euler_order_to_test.push_back(EulerOrder::ZYX);
Vector<Vector3> vectors_to_test;
// Add 1000 random vectors with weirds numbers.
@@ -275,12 +203,99 @@ TEST_CASE("[Stress][Basis] Euler conversions") {
rng.randf_range(-1800, 1800)));
}
- for (int h = 0; h < rotorder_to_test.size(); h += 1) {
+ for (int h = 0; h < euler_order_to_test.size(); h += 1) {
for (int i = 0; i < vectors_to_test.size(); i += 1) {
- test_rotation(vectors_to_test[i], rotorder_to_test[h]);
+ test_rotation(vectors_to_test[i], euler_order_to_test[h]);
}
}
}
+
+TEST_CASE("[Basis] Set axis angle") {
+ Vector3 axis;
+ real_t angle;
+ real_t pi = (real_t)Math_PI;
+
+ // Testing the singularity when the angle is 0°.
+ Basis identity(1, 0, 0, 0, 1, 0, 0, 0, 1);
+ identity.get_axis_angle(axis, angle);
+ CHECK(angle == 0);
+
+ // Testing the singularity when the angle is 180°.
+ Basis singularityPi(-1, 0, 0, 0, 1, 0, 0, 0, -1);
+ singularityPi.get_axis_angle(axis, angle);
+ CHECK(angle == doctest::Approx(pi));
+
+ // Testing reversing the an axis (of an 30° angle).
+ float cos30deg = Math::cos(Math::deg_to_rad((real_t)30.0));
+ Basis z_positive(cos30deg, -0.5, 0, 0.5, cos30deg, 0, 0, 0, 1);
+ Basis z_negative(cos30deg, 0.5, 0, -0.5, cos30deg, 0, 0, 0, 1);
+
+ z_positive.get_axis_angle(axis, angle);
+ CHECK(angle == doctest::Approx(Math::deg_to_rad((real_t)30.0)));
+ CHECK(axis == Vector3(0, 0, 1));
+
+ z_negative.get_axis_angle(axis, angle);
+ CHECK(angle == doctest::Approx(Math::deg_to_rad((real_t)30.0)));
+ CHECK(axis == Vector3(0, 0, -1));
+
+ // Testing a rotation of 90° on x-y-z.
+ Basis x90deg(1, 0, 0, 0, 0, -1, 0, 1, 0);
+ x90deg.get_axis_angle(axis, angle);
+ CHECK(angle == doctest::Approx(pi / (real_t)2));
+ CHECK(axis == Vector3(1, 0, 0));
+
+ Basis y90deg(0, 0, 1, 0, 1, 0, -1, 0, 0);
+ y90deg.get_axis_angle(axis, angle);
+ CHECK(axis == Vector3(0, 1, 0));
+
+ Basis z90deg(0, -1, 0, 1, 0, 0, 0, 0, 1);
+ z90deg.get_axis_angle(axis, angle);
+ CHECK(axis == Vector3(0, 0, 1));
+
+ // Regression test: checks that the method returns a small angle (not 0).
+ Basis tiny(1, 0, 0, 0, 0.9999995, -0.001, 0, 001, 0.9999995); // The min angle possible with float is 0.001rad.
+ tiny.get_axis_angle(axis, angle);
+ CHECK(angle == doctest::Approx(0.001).epsilon(0.0001));
+
+ // Regression test: checks that the method returns an angle which is a number (not NaN)
+ Basis bugNan(1.00000024, 0, 0.000100001693, 0, 1, 0, -0.000100009143, 0, 1.00000024);
+ bugNan.get_axis_angle(axis, angle);
+ CHECK(!Math::is_nan(angle));
+}
+
+TEST_CASE("[Basis] Finite number checks") {
+ const Vector3 x(0, 1, 2);
+ const Vector3 infinite(NAN, NAN, NAN);
+
+ CHECK_MESSAGE(
+ Basis(x, x, x).is_finite(),
+ "Basis with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(infinite, x, x).is_finite(),
+ "Basis with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Basis(x, infinite, x).is_finite(),
+ "Basis with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Basis(x, x, infinite).is_finite(),
+ "Basis with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(infinite, infinite, x).is_finite(),
+ "Basis with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Basis(infinite, x, infinite).is_finite(),
+ "Basis with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Basis(x, infinite, infinite).is_finite(),
+ "Basis with two components infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(infinite, infinite, infinite).is_finite(),
+ "Basis with three components infinite should not be finite.");
+}
+
} // namespace TestBasis
#endif // TEST_BASIS_H
diff --git a/tests/core/math/test_color.h b/tests/core/math/test_color.h
index 51c3bc8bdc..c6550778e8 100644
--- a/tests/core/math/test_color.h
+++ b/tests/core/math/test_color.h
@@ -101,13 +101,13 @@ TEST_CASE("[Color] Reading methods") {
const Color dark_blue = Color(0, 0, 0.5, 0.4);
CHECK_MESSAGE(
- Math::is_equal_approx(dark_blue.get_h(), 240.0f / 360.0f),
+ dark_blue.get_h() == doctest::Approx(240.0f / 360.0f),
"The returned HSV hue should match the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(dark_blue.get_s(), 1.0f),
+ dark_blue.get_s() == doctest::Approx(1.0f),
"The returned HSV saturation should match the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(dark_blue.get_v(), 0.5f),
+ dark_blue.get_v() == doctest::Approx(0.5f),
"The returned HSV value should match the expected value.");
}
diff --git a/tests/core/math/test_expression.h b/tests/core/math/test_expression.h
index 6e3be541b0..9734fd9f36 100644
--- a/tests/core/math/test_expression.h
+++ b/tests/core/math/test_expression.h
@@ -83,42 +83,42 @@ TEST_CASE("[Expression] Floating-point arithmetic") {
expression.parse("-123.456") == OK,
"Float identity should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), -123.456),
+ double(expression.execute()) == doctest::Approx(-123.456),
"Float identity should return the expected result.");
CHECK_MESSAGE(
expression.parse("2.0 + 3.0") == OK,
"Float addition should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 5),
+ double(expression.execute()) == doctest::Approx(5),
"Float addition should return the expected result.");
CHECK_MESSAGE(
expression.parse("3.0 / 10") == OK,
"Float / integer division should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 0.3),
+ double(expression.execute()) == doctest::Approx(0.3),
"Float / integer division should return the expected result.");
CHECK_MESSAGE(
expression.parse("3 / 10.0") == OK,
"Basic integer / float division should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 0.3),
+ double(expression.execute()) == doctest::Approx(0.3),
"Basic integer / float division should return the expected result.");
CHECK_MESSAGE(
expression.parse("3.0 / 10.0") == OK,
"Float / float division should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 0.3),
+ double(expression.execute()) == doctest::Approx(0.3),
"Float / float division should return the expected result.");
CHECK_MESSAGE(
expression.parse("2.5 * (6.0 + 14.25) / 2.0 - 5.12345") == OK,
"Float multiplication-addition-subtraction-division should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 20.18905),
+ double(expression.execute()) == doctest::Approx(20.18905),
"Float multiplication-addition-subtraction-division should return the expected result.");
}
@@ -129,7 +129,7 @@ TEST_CASE("[Expression] Scientific notation") {
expression.parse("2.e5") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 200'000),
+ double(expression.execute()) == doctest::Approx(200'000),
"The expression should return the expected result.");
// The middle "e" is ignored here.
@@ -137,14 +137,14 @@ TEST_CASE("[Expression] Scientific notation") {
expression.parse("2e5") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 2e5),
+ double(expression.execute()) == doctest::Approx(2e5),
"The expression should return the expected result.");
CHECK_MESSAGE(
expression.parse("2e.5") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 2),
+ double(expression.execute()) == doctest::Approx(2),
"The expression should return the expected result.");
}
@@ -176,7 +176,7 @@ TEST_CASE("[Expression] Built-in functions") {
expression.parse("snapped(sin(0.5), 0.01)") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 0.48),
+ double(expression.execute()) == doctest::Approx(0.48),
"`snapped(sin(0.5), 0.01)` should return the expected result.");
CHECK_MESSAGE(
diff --git a/tests/core/math/test_geometry_2d.h b/tests/core/math/test_geometry_2d.h
index db4e6e2177..27c9e7f58b 100644
--- a/tests/core/math/test_geometry_2d.h
+++ b/tests/core/math/test_geometry_2d.h
@@ -64,7 +64,7 @@ TEST_CASE("[Geometry2D] Point in triangle") {
// This tests points on the edge of the triangle. They are treated as being outside the triangle.
// In `is_point_in_circle` and `is_point_in_polygon` they are treated as being inside, so in order the make
- // the behaviour consistent this may change in the future (see issue #44717 and PR #44274).
+ // the behavior consistent this may change in the future (see issue #44717 and PR #44274).
CHECK_FALSE(Geometry2D::is_point_in_triangle(Vector2(1, 1), Vector2(-1, 1), Vector2(0, -1), Vector2(1, 1)));
CHECK_FALSE(Geometry2D::is_point_in_triangle(Vector2(0, 1), Vector2(-1, 1), Vector2(0, -1), Vector2(1, 1)));
}
@@ -171,43 +171,43 @@ TEST_CASE("[Geometry2D] Segment intersection with circle") {
real_t one = 1.0;
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(4, 0), Vector2(0, 0), 1.0), one_quarter),
+ Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(4, 0), Vector2(0, 0), 1.0) == doctest::Approx(one_quarter),
"Segment from inside to outside of circle should intersect it.");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(4, 0), Vector2(0, 0), Vector2(0, 0), 1.0), three_quarters),
+ Geometry2D::segment_intersects_circle(Vector2(4, 0), Vector2(0, 0), Vector2(0, 0), 1.0) == doctest::Approx(three_quarters),
"Segment from outside to inside of circle should intersect it.");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-2, 0), Vector2(2, 0), Vector2(0, 0), 1.0), one_quarter),
+ Geometry2D::segment_intersects_circle(Vector2(-2, 0), Vector2(2, 0), Vector2(0, 0), 1.0) == doctest::Approx(one_quarter),
"Segment running through circle should intersect it.");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(-2, 0), Vector2(0, 0), 1.0), one_quarter),
+ Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(-2, 0), Vector2(0, 0), 1.0) == doctest::Approx(one_quarter),
"Segment running through circle should intersect it.");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one),
+ Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(1, 0), Vector2(0, 0), 1.0) == doctest::Approx(one),
"Segment starting inside the circle and ending on the circle should intersect it");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(0, 0), Vector2(0, 0), 1.0), zero),
+ Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(0, 0), Vector2(0, 0), 1.0) == doctest::Approx(zero),
"Segment starting on the circle and going inwards should intersect it");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(2, 0), Vector2(0, 0), 1.0), zero),
+ Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(2, 0), Vector2(0, 0), 1.0) == doctest::Approx(zero),
"Segment starting on the circle and going outwards should intersect it");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one),
+ Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(1, 0), Vector2(0, 0), 1.0) == doctest::Approx(one),
"Segment starting outside the circle and ending on the circle intersect it");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-1, 0), Vector2(1, 0), Vector2(0, 0), 2.0), minus_one),
+ Geometry2D::segment_intersects_circle(Vector2(-1, 0), Vector2(1, 0), Vector2(0, 0), 2.0) == doctest::Approx(minus_one),
"Segment completely within the circle should not intersect it");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(-1, 0), Vector2(0, 0), 2.0), minus_one),
+ Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(-1, 0), Vector2(0, 0), 2.0) == doctest::Approx(minus_one),
"Segment completely within the circle should not intersect it");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(3, 0), Vector2(0, 0), 1.0), minus_one),
+ Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(3, 0), Vector2(0, 0), 1.0) == doctest::Approx(minus_one),
"Segment completely outside the circle should not intersect it");
CHECK_MESSAGE(
- Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(3, 0), Vector2(2, 0), Vector2(0, 0), 1.0), minus_one),
+ Geometry2D::segment_intersects_circle(Vector2(3, 0), Vector2(2, 0), Vector2(0, 0), 1.0) == doctest::Approx(minus_one),
"Segment completely outside the circle should not intersect it");
}
diff --git a/tests/core/math/test_geometry_3d.h b/tests/core/math/test_geometry_3d.h
index 99a4ef2d46..23bbf1e183 100644
--- a/tests/core/math/test_geometry_3d.h
+++ b/tests/core/math/test_geometry_3d.h
@@ -63,7 +63,7 @@ TEST_CASE("[Geometry3D] Closest Distance Between Segments") {
p_1(p_p_1), p_2(p_p_2), p_3(p_p_3), p_4(p_p_4), want(p_want){};
};
Vector<Case> tt;
- tt.push_back(Case(Vector3(1, -2, 0), Vector3(1, 2, 0), Vector3(-1, 2, 0), Vector3(-1, -2, 0), 0.0f));
+ tt.push_back(Case(Vector3(1, -2, 0), Vector3(1, 2, 0), Vector3(-1, 2, 0), Vector3(-1, -2, 0), 2.0f));
for (int i = 0; i < tt.size(); ++i) {
Case current_case = tt[i];
float out = Geometry3D::get_closest_distance_between_segments(current_case.p_1, current_case.p_2, current_case.p_3, current_case.p_4);
diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h
new file mode 100644
index 0000000000..c9d14bbd21
--- /dev/null
+++ b/tests/core/math/test_math_funcs.h
@@ -0,0 +1,549 @@
+/*************************************************************************/
+/* test_math_funcs.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_MATH_FUNCS_H
+#define TEST_MATH_FUNCS_H
+
+#include "tests/test_macros.h"
+
+namespace TestMath {
+
+TEST_CASE("[Math] C++ macros") {
+ CHECK(MIN(-2, 2) == -2);
+ CHECK(MIN(600, 2) == 2);
+
+ CHECK(MAX(-2, 2) == 2);
+ CHECK(MAX(600, 2) == 600);
+
+ CHECK(CLAMP(600, -2, 2) == 2);
+ CHECK(CLAMP(620, 600, 650) == 620);
+ // `max` is lower than `min`.
+ CHECK(CLAMP(620, 600, 50) == 50);
+
+ CHECK(ABS(-5) == 5);
+ CHECK(ABS(0) == 0);
+ CHECK(ABS(5) == 5);
+
+ CHECK(SIGN(-5) == -1.0);
+ CHECK(SIGN(0) == 0.0);
+ CHECK(SIGN(5) == 1.0);
+}
+
+TEST_CASE("[Math] Power of two functions") {
+ CHECK(next_power_of_2(0) == 0);
+ CHECK(next_power_of_2(1) == 1);
+ CHECK(next_power_of_2(16) == 16);
+ CHECK(next_power_of_2(17) == 32);
+ CHECK(next_power_of_2(65535) == 65536);
+
+ CHECK(previous_power_of_2(0) == 0);
+ CHECK(previous_power_of_2(1) == 1);
+ CHECK(previous_power_of_2(16) == 16);
+ CHECK(previous_power_of_2(17) == 16);
+ CHECK(previous_power_of_2(65535) == 32768);
+
+ CHECK(closest_power_of_2(0) == 0);
+ CHECK(closest_power_of_2(1) == 1);
+ CHECK(closest_power_of_2(16) == 16);
+ CHECK(closest_power_of_2(17) == 16);
+ CHECK(closest_power_of_2(65535) == 65536);
+
+ CHECK(get_shift_from_power_of_2(0) == -1);
+ CHECK(get_shift_from_power_of_2(1) == 0);
+ CHECK(get_shift_from_power_of_2(16) == 4);
+ CHECK(get_shift_from_power_of_2(17) == -1);
+ CHECK(get_shift_from_power_of_2(65535) == -1);
+
+ CHECK(nearest_shift(0) == 0);
+ CHECK(nearest_shift(1) == 1);
+ CHECK(nearest_shift(16) == 5);
+ CHECK(nearest_shift(17) == 5);
+ CHECK(nearest_shift(65535) == 16);
+}
+
+TEST_CASE_TEMPLATE("[Math] abs", T, int, float, double) {
+ CHECK(Math::abs((T)-1) == (T)1);
+ CHECK(Math::abs((T)0) == (T)0);
+ CHECK(Math::abs((T)1) == (T)1);
+ CHECK(Math::abs((T)0.1) == (T)0.1);
+}
+
+TEST_CASE_TEMPLATE("[Math] round/floor/ceil", T, float, double) {
+ CHECK(Math::round((T)1.5) == (T)2.0);
+ CHECK(Math::round((T)1.6) == (T)2.0);
+ CHECK(Math::round((T)-1.5) == (T)-2.0);
+ CHECK(Math::round((T)-1.1) == (T)-1.0);
+
+ CHECK(Math::floor((T)1.5) == (T)1.0);
+ CHECK(Math::floor((T)-1.5) == (T)-2.0);
+
+ CHECK(Math::ceil((T)1.5) == (T)2.0);
+ CHECK(Math::ceil((T)-1.9) == (T)-1.0);
+}
+
+TEST_CASE_TEMPLATE("[Math] sin/cos/tan", T, float, double) {
+ CHECK(Math::sin((T)-0.1) == doctest::Approx((T)-0.0998334166));
+ CHECK(Math::sin((T)0.1) == doctest::Approx((T)0.0998334166));
+ CHECK(Math::sin((T)0.5) == doctest::Approx((T)0.4794255386));
+ CHECK(Math::sin((T)1.0) == doctest::Approx((T)0.8414709848));
+ CHECK(Math::sin((T)1.5) == doctest::Approx((T)0.9974949866));
+ CHECK(Math::sin((T)450.0) == doctest::Approx((T)-0.683283725));
+
+ CHECK(Math::cos((T)-0.1) == doctest::Approx((T)0.99500416530));
+ CHECK(Math::cos((T)0.1) == doctest::Approx((T)0.9950041653));
+ CHECK(Math::cos((T)0.5) == doctest::Approx((T)0.8775825619));
+ CHECK(Math::cos((T)1.0) == doctest::Approx((T)0.5403023059));
+ CHECK(Math::cos((T)1.5) == doctest::Approx((T)0.0707372017));
+ CHECK(Math::cos((T)450.0) == doctest::Approx((T)-0.7301529642));
+
+ CHECK(Math::tan((T)-0.1) == doctest::Approx((T)-0.1003346721));
+ CHECK(Math::tan((T)0.1) == doctest::Approx((T)0.1003346721));
+ CHECK(Math::tan((T)0.5) == doctest::Approx((T)0.5463024898));
+ CHECK(Math::tan((T)1.0) == doctest::Approx((T)1.5574077247));
+ CHECK(Math::tan((T)1.5) == doctest::Approx((T)14.1014199472));
+ CHECK(Math::tan((T)450.0) == doctest::Approx((T)0.9358090134));
+}
+
+TEST_CASE_TEMPLATE("[Math] sinh/cosh/tanh", T, float, double) {
+ CHECK(Math::sinh((T)-0.1) == doctest::Approx((T)-0.10016675));
+ CHECK(Math::sinh((T)0.1) == doctest::Approx((T)0.10016675));
+ CHECK(Math::sinh((T)0.5) == doctest::Approx((T)0.5210953055));
+ CHECK(Math::sinh((T)1.0) == doctest::Approx((T)1.1752011936));
+ CHECK(Math::sinh((T)1.5) == doctest::Approx((T)2.1292794551));
+
+ CHECK(Math::cosh((T)-0.1) == doctest::Approx((T)1.0050041681));
+ CHECK(Math::cosh((T)0.1) == doctest::Approx((T)1.0050041681));
+ CHECK(Math::cosh((T)0.5) == doctest::Approx((T)1.1276259652));
+ CHECK(Math::cosh((T)1.0) == doctest::Approx((T)1.5430806348));
+ CHECK(Math::cosh((T)1.5) == doctest::Approx((T)2.3524096152));
+
+ CHECK(Math::tanh((T)-0.1) == doctest::Approx((T)-0.0996679946));
+ CHECK(Math::tanh((T)0.1) == doctest::Approx((T)0.0996679946));
+ CHECK(Math::tanh((T)0.5) == doctest::Approx((T)0.4621171573));
+ CHECK(Math::tanh((T)1.0) == doctest::Approx((T)0.761594156));
+ CHECK(Math::tanh((T)1.5) == doctest::Approx((T)0.9051482536));
+ CHECK(Math::tanh((T)450.0) == doctest::Approx((T)1.0));
+}
+
+TEST_CASE_TEMPLATE("[Math] asin/acos/atan", T, float, double) {
+ CHECK(Math::asin((T)-0.1) == doctest::Approx((T)-0.1001674212));
+ CHECK(Math::asin((T)0.1) == doctest::Approx((T)0.1001674212));
+ CHECK(Math::asin((T)0.5) == doctest::Approx((T)0.5235987756));
+ CHECK(Math::asin((T)1.0) == doctest::Approx((T)1.5707963268));
+ CHECK(Math::is_nan(Math::asin((T)1.5)));
+ CHECK(Math::is_nan(Math::asin((T)450.0)));
+
+ CHECK(Math::acos((T)-0.1) == doctest::Approx((T)1.670963748));
+ CHECK(Math::acos((T)0.1) == doctest::Approx((T)1.4706289056));
+ CHECK(Math::acos((T)0.5) == doctest::Approx((T)1.0471975512));
+ CHECK(Math::acos((T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::is_nan(Math::acos((T)1.5)));
+ CHECK(Math::is_nan(Math::acos((T)450.0)));
+
+ CHECK(Math::atan((T)-0.1) == doctest::Approx((T)-0.0996686525));
+ CHECK(Math::atan((T)0.1) == doctest::Approx((T)0.0996686525));
+ CHECK(Math::atan((T)0.5) == doctest::Approx((T)0.463647609));
+ CHECK(Math::atan((T)1.0) == doctest::Approx((T)0.7853981634));
+ CHECK(Math::atan((T)1.5) == doctest::Approx((T)0.9827937232));
+ CHECK(Math::atan((T)450.0) == doctest::Approx((T)1.5685741082));
+}
+
+TEST_CASE_TEMPLATE("[Math] sinc/sincn/atan2", T, float, double) {
+ CHECK(Math::sinc((T)-0.1) == doctest::Approx((T)0.9983341665));
+ CHECK(Math::sinc((T)0.1) == doctest::Approx((T)0.9983341665));
+ CHECK(Math::sinc((T)0.5) == doctest::Approx((T)0.9588510772));
+ CHECK(Math::sinc((T)1.0) == doctest::Approx((T)0.8414709848));
+ CHECK(Math::sinc((T)1.5) == doctest::Approx((T)0.6649966577));
+ CHECK(Math::sinc((T)450.0) == doctest::Approx((T)-0.0015184083));
+
+ CHECK(Math::sincn((T)-0.1) == doctest::Approx((T)0.9836316431));
+ CHECK(Math::sincn((T)0.1) == doctest::Approx((T)0.9836316431));
+ CHECK(Math::sincn((T)0.5) == doctest::Approx((T)0.6366197724));
+ CHECK(Math::sincn((T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::sincn((T)1.5) == doctest::Approx((T)-0.2122065908));
+ CHECK(Math::sincn((T)450.0) == doctest::Approx((T)0.0));
+
+ CHECK(Math::atan2((T)-0.1, (T)0.5) == doctest::Approx((T)-0.1973955598));
+ CHECK(Math::atan2((T)0.1, (T)-0.5) == doctest::Approx((T)2.9441970937));
+ CHECK(Math::atan2((T)0.5, (T)1.5) == doctest::Approx((T)0.3217505544));
+ CHECK(Math::atan2((T)1.0, (T)2.5) == doctest::Approx((T)0.3805063771));
+ CHECK(Math::atan2((T)1.5, (T)1.0) == doctest::Approx((T)0.9827937232));
+ CHECK(Math::atan2((T)450.0, (T)1.0) == doctest::Approx((T)1.5685741082));
+}
+
+TEST_CASE_TEMPLATE("[Math] pow/log/log2/exp/sqrt", T, float, double) {
+ CHECK(Math::pow((T)-0.1, (T)2.0) == doctest::Approx((T)0.01));
+ CHECK(Math::pow((T)0.1, (T)2.5) == doctest::Approx((T)0.0031622777));
+ CHECK(Math::pow((T)0.5, (T)0.5) == doctest::Approx((T)0.7071067812));
+ CHECK(Math::pow((T)1.0, (T)1.0) == doctest::Approx((T)1.0));
+ CHECK(Math::pow((T)1.5, (T)-1.0) == doctest::Approx((T)0.6666666667));
+ CHECK(Math::pow((T)450.0, (T)-2.0) == doctest::Approx((T)0.0000049383));
+ CHECK(Math::pow((T)450.0, (T)0.0) == doctest::Approx((T)1.0));
+
+ CHECK(Math::is_nan(Math::log((T)-0.1)));
+ CHECK(Math::log((T)0.1) == doctest::Approx((T)-2.302585093));
+ CHECK(Math::log((T)0.5) == doctest::Approx((T)-0.6931471806));
+ CHECK(Math::log((T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::log((T)1.5) == doctest::Approx((T)0.4054651081));
+ CHECK(Math::log((T)450.0) == doctest::Approx((T)6.1092475828));
+
+ CHECK(Math::is_nan(Math::log2((T)-0.1)));
+ CHECK(Math::log2((T)0.1) == doctest::Approx((T)-3.3219280949));
+ CHECK(Math::log2((T)0.5) == doctest::Approx((T)-1.0));
+ CHECK(Math::log2((T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::log2((T)1.5) == doctest::Approx((T)0.5849625007));
+ CHECK(Math::log2((T)450.0) == doctest::Approx((T)8.8137811912));
+
+ CHECK(Math::exp((T)-0.1) == doctest::Approx((T)0.904837418));
+ CHECK(Math::exp((T)0.1) == doctest::Approx((T)1.1051709181));
+ CHECK(Math::exp((T)0.5) == doctest::Approx((T)1.6487212707));
+ CHECK(Math::exp((T)1.0) == doctest::Approx((T)2.7182818285));
+ CHECK(Math::exp((T)1.5) == doctest::Approx((T)4.4816890703));
+
+ CHECK(Math::is_nan(Math::sqrt((T)-0.1)));
+ CHECK(Math::sqrt((T)0.1) == doctest::Approx((T)0.316228));
+ CHECK(Math::sqrt((T)0.5) == doctest::Approx((T)0.707107));
+ CHECK(Math::sqrt((T)1.0) == doctest::Approx((T)1.0));
+ CHECK(Math::sqrt((T)1.5) == doctest::Approx((T)1.224745));
+}
+
+TEST_CASE_TEMPLATE("[Math] is_nan/is_inf", T, float, double) {
+ CHECK(!Math::is_nan((T)0.0));
+ CHECK(Math::is_nan((T)NAN));
+
+ CHECK(!Math::is_inf((T)0.0));
+ CHECK(Math::is_inf((T)INFINITY));
+}
+
+TEST_CASE_TEMPLATE("[Math] linear_to_db", T, float, double) {
+ CHECK(Math::linear_to_db((T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::linear_to_db((T)20.0) == doctest::Approx((T)26.0206));
+ CHECK(Math::is_inf(Math::linear_to_db((T)0.0)));
+ CHECK(Math::is_nan(Math::linear_to_db((T)-20.0)));
+}
+
+TEST_CASE_TEMPLATE("[Math] db_to_linear", T, float, double) {
+ CHECK(Math::db_to_linear((T)0.0) == doctest::Approx((T)1.0));
+ CHECK(Math::db_to_linear((T)1.0) == doctest::Approx((T)1.122018));
+ CHECK(Math::db_to_linear((T)20.0) == doctest::Approx((T)10.0));
+ CHECK(Math::db_to_linear((T)-20.0) == doctest::Approx((T)0.1));
+}
+
+TEST_CASE_TEMPLATE("[Math] step_decimals", T, float, double) {
+ CHECK(Math::step_decimals((T)-0.5) == 1);
+ CHECK(Math::step_decimals((T)0) == 0);
+ CHECK(Math::step_decimals((T)1) == 0);
+ CHECK(Math::step_decimals((T)0.1) == 1);
+ CHECK(Math::step_decimals((T)0.01) == 2);
+ CHECK(Math::step_decimals((T)0.001) == 3);
+ CHECK(Math::step_decimals((T)0.0001) == 4);
+ CHECK(Math::step_decimals((T)0.00001) == 5);
+ CHECK(Math::step_decimals((T)0.000001) == 6);
+ CHECK(Math::step_decimals((T)0.0000001) == 7);
+ CHECK(Math::step_decimals((T)0.00000001) == 8);
+ CHECK(Math::step_decimals((T)0.000000001) == 9);
+ // Too many decimals to handle.
+ CHECK(Math::step_decimals((T)0.0000000001) == 0);
+}
+
+TEST_CASE_TEMPLATE("[Math] range_step_decimals", T, float, double) {
+ CHECK(Math::range_step_decimals((T)0.000000001) == 9);
+ // Too many decimals to handle.
+ CHECK(Math::range_step_decimals((T)0.0000000001) == 0);
+ // Should be treated as a step of 0 for use by the editor.
+ CHECK(Math::range_step_decimals((T)0.0) == 16);
+ CHECK(Math::range_step_decimals((T)-0.5) == 16);
+}
+
+TEST_CASE_TEMPLATE("[Math] lerp", T, float, double) {
+ CHECK(Math::lerp((T)2.0, (T)5.0, (T)-0.1) == doctest::Approx((T)1.7));
+ CHECK(Math::lerp((T)2.0, (T)5.0, (T)0.0) == doctest::Approx((T)2.0));
+ CHECK(Math::lerp((T)2.0, (T)5.0, (T)0.1) == doctest::Approx((T)2.3));
+ CHECK(Math::lerp((T)2.0, (T)5.0, (T)1.0) == doctest::Approx((T)5.0));
+ CHECK(Math::lerp((T)2.0, (T)5.0, (T)2.0) == doctest::Approx((T)8.0));
+
+ CHECK(Math::lerp((T)-2.0, (T)-5.0, (T)-0.1) == doctest::Approx((T)-1.7));
+ CHECK(Math::lerp((T)-2.0, (T)-5.0, (T)0.0) == doctest::Approx((T)-2.0));
+ CHECK(Math::lerp((T)-2.0, (T)-5.0, (T)0.1) == doctest::Approx((T)-2.3));
+ CHECK(Math::lerp((T)-2.0, (T)-5.0, (T)1.0) == doctest::Approx((T)-5.0));
+ CHECK(Math::lerp((T)-2.0, (T)-5.0, (T)2.0) == doctest::Approx((T)-8.0));
+}
+
+TEST_CASE_TEMPLATE("[Math] inverse_lerp", T, float, double) {
+ CHECK(Math::inverse_lerp((T)2.0, (T)5.0, (T)1.7) == doctest::Approx((T)-0.1));
+ CHECK(Math::inverse_lerp((T)2.0, (T)5.0, (T)2.0) == doctest::Approx((T)0.0));
+ CHECK(Math::inverse_lerp((T)2.0, (T)5.0, (T)2.3) == doctest::Approx((T)0.1));
+ CHECK(Math::inverse_lerp((T)2.0, (T)5.0, (T)5.0) == doctest::Approx((T)1.0));
+ CHECK(Math::inverse_lerp((T)2.0, (T)5.0, (T)8.0) == doctest::Approx((T)2.0));
+
+ CHECK(Math::inverse_lerp((T)-2.0, (T)-5.0, (T)-1.7) == doctest::Approx((T)-0.1));
+ CHECK(Math::inverse_lerp((T)-2.0, (T)-5.0, (T)-2.0) == doctest::Approx((T)0.0));
+ CHECK(Math::inverse_lerp((T)-2.0, (T)-5.0, (T)-2.3) == doctest::Approx((T)0.1));
+ CHECK(Math::inverse_lerp((T)-2.0, (T)-5.0, (T)-5.0) == doctest::Approx((T)1.0));
+ CHECK(Math::inverse_lerp((T)-2.0, (T)-5.0, (T)-8.0) == doctest::Approx((T)2.0));
+}
+
+TEST_CASE_TEMPLATE("[Math] remap", T, float, double) {
+ CHECK(Math::remap((T)50.0, (T)100.0, (T)200.0, (T)0.0, (T)1000.0) == doctest::Approx((T)-500.0));
+ CHECK(Math::remap((T)100.0, (T)100.0, (T)200.0, (T)0.0, (T)1000.0) == doctest::Approx((T)0.0));
+ CHECK(Math::remap((T)200.0, (T)100.0, (T)200.0, (T)0.0, (T)1000.0) == doctest::Approx((T)1000.0));
+ CHECK(Math::remap((T)250.0, (T)100.0, (T)200.0, (T)0.0, (T)1000.0) == doctest::Approx((T)1500.0));
+
+ CHECK(Math::remap((T)-50.0, (T)-100.0, (T)-200.0, (T)0.0, (T)1000.0) == doctest::Approx((T)-500.0));
+ CHECK(Math::remap((T)-100.0, (T)-100.0, (T)-200.0, (T)0.0, (T)1000.0) == doctest::Approx((T)0.0));
+ CHECK(Math::remap((T)-200.0, (T)-100.0, (T)-200.0, (T)0.0, (T)1000.0) == doctest::Approx((T)1000.0));
+ CHECK(Math::remap((T)-250.0, (T)-100.0, (T)-200.0, (T)0.0, (T)1000.0) == doctest::Approx((T)1500.0));
+
+ CHECK(Math::remap((T)-50.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)500.0));
+ CHECK(Math::remap((T)-100.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)0.0));
+ CHECK(Math::remap((T)-200.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)-1000.0));
+ CHECK(Math::remap((T)-250.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)-1500.0));
+}
+
+TEST_CASE_TEMPLATE("[Math] lerp_angle", T, float, double) {
+ // Counter-clockwise rotation.
+ CHECK(Math::lerp_angle((T)0.24 * Math_TAU, 0.75 * Math_TAU, 0.5) == doctest::Approx((T)-0.005 * Math_TAU));
+ // Counter-clockwise rotation.
+ CHECK(Math::lerp_angle((T)0.25 * Math_TAU, 0.75 * Math_TAU, 0.5) == doctest::Approx((T)0.0));
+ // Clockwise rotation.
+ CHECK(Math::lerp_angle((T)0.26 * Math_TAU, 0.75 * Math_TAU, 0.5) == doctest::Approx((T)0.505 * Math_TAU));
+
+ CHECK(Math::lerp_angle((T)-0.25 * Math_TAU, 1.25 * Math_TAU, 0.5) == doctest::Approx((T)-0.5 * Math_TAU));
+ CHECK(Math::lerp_angle((T)0.72 * Math_TAU, 1.44 * Math_TAU, 0.96) == doctest::Approx((T)0.4512 * Math_TAU));
+ CHECK(Math::lerp_angle((T)0.72 * Math_TAU, 1.44 * Math_TAU, 1.04) == doctest::Approx((T)0.4288 * Math_TAU));
+
+ // Initial and final angles are effectively identical, so the value returned
+ // should always be the same regardless of the `weight` parameter.
+ CHECK(Math::lerp_angle((T)-4 * Math_TAU, 4 * Math_TAU, -1.0) == doctest::Approx((T)-4.0 * Math_TAU));
+ CHECK(Math::lerp_angle((T)-4 * Math_TAU, 4 * Math_TAU, 0.0) == doctest::Approx((T)-4.0 * Math_TAU));
+ CHECK(Math::lerp_angle((T)-4 * Math_TAU, 4 * Math_TAU, 0.5) == doctest::Approx((T)-4.0 * Math_TAU));
+ CHECK(Math::lerp_angle((T)-4 * Math_TAU, 4 * Math_TAU, 1.0) == doctest::Approx((T)-4.0 * Math_TAU));
+ CHECK(Math::lerp_angle((T)-4 * Math_TAU, 4 * Math_TAU, 500.0) == doctest::Approx((T)-4.0 * Math_TAU));
+}
+
+TEST_CASE_TEMPLATE("[Math] move_toward", T, float, double) {
+ CHECK(Math::move_toward(2.0, 5.0, -1.0) == doctest::Approx((T)1.0));
+ CHECK(Math::move_toward(2.0, 5.0, 2.5) == doctest::Approx((T)4.5));
+ CHECK(Math::move_toward(2.0, 5.0, 4.0) == doctest::Approx((T)5.0));
+ CHECK(Math::move_toward(-2.0, -5.0, -1.0) == doctest::Approx((T)-1.0));
+ CHECK(Math::move_toward(-2.0, -5.0, 2.5) == doctest::Approx((T)-4.5));
+ CHECK(Math::move_toward(-2.0, -5.0, 4.0) == doctest::Approx((T)-5.0));
+}
+
+TEST_CASE_TEMPLATE("[Math] smoothstep", T, float, double) {
+ CHECK(Math::smoothstep((T)0.0, (T)2.0, (T)-5.0) == doctest::Approx((T)0.0));
+ CHECK(Math::smoothstep((T)0.0, (T)2.0, (T)0.5) == doctest::Approx((T)0.15625));
+ CHECK(Math::smoothstep((T)0.0, (T)2.0, (T)1.0) == doctest::Approx((T)0.5));
+ CHECK(Math::smoothstep((T)0.0, (T)2.0, (T)2.0) == doctest::Approx((T)1.0));
+}
+
+TEST_CASE("[Math] ease") {
+ CHECK(Math::ease(0.1, 1.0) == doctest::Approx(0.1));
+ CHECK(Math::ease(0.1, 2.0) == doctest::Approx(0.01));
+ CHECK(Math::ease(0.1, 0.5) == doctest::Approx(0.19));
+ CHECK(Math::ease(0.1, 0.0) == doctest::Approx(0));
+ CHECK(Math::ease(0.1, -0.5) == doctest::Approx(0.2236067977));
+ CHECK(Math::ease(0.1, -1.0) == doctest::Approx(0.1));
+ CHECK(Math::ease(0.1, -2.0) == doctest::Approx(0.02));
+
+ CHECK(Math::ease(-1.0, 1.0) == doctest::Approx(0));
+ CHECK(Math::ease(-1.0, 2.0) == doctest::Approx(0));
+ CHECK(Math::ease(-1.0, 0.5) == doctest::Approx(0));
+ CHECK(Math::ease(-1.0, 0.0) == doctest::Approx(0));
+ CHECK(Math::ease(-1.0, -0.5) == doctest::Approx(0));
+ CHECK(Math::ease(-1.0, -1.0) == doctest::Approx(0));
+ CHECK(Math::ease(-1.0, -2.0) == doctest::Approx(0));
+}
+
+TEST_CASE("[Math] snapped") {
+ CHECK(Math::snapped(0.5, 0.04) == doctest::Approx(0.52));
+ CHECK(Math::snapped(-0.5, 0.04) == doctest::Approx(-0.48));
+ CHECK(Math::snapped(0.0, 0.04) == doctest::Approx(0));
+ CHECK(Math::snapped(128'000.025, 0.04) == doctest::Approx(128'000.04));
+
+ CHECK(Math::snapped(0.5, 400) == doctest::Approx(0));
+ CHECK(Math::snapped(-0.5, 400) == doctest::Approx(0));
+ CHECK(Math::snapped(0.0, 400) == doctest::Approx(0));
+ CHECK(Math::snapped(128'000.025, 400) == doctest::Approx(128'000.0));
+
+ CHECK(Math::snapped(0.5, 0.0) == doctest::Approx(0.5));
+ CHECK(Math::snapped(-0.5, 0.0) == doctest::Approx(-0.5));
+ CHECK(Math::snapped(0.0, 0.0) == doctest::Approx(0.0));
+ CHECK(Math::snapped(128'000.025, 0.0) == doctest::Approx(128'000.0));
+
+ CHECK(Math::snapped(0.5, -1.0) == doctest::Approx(0));
+ CHECK(Math::snapped(-0.5, -1.0) == doctest::Approx(-1.0));
+ CHECK(Math::snapped(0.0, -1.0) == doctest::Approx(0));
+ CHECK(Math::snapped(128'000.025, -1.0) == doctest::Approx(128'000.0));
+}
+
+TEST_CASE("[Math] larger_prime") {
+ CHECK(Math::larger_prime(0) == 5);
+ CHECK(Math::larger_prime(1) == 5);
+ CHECK(Math::larger_prime(2) == 5);
+ CHECK(Math::larger_prime(5) == 13);
+ CHECK(Math::larger_prime(500) == 769);
+ CHECK(Math::larger_prime(1'000'000) == 1'572'869);
+ CHECK(Math::larger_prime(1'000'000'000) == 1'610'612'741);
+
+ // The next prime is larger than `INT32_MAX` and is not present in the built-in prime table.
+ ERR_PRINT_OFF;
+ CHECK(Math::larger_prime(2'000'000'000) == 0);
+ ERR_PRINT_ON;
+}
+
+TEST_CASE_TEMPLATE("[Math] fmod", T, float, double) {
+ CHECK(Math::fmod((T)-2.0, (T)0.3) == doctest::Approx((T)-0.2));
+ CHECK(Math::fmod((T)0.0, (T)0.3) == doctest::Approx((T)0.0));
+ CHECK(Math::fmod((T)2.0, (T)0.3) == doctest::Approx((T)0.2));
+
+ CHECK(Math::fmod((T)-2.0, (T)-0.3) == doctest::Approx((T)-0.2));
+ CHECK(Math::fmod((T)0.0, (T)-0.3) == doctest::Approx((T)0.0));
+ CHECK(Math::fmod((T)2.0, (T)-0.3) == doctest::Approx((T)0.2));
+}
+
+TEST_CASE_TEMPLATE("[Math] fposmod", T, float, double) {
+ CHECK(Math::fposmod((T)-2.0, (T)0.3) == doctest::Approx((T)0.1));
+ CHECK(Math::fposmod((T)0.0, (T)0.3) == doctest::Approx((T)0.0));
+ CHECK(Math::fposmod((T)2.0, (T)0.3) == doctest::Approx((T)0.2));
+
+ CHECK(Math::fposmod((T)-2.0, (T)-0.3) == doctest::Approx((T)-0.2));
+ CHECK(Math::fposmod((T)0.0, (T)-0.3) == doctest::Approx((T)0.0));
+ CHECK(Math::fposmod((T)2.0, (T)-0.3) == doctest::Approx((T)-0.1));
+}
+
+TEST_CASE_TEMPLATE("[Math] fposmodp", T, float, double) {
+ CHECK(Math::fposmodp((T)-2.0, (T)0.3) == doctest::Approx((T)0.1));
+ CHECK(Math::fposmodp((T)0.0, (T)0.3) == doctest::Approx((T)0.0));
+ CHECK(Math::fposmodp((T)2.0, (T)0.3) == doctest::Approx((T)0.2));
+
+ CHECK(Math::fposmodp((T)-2.0, (T)-0.3) == doctest::Approx((T)-0.5));
+ CHECK(Math::fposmodp((T)0.0, (T)-0.3) == doctest::Approx((T)0.0));
+ CHECK(Math::fposmodp((T)2.0, (T)-0.3) == doctest::Approx((T)0.2));
+}
+
+TEST_CASE("[Math] posmod") {
+ CHECK(Math::posmod(-20, 3) == 1);
+ CHECK(Math::posmod(0, 3) == 0);
+ CHECK(Math::posmod(20, 3) == 2);
+ CHECK(Math::posmod(-20, -3) == -2);
+ CHECK(Math::posmod(0, -3) == 0);
+ CHECK(Math::posmod(20, -3) == -1);
+}
+
+TEST_CASE("[Math] wrapi") {
+ CHECK(Math::wrapi(-30, -20, 160) == 150);
+ CHECK(Math::wrapi(30, -20, 160) == 30);
+ CHECK(Math::wrapi(300, -20, 160) == 120);
+ CHECK(Math::wrapi(300'000'000'000, -20, 160) == 120);
+}
+
+TEST_CASE_TEMPLATE("[Math] wrapf", T, float, double) {
+ CHECK(Math::wrapf((T)-30.0, (T)-20.0, (T)160.0) == doctest::Approx((T)150.0));
+ CHECK(Math::wrapf((T)30.0, (T)-2.0, (T)160.0) == doctest::Approx((T)30.0));
+ CHECK(Math::wrapf((T)300.0, (T)-20.0, (T)160.0) == doctest::Approx((T)120.0));
+
+ CHECK(Math::wrapf(300'000'000'000.0, -20.0, 160.0) == doctest::Approx((T)120.0));
+ // float's precision is too low for 300'000'000'000.0, so we reduce it by a factor of 1000.
+ CHECK(Math::wrapf((float)300'000'000.0, (float)-20.0, (float)160.0) == doctest::Approx((T)128.0));
+}
+
+TEST_CASE_TEMPLATE("[Math] fract", T, float, double) {
+ CHECK(Math::fract((T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::fract((T)77.8) == doctest::Approx((T)0.8));
+ CHECK(Math::fract((T)-10.1) == doctest::Approx((T)0.9));
+}
+
+TEST_CASE_TEMPLATE("[Math] pingpong", T, float, double) {
+ CHECK(Math::pingpong((T)0.0, (T)0.0) == doctest::Approx((T)0.0));
+ CHECK(Math::pingpong((T)1.0, (T)1.0) == doctest::Approx((T)1.0));
+ CHECK(Math::pingpong((T)0.5, (T)2.0) == doctest::Approx((T)0.5));
+ CHECK(Math::pingpong((T)3.5, (T)2.0) == doctest::Approx((T)0.5));
+ CHECK(Math::pingpong((T)11.5, (T)2.0) == doctest::Approx((T)0.5));
+ CHECK(Math::pingpong((T)-2.5, (T)2.0) == doctest::Approx((T)1.5));
+}
+
+TEST_CASE_TEMPLATE("[Math] deg_to_rad/rad_to_deg", T, float, double) {
+ CHECK(Math::deg_to_rad((T)180.0) == doctest::Approx((T)Math_PI));
+ CHECK(Math::deg_to_rad((T)-27.0) == doctest::Approx((T)-0.471239));
+
+ CHECK(Math::rad_to_deg((T)Math_PI) == doctest::Approx((T)180.0));
+ CHECK(Math::rad_to_deg((T)-1.5) == doctest::Approx((T)-85.94366927));
+}
+
+TEST_CASE_TEMPLATE("[Math] cubic_interpolate", T, float, double) {
+ CHECK(Math::cubic_interpolate((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)0.0) == doctest::Approx((T)0.2));
+ CHECK(Math::cubic_interpolate((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)0.25) == doctest::Approx((T)0.33125));
+ CHECK(Math::cubic_interpolate((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)0.5) == doctest::Approx((T)0.5));
+ CHECK(Math::cubic_interpolate((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)0.75) == doctest::Approx((T)0.66875));
+ CHECK(Math::cubic_interpolate((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)1.0) == doctest::Approx((T)0.8));
+
+ CHECK(Math::cubic_interpolate((T)20.2, (T)30.1, (T)-100.0, (T)32.0, (T)-50.0) == doctest::Approx((T)-6662732.3));
+ CHECK(Math::cubic_interpolate((T)20.2, (T)30.1, (T)-100.0, (T)32.0, (T)-5.0) == doctest::Approx((T)-9356.3));
+ CHECK(Math::cubic_interpolate((T)20.2, (T)30.1, (T)-100.0, (T)32.0, (T)0.0) == doctest::Approx((T)20.2));
+ CHECK(Math::cubic_interpolate((T)20.2, (T)30.1, (T)-100.0, (T)32.0, (T)1.0) == doctest::Approx((T)30.1));
+ CHECK(Math::cubic_interpolate((T)20.2, (T)30.1, (T)-100.0, (T)32.0, (T)4.0) == doctest::Approx((T)1853.2));
+}
+
+TEST_CASE_TEMPLATE("[Math] cubic_interpolate_angle", T, float, double) {
+ CHECK(Math::cubic_interpolate_angle((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)0.0) == doctest::Approx((T)Math_PI * (1.0 / 6.0)));
+ CHECK(Math::cubic_interpolate_angle((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)0.25) == doctest::Approx((T)0.973566));
+ CHECK(Math::cubic_interpolate_angle((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)0.5) == doctest::Approx((T)Math_PI / 2.0));
+ CHECK(Math::cubic_interpolate_angle((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)0.75) == doctest::Approx((T)2.16803));
+ CHECK(Math::cubic_interpolate_angle((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)1.0) == doctest::Approx((T)Math_PI * (5.0 / 6.0)));
+}
+
+TEST_CASE_TEMPLATE("[Math] cubic_interpolate_in_time", T, float, double) {
+ CHECK(Math::cubic_interpolate_in_time((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)0.0, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::cubic_interpolate_in_time((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)0.25, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)0.1625));
+ CHECK(Math::cubic_interpolate_in_time((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)0.5, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)0.4));
+ CHECK(Math::cubic_interpolate_in_time((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)0.75, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)0.6375));
+ CHECK(Math::cubic_interpolate_in_time((T)0.2, (T)0.8, (T)0.0, (T)1.0, (T)1.0, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)0.8));
+}
+
+TEST_CASE_TEMPLATE("[Math] cubic_interpolate_angle_in_time", T, float, double) {
+ CHECK(Math::cubic_interpolate_angle_in_time((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)0.0, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::cubic_interpolate_angle_in_time((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)0.25, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)0.494964));
+ CHECK(Math::cubic_interpolate_angle_in_time((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)0.5, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)1.27627));
+ CHECK(Math::cubic_interpolate_angle_in_time((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)0.75, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)2.07394));
+ CHECK(Math::cubic_interpolate_angle_in_time((T)(Math_PI * (1.0 / 6.0)), (T)(Math_PI * (5.0 / 6.0)), (T)0.0, (T)Math_PI, (T)1.0, (T)0.5, (T)0.0, (T)1.0) == doctest::Approx((T)Math_PI * (5.0 / 6.0)));
+}
+
+TEST_CASE_TEMPLATE("[Math] bezier_interpolate", T, float, double) {
+ CHECK(Math::bezier_interpolate((T)0.0, (T)0.2, (T)0.8, (T)1.0, (T)0.0) == doctest::Approx((T)0.0));
+ CHECK(Math::bezier_interpolate((T)0.0, (T)0.2, (T)0.8, (T)1.0, (T)0.25) == doctest::Approx((T)0.2125));
+ CHECK(Math::bezier_interpolate((T)0.0, (T)0.2, (T)0.8, (T)1.0, (T)0.5) == doctest::Approx((T)0.5));
+ CHECK(Math::bezier_interpolate((T)0.0, (T)0.2, (T)0.8, (T)1.0, (T)0.75) == doctest::Approx((T)0.7875));
+ CHECK(Math::bezier_interpolate((T)0.0, (T)0.2, (T)0.8, (T)1.0, (T)1.0) == doctest::Approx((T)1.0));
+}
+
+} // namespace TestMath
+
+#endif // TEST_MATH_FUNCS_H
diff --git a/tests/core/math/test_plane.h b/tests/core/math/test_plane.h
index d81a5af1ce..84d9a0ff7d 100644
--- a/tests/core/math/test_plane.h
+++ b/tests/core/math/test_plane.h
@@ -167,6 +167,29 @@ TEST_CASE("[Plane] Intersection") {
vec_out.is_equal_approx(Vector3(1, 1, 1)),
"intersects_segment() should modify vec_out to the expected result.");
}
+
+TEST_CASE("[Plane] Finite number checks") {
+ const Vector3 x(0, 1, 2);
+ const Vector3 infinite_vec(NAN, NAN, NAN);
+ const real_t y = 0;
+ const real_t infinite_y = NAN;
+
+ CHECK_MESSAGE(
+ Plane(x, y).is_finite(),
+ "Plane with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Plane(x, infinite_y).is_finite(),
+ "Plane with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Plane(infinite_vec, y).is_finite(),
+ "Plane with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Plane(infinite_vec, infinite_y).is_finite(),
+ "Plane with two components infinite should not be finite.");
+}
+
} // namespace TestPlane
#endif // TEST_PLANE_H
diff --git a/tests/core/math/test_quaternion.h b/tests/core/math/test_quaternion.h
index 94eef6c463..c3ae322991 100644
--- a/tests/core/math/test_quaternion.h
+++ b/tests/core/math/test_quaternion.h
@@ -41,15 +41,15 @@
namespace TestQuaternion {
Quaternion quat_euler_yxz_deg(Vector3 angle) {
- double yaw = Math::deg2rad(angle[1]);
- double pitch = Math::deg2rad(angle[0]);
- double roll = Math::deg2rad(angle[2]);
+ double yaw = Math::deg_to_rad(angle[1]);
+ double pitch = Math::deg_to_rad(angle[0]);
+ double roll = Math::deg_to_rad(angle[2]);
// Generate YXZ (Z-then-X-then-Y) Quaternion using single-axis Euler
// constructor and quaternion product, both tested separately.
- Quaternion q_y(Vector3(0.0, yaw, 0.0));
- Quaternion q_p(Vector3(pitch, 0.0, 0.0));
- Quaternion q_r(Vector3(0.0, 0.0, roll));
+ Quaternion q_y = Quaternion::from_euler(Vector3(0.0, yaw, 0.0));
+ Quaternion q_p = Quaternion::from_euler(Vector3(pitch, 0.0, 0.0));
+ Quaternion q_r = Quaternion::from_euler(Vector3(0.0, 0.0, roll));
// Roll-Z is followed by Pitch-X, then Yaw-Y.
Quaternion q_yxz = q_y * q_p * q_r;
@@ -77,7 +77,7 @@ TEST_CASE("[Quaternion] Construct x,y,z,w") {
TEST_CASE("[Quaternion] Construct AxisAngle 1") {
// Easy to visualize: 120 deg about X-axis.
- Quaternion q(Vector3(1.0, 0.0, 0.0), Math::deg2rad(120.0));
+ Quaternion q(Vector3(1.0, 0.0, 0.0), Math::deg_to_rad(120.0));
// 0.866 isn't close enough; doctest::Approx doesn't cut much slack!
CHECK(q[0] == doctest::Approx(0.866025)); // Sine of half the angle.
@@ -88,7 +88,7 @@ TEST_CASE("[Quaternion] Construct AxisAngle 1") {
TEST_CASE("[Quaternion] Construct AxisAngle 2") {
// Easy to visualize: 30 deg about Y-axis.
- Quaternion q(Vector3(0.0, 1.0, 0.0), Math::deg2rad(30.0));
+ Quaternion q(Vector3(0.0, 1.0, 0.0), Math::deg_to_rad(30.0));
CHECK(q[0] == doctest::Approx(0.0));
CHECK(q[1] == doctest::Approx(0.258819)); // Sine of half the angle.
@@ -98,7 +98,7 @@ TEST_CASE("[Quaternion] Construct AxisAngle 2") {
TEST_CASE("[Quaternion] Construct AxisAngle 3") {
// Easy to visualize: 60 deg about Z-axis.
- Quaternion q(Vector3(0.0, 0.0, 1.0), Math::deg2rad(60.0));
+ Quaternion q(Vector3(0.0, 0.0, 1.0), Math::deg_to_rad(60.0));
CHECK(q[0] == doctest::Approx(0.0));
CHECK(q[1] == doctest::Approx(0.0));
@@ -109,7 +109,7 @@ TEST_CASE("[Quaternion] Construct AxisAngle 3") {
TEST_CASE("[Quaternion] Construct AxisAngle 4") {
// More complex & hard to visualize, so test w/ data from online calculator.
Vector3 axis(1.0, 2.0, 0.5);
- Quaternion q(axis.normalized(), Math::deg2rad(35.0));
+ Quaternion q(axis.normalized(), Math::deg_to_rad(35.0));
CHECK(q[0] == doctest::Approx(0.131239));
CHECK(q[1] == doctest::Approx(0.262478));
@@ -119,7 +119,7 @@ TEST_CASE("[Quaternion] Construct AxisAngle 4") {
TEST_CASE("[Quaternion] Construct from Quaternion") {
Vector3 axis(1.0, 2.0, 0.5);
- Quaternion q_src(axis.normalized(), Math::deg2rad(35.0));
+ Quaternion q_src(axis.normalized(), Math::deg_to_rad(35.0));
Quaternion q(q_src);
CHECK(q[0] == doctest::Approx(0.131239));
@@ -129,26 +129,26 @@ TEST_CASE("[Quaternion] Construct from Quaternion") {
}
TEST_CASE("[Quaternion] Construct Euler SingleAxis") {
- double yaw = Math::deg2rad(45.0);
- double pitch = Math::deg2rad(30.0);
- double roll = Math::deg2rad(10.0);
+ double yaw = Math::deg_to_rad(45.0);
+ double pitch = Math::deg_to_rad(30.0);
+ double roll = Math::deg_to_rad(10.0);
Vector3 euler_y(0.0, yaw, 0.0);
- Quaternion q_y(euler_y);
+ Quaternion q_y = Quaternion::from_euler(euler_y);
CHECK(q_y[0] == doctest::Approx(0.0));
CHECK(q_y[1] == doctest::Approx(0.382684));
CHECK(q_y[2] == doctest::Approx(0.0));
CHECK(q_y[3] == doctest::Approx(0.923879));
Vector3 euler_p(pitch, 0.0, 0.0);
- Quaternion q_p(euler_p);
+ Quaternion q_p = Quaternion::from_euler(euler_p);
CHECK(q_p[0] == doctest::Approx(0.258819));
CHECK(q_p[1] == doctest::Approx(0.0));
CHECK(q_p[2] == doctest::Approx(0.0));
CHECK(q_p[3] == doctest::Approx(0.965926));
Vector3 euler_r(0.0, 0.0, roll);
- Quaternion q_r(euler_r);
+ Quaternion q_r = Quaternion::from_euler(euler_r);
CHECK(q_r[0] == doctest::Approx(0.0));
CHECK(q_r[1] == doctest::Approx(0.0));
CHECK(q_r[2] == doctest::Approx(0.0871558));
@@ -156,50 +156,50 @@ TEST_CASE("[Quaternion] Construct Euler SingleAxis") {
}
TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") {
- double yaw = Math::deg2rad(45.0);
- double pitch = Math::deg2rad(30.0);
- double roll = Math::deg2rad(10.0);
+ double yaw = Math::deg_to_rad(45.0);
+ double pitch = Math::deg_to_rad(30.0);
+ double roll = Math::deg_to_rad(10.0);
- // Generate YXZ comparision data (Z-then-X-then-Y) using single-axis Euler
+ // Generate YXZ comparison data (Z-then-X-then-Y) using single-axis Euler
// constructor and quaternion product, both tested separately.
Vector3 euler_y(0.0, yaw, 0.0);
- Quaternion q_y(euler_y);
+ Quaternion q_y = Quaternion::from_euler(euler_y);
Vector3 euler_p(pitch, 0.0, 0.0);
- Quaternion q_p(euler_p);
+ Quaternion q_p = Quaternion::from_euler(euler_p);
Vector3 euler_r(0.0, 0.0, roll);
- Quaternion q_r(euler_r);
+ Quaternion q_r = Quaternion::from_euler(euler_r);
- // Roll-Z is followed by Pitch-X.
- Quaternion check_xz = q_p * q_r;
- // Then Yaw-Y follows both.
- Quaternion check_yxz = q_y * check_xz;
+ // Instrinsically, Yaw-Y then Pitch-X then Roll-Z.
+ // Extrinsically, Roll-Z is followed by Pitch-X, then Yaw-Y.
+ Quaternion check_yxz = q_y * q_p * q_r;
// Test construction from YXZ Euler angles.
Vector3 euler_yxz(pitch, yaw, roll);
- Quaternion q(euler_yxz);
+ Quaternion q = Quaternion::from_euler(euler_yxz);
CHECK(q[0] == doctest::Approx(check_yxz[0]));
CHECK(q[1] == doctest::Approx(check_yxz[1]));
CHECK(q[2] == doctest::Approx(check_yxz[2]));
CHECK(q[3] == doctest::Approx(check_yxz[3]));
- // Sneak in a test of is_equal_approx.
CHECK(q.is_equal_approx(check_yxz));
+ CHECK(q.get_euler().is_equal_approx(euler_yxz));
+ CHECK(check_yxz.get_euler().is_equal_approx(euler_yxz));
}
TEST_CASE("[Quaternion] Construct Basis Euler") {
- double yaw = Math::deg2rad(45.0);
- double pitch = Math::deg2rad(30.0);
- double roll = Math::deg2rad(10.0);
+ double yaw = Math::deg_to_rad(45.0);
+ double pitch = Math::deg_to_rad(30.0);
+ double roll = Math::deg_to_rad(10.0);
Vector3 euler_yxz(pitch, yaw, roll);
- Quaternion q_yxz(euler_yxz);
- Basis basis_axes(euler_yxz);
+ Quaternion q_yxz = Quaternion::from_euler(euler_yxz);
+ Basis basis_axes = Basis::from_euler(euler_yxz);
Quaternion q(basis_axes);
CHECK(q.is_equal_approx(q_yxz));
}
TEST_CASE("[Quaternion] Construct Basis Axes") {
// Arbitrary Euler angles.
- Vector3 euler_yxz(Math::deg2rad(31.41), Math::deg2rad(-49.16), Math::deg2rad(12.34));
+ Vector3 euler_yxz(Math::deg_to_rad(31.41), Math::deg_to_rad(-49.16), Math::deg_to_rad(12.34));
// Basis vectors from online calculation of rotation matrix.
Vector3 i_unit(0.5545787, 0.1823950, 0.8118957);
Vector3 j_unit(-0.5249245, 0.8337420, 0.1712555);
@@ -209,7 +209,7 @@ TEST_CASE("[Quaternion] Construct Basis Axes") {
// Quaternion from local calculation.
Quaternion q_local = quat_euler_yxz_deg(Vector3(31.41, -49.16, 12.34));
// Quaternion from Euler angles constructor.
- Quaternion q_euler(euler_yxz);
+ Quaternion q_euler = Quaternion::from_euler(euler_yxz);
CHECK(q_calc.is_equal_approx(q_local));
CHECK(q_local.is_equal_approx(q_euler));
@@ -218,7 +218,7 @@ TEST_CASE("[Quaternion] Construct Basis Axes") {
// This is by design, but may be subject to change.
// Workaround by constructing Basis from Euler angles.
// basis_axes = Basis(i_unit, j_unit, k_unit);
- Basis basis_axes(euler_yxz);
+ Basis basis_axes = Basis::from_euler(euler_yxz);
Quaternion q(basis_axes);
CHECK(basis_axes.get_column(0).is_equal_approx(i_unit));
@@ -235,6 +235,23 @@ TEST_CASE("[Quaternion] Construct Basis Axes") {
CHECK(q[3] == doctest::Approx(0.8582598));
}
+TEST_CASE("[Quaternion] Get Euler Orders") {
+ double x = Math::deg_to_rad(30.0);
+ double y = Math::deg_to_rad(45.0);
+ double z = Math::deg_to_rad(10.0);
+ Vector3 euler(x, y, z);
+ for (int i = 0; i < 6; i++) {
+ EulerOrder order = (EulerOrder)i;
+ Basis basis = Basis::from_euler(euler, order);
+ Quaternion q = Quaternion(basis);
+ Vector3 check = q.get_euler(order);
+ CHECK_MESSAGE(check.is_equal_approx(euler),
+ "Quaternion get_euler method should return the original angles.");
+ CHECK_MESSAGE(check.is_equal_approx(basis.get_euler(order)),
+ "Quaternion get_euler method should behave the same as Basis get_euler.");
+ }
+}
+
TEST_CASE("[Quaternion] Product (book)") {
// Example from "Quaternions and Rotation Sequences" by Jack Kuipers, p. 108.
Quaternion p(1.0, -2.0, 1.0, 3.0);
@@ -248,26 +265,26 @@ TEST_CASE("[Quaternion] Product (book)") {
}
TEST_CASE("[Quaternion] Product") {
- double yaw = Math::deg2rad(45.0);
- double pitch = Math::deg2rad(30.0);
- double roll = Math::deg2rad(10.0);
+ double yaw = Math::deg_to_rad(45.0);
+ double pitch = Math::deg_to_rad(30.0);
+ double roll = Math::deg_to_rad(10.0);
Vector3 euler_y(0.0, yaw, 0.0);
- Quaternion q_y(euler_y);
+ Quaternion q_y = Quaternion::from_euler(euler_y);
CHECK(q_y[0] == doctest::Approx(0.0));
CHECK(q_y[1] == doctest::Approx(0.382684));
CHECK(q_y[2] == doctest::Approx(0.0));
CHECK(q_y[3] == doctest::Approx(0.923879));
Vector3 euler_p(pitch, 0.0, 0.0);
- Quaternion q_p(euler_p);
+ Quaternion q_p = Quaternion::from_euler(euler_p);
CHECK(q_p[0] == doctest::Approx(0.258819));
CHECK(q_p[1] == doctest::Approx(0.0));
CHECK(q_p[2] == doctest::Approx(0.0));
CHECK(q_p[3] == doctest::Approx(0.965926));
Vector3 euler_r(0.0, 0.0, roll);
- Quaternion q_r(euler_r);
+ Quaternion q_r = Quaternion::from_euler(euler_r);
CHECK(q_r[0] == doctest::Approx(0.0));
CHECK(q_r[1] == doctest::Approx(0.0));
CHECK(q_r[2] == doctest::Approx(0.0871558));
@@ -292,7 +309,7 @@ TEST_CASE("[Quaternion] Product") {
TEST_CASE("[Quaternion] xform unit vectors") {
// Easy to visualize: 120 deg about X-axis.
// Transform the i, j, & k unit vectors.
- Quaternion q(Vector3(1.0, 0.0, 0.0), Math::deg2rad(120.0));
+ Quaternion q(Vector3(1.0, 0.0, 0.0), Math::deg_to_rad(120.0));
Vector3 i_t = q.xform(Vector3(1.0, 0.0, 0.0));
Vector3 j_t = q.xform(Vector3(0.0, 1.0, 0.0));
Vector3 k_t = q.xform(Vector3(0.0, 0.0, 1.0));
@@ -305,7 +322,7 @@ TEST_CASE("[Quaternion] xform unit vectors") {
CHECK(k_t.length_squared() == doctest::Approx(1.0));
// Easy to visualize: 30 deg about Y-axis.
- q = Quaternion(Vector3(0.0, 1.0, 0.0), Math::deg2rad(30.0));
+ q = Quaternion(Vector3(0.0, 1.0, 0.0), Math::deg_to_rad(30.0));
i_t = q.xform(Vector3(1.0, 0.0, 0.0));
j_t = q.xform(Vector3(0.0, 1.0, 0.0));
k_t = q.xform(Vector3(0.0, 0.0, 1.0));
@@ -318,7 +335,7 @@ TEST_CASE("[Quaternion] xform unit vectors") {
CHECK(k_t.length_squared() == doctest::Approx(1.0));
// Easy to visualize: 60 deg about Z-axis.
- q = Quaternion(Vector3(0.0, 0.0, 1.0), Math::deg2rad(60.0));
+ q = Quaternion(Vector3(0.0, 0.0, 1.0), Math::deg_to_rad(60.0));
i_t = q.xform(Vector3(1.0, 0.0, 0.0));
j_t = q.xform(Vector3(0.0, 1.0, 0.0));
k_t = q.xform(Vector3(0.0, 0.0, 1.0));
@@ -333,8 +350,8 @@ TEST_CASE("[Quaternion] xform unit vectors") {
TEST_CASE("[Quaternion] xform vector") {
// Arbitrary quaternion rotates an arbitrary vector.
- Vector3 euler_yzx(Math::deg2rad(31.41), Math::deg2rad(-49.16), Math::deg2rad(12.34));
- Basis basis_axes(euler_yzx);
+ Vector3 euler_yzx(Math::deg_to_rad(31.41), Math::deg_to_rad(-49.16), Math::deg_to_rad(12.34));
+ Basis basis_axes = Basis::from_euler(euler_yzx);
Quaternion q(basis_axes);
Vector3 v_arb(3.0, 4.0, 5.0);
@@ -347,7 +364,7 @@ TEST_CASE("[Quaternion] xform vector") {
// Test vector xform for a single combination of Quaternion and Vector.
void test_quat_vec_rotate(Vector3 euler_yzx, Vector3 v_in) {
- Basis basis_axes(euler_yzx);
+ Basis basis_axes = Basis::from_euler(euler_yzx);
Quaternion q(basis_axes);
Vector3 v_rot = q.xform(v_in);
@@ -384,6 +401,63 @@ TEST_CASE("[Stress][Quaternion] Many vector xforms") {
}
}
+TEST_CASE("[Quaternion] Finite number checks") {
+ const real_t x = NAN;
+
+ CHECK_MESSAGE(
+ Quaternion(0, 1, 2, 3).is_finite(),
+ "Quaternion with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, 1, 2, 3).is_finite(),
+ "Quaternion with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, x, 2, 3).is_finite(),
+ "Quaternion with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, 1, x, 3).is_finite(),
+ "Quaternion with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, 1, 2, x).is_finite(),
+ "Quaternion with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, x, 2, 3).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, 1, x, 3).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, 1, 2, x).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, x, x, 3).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, x, 2, x).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, 1, x, x).is_finite(),
+ "Quaternion with two components infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Quaternion(0, x, x, x).is_finite(),
+ "Quaternion with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, 1, x, x).is_finite(),
+ "Quaternion with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, x, 2, x).is_finite(),
+ "Quaternion with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, x, x, 3).is_finite(),
+ "Quaternion with three components infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Quaternion(x, x, x, x).is_finite(),
+ "Quaternion with four components infinite should not be finite.");
+}
+
} // namespace TestQuaternion
#endif // TEST_QUATERNION_H
diff --git a/tests/core/math/test_rect2.h b/tests/core/math/test_rect2.h
index 0b1106ac3c..9984823331 100644
--- a/tests/core/math/test_rect2.h
+++ b/tests/core/math/test_rect2.h
@@ -102,33 +102,33 @@ TEST_CASE("[Rect2] Basic setters") {
TEST_CASE("[Rect2] Area getters") {
CHECK_MESSAGE(
- Math::is_equal_approx(Rect2(0, 100, 1280, 720).get_area(), 921'600),
+ Rect2(0, 100, 1280, 720).get_area() == doctest::Approx(921'600),
"get_area() should return the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(Rect2(0, 100, -1280, -720).get_area(), 921'600),
+ Rect2(0, 100, -1280, -720).get_area() == doctest::Approx(921'600),
"get_area() should return the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(Rect2(0, 100, 1280, -720).get_area(), -921'600),
+ Rect2(0, 100, 1280, -720).get_area() == doctest::Approx(-921'600),
"get_area() should return the expected value.");
CHECK_MESSAGE(
- Math::is_equal_approx(Rect2(0, 100, -1280, 720).get_area(), -921'600),
+ Rect2(0, 100, -1280, 720).get_area() == doctest::Approx(-921'600),
"get_area() should return the expected value.");
CHECK_MESSAGE(
Math::is_zero_approx(Rect2(0, 100, 0, 720).get_area()),
"get_area() should return the expected value.");
CHECK_MESSAGE(
- !Rect2(0, 100, 1280, 720).has_no_area(),
- "has_no_area() should return the expected value on Rect2 with an area.");
+ Rect2(0, 100, 1280, 720).has_area(),
+ "has_area() should return the expected value on Rect2 with an area.");
CHECK_MESSAGE(
- Rect2(0, 100, 0, 500).has_no_area(),
- "has_no_area() should return the expected value on Rect2 with no area.");
+ !Rect2(0, 100, 0, 500).has_area(),
+ "has_area() should return the expected value on Rect2 with no area.");
CHECK_MESSAGE(
- Rect2(0, 100, 500, 0).has_no_area(),
- "has_no_area() should return the expected value on Rect2 with no area.");
+ !Rect2(0, 100, 500, 0).has_area(),
+ "has_area() should return the expected value on Rect2 with no area.");
CHECK_MESSAGE(
- Rect2(0, 100, 0, 0).has_no_area(),
- "has_no_area() should return the expected value on Rect2 with no area.");
+ !Rect2(0, 100, 0, 0).has_area(),
+ "has_area() should return the expected value on Rect2 with no area.");
}
TEST_CASE("[Rect2] Absolute coordinates") {
@@ -300,6 +300,27 @@ TEST_CASE("[Rect2] Merging") {
Rect2(0, 100, 1280, 720).merge(Rect2(-4000, -4000, 100, 100)).is_equal_approx(Rect2(-4000, -4000, 5280, 4820)),
"merge() with non-enclosed Rect2 should return the expected result.");
}
+
+TEST_CASE("[Rect2] Finite number checks") {
+ const Vector2 x(0, 1);
+ const Vector2 infinite(NAN, NAN);
+
+ CHECK_MESSAGE(
+ Rect2(x, x).is_finite(),
+ "Rect2 with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Rect2(infinite, x).is_finite(),
+ "Rect2 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Rect2(x, infinite).is_finite(),
+ "Rect2 with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Rect2(infinite, infinite).is_finite(),
+ "Rect2 with two components infinite should not be finite.");
+}
+
} // namespace TestRect2
#endif // TEST_RECT2_H
diff --git a/tests/core/math/test_rect2i.h b/tests/core/math/test_rect2i.h
index 0d1a088a66..4005300e1f 100644
--- a/tests/core/math/test_rect2i.h
+++ b/tests/core/math/test_rect2i.h
@@ -118,17 +118,17 @@ TEST_CASE("[Rect2i] Area getters") {
"get_area() should return the expected value.");
CHECK_MESSAGE(
- !Rect2i(0, 100, 1280, 720).has_no_area(),
- "has_no_area() should return the expected value on Rect2i with an area.");
+ Rect2i(0, 100, 1280, 720).has_area(),
+ "has_area() should return the expected value on Rect2i with an area.");
CHECK_MESSAGE(
- Rect2i(0, 100, 0, 500).has_no_area(),
- "has_no_area() should return the expected value on Rect2i with no area.");
+ !Rect2i(0, 100, 0, 500).has_area(),
+ "has_area() should return the expected value on Rect2i with no area.");
CHECK_MESSAGE(
- Rect2i(0, 100, 500, 0).has_no_area(),
- "has_no_area() should return the expected value on Rect2i with no area.");
+ !Rect2i(0, 100, 500, 0).has_area(),
+ "has_area() should return the expected value on Rect2i with no area.");
CHECK_MESSAGE(
- Rect2i(0, 100, 0, 0).has_no_area(),
- "has_no_area() should return the expected value on Rect2i with no area.");
+ !Rect2i(0, 100, 0, 0).has_area(),
+ "has_area() should return the expected value on Rect2i with no area.");
}
TEST_CASE("[Rect2i] Absolute coordinates") {
diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h
index 697bf63fc5..ab51bcd7da 100644
--- a/tests/core/math/test_transform_2d.h
+++ b/tests/core/math/test_transform_2d.h
@@ -83,6 +83,40 @@ TEST_CASE("[Transform2D] rotation") {
CHECK(orig.rotated(phi) == R * orig);
CHECK(orig.rotated_local(phi) == orig * R);
}
+
+TEST_CASE("[Transform2D] Finite number checks") {
+ const Vector2 x(0, 1);
+ const Vector2 infinite(NAN, NAN);
+
+ CHECK_MESSAGE(
+ Transform2D(x, x, x).is_finite(),
+ "Transform2D with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Transform2D(infinite, x, x).is_finite(),
+ "Transform2D with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform2D(x, infinite, x).is_finite(),
+ "Transform2D with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform2D(x, x, infinite).is_finite(),
+ "Transform2D with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Transform2D(infinite, infinite, x).is_finite(),
+ "Transform2D with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform2D(infinite, x, infinite).is_finite(),
+ "Transform2D with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform2D(x, infinite, infinite).is_finite(),
+ "Transform2D with two components infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Transform2D(infinite, infinite, infinite).is_finite(),
+ "Transform2D with three components infinite should not be finite.");
+}
+
} // namespace TestTransform2D
#endif // TEST_TRANSFORM_2D_H
diff --git a/tests/core/math/test_transform_3d.h b/tests/core/math/test_transform_3d.h
index da166b43f7..d2730f3577 100644
--- a/tests/core/math/test_transform_3d.h
+++ b/tests/core/math/test_transform_3d.h
@@ -84,6 +84,29 @@ TEST_CASE("[Transform3D] rotation") {
CHECK(orig.rotated(axis, phi) == R * orig);
CHECK(orig.rotated_local(axis, phi) == orig * R);
}
+
+TEST_CASE("[Transform3D] Finite number checks") {
+ const Vector3 y(0, 1, 2);
+ const Vector3 infinite_vec(NAN, NAN, NAN);
+ const Basis x(y, y, y);
+ const Basis infinite_basis(infinite_vec, infinite_vec, infinite_vec);
+
+ CHECK_MESSAGE(
+ Transform3D(x, y).is_finite(),
+ "Transform3D with all components finite should be finite");
+
+ CHECK_FALSE_MESSAGE(
+ Transform3D(x, infinite_vec).is_finite(),
+ "Transform3D with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Transform3D(infinite_basis, y).is_finite(),
+ "Transform3D with one component infinite should not be finite.");
+
+ CHECK_FALSE_MESSAGE(
+ Transform3D(infinite_basis, infinite_vec).is_finite(),
+ "Transform3D with two components infinite should not be finite.");
+}
+
} // namespace TestTransform3D
#endif // TEST_TRANSFORM_3D_H
diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h
index 9b7800164a..8f8fccd717 100644
--- a/tests/core/math/test_vector2.h
+++ b/tests/core/math/test_vector2.h
@@ -37,20 +37,28 @@
namespace TestVector2 {
+TEST_CASE("[Vector2] Constructor methods") {
+ const Vector2 vector_empty = Vector2();
+ const Vector2 vector_zero = Vector2(0.0, 0.0);
+ CHECK_MESSAGE(
+ vector_empty == vector_zero,
+ "Vector2 Constructor with no inputs should return a zero Vector2.");
+}
+
TEST_CASE("[Vector2] Angle methods") {
const Vector2 vector_x = Vector2(1, 0);
const Vector2 vector_y = Vector2(0, 1);
CHECK_MESSAGE(
- Math::is_equal_approx(vector_x.angle_to(vector_y), (real_t)Math_TAU / 4),
+ vector_x.angle_to(vector_y) == doctest::Approx((real_t)Math_TAU / 4),
"Vector2 angle_to should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector_y.angle_to(vector_x), (real_t)-Math_TAU / 4),
+ vector_y.angle_to(vector_x) == doctest::Approx((real_t)-Math_TAU / 4),
"Vector2 angle_to should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector_x.angle_to_point(vector_y), (real_t)Math_TAU * 3 / 8),
+ vector_x.angle_to_point(vector_y) == doctest::Approx((real_t)Math_TAU * 3 / 8),
"Vector2 angle_to_point should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector_y.angle_to_point(vector_x), (real_t)-Math_TAU / 8),
+ vector_y.angle_to_point(vector_x) == doctest::Approx((real_t)-Math_TAU / 8),
"Vector2 angle_to_point should work as expected.");
}
@@ -102,10 +110,13 @@ TEST_CASE("[Vector2] Interpolation methods") {
Vector2(1, 1).slerp(Vector2(), 0.5) == Vector2(0.5, 0.5),
"Vector2 slerp with one input as zero should behave like a regular lerp.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.slerp(vector2, 0.5).length(), (real_t)4.31959610746631919),
+ Vector2(4, 6).slerp(Vector2(8, 10), 0.5).is_equal_approx(Vector2(5.9076470794008017626, 8.07918879020090480697)),
+ "Vector2 slerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.slerp(vector2, 0.5).length() == doctest::Approx((real_t)4.31959610746631919),
"Vector2 slerp with different length input should return a vector with an interpolated length.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2, vector1.angle_to(vector2)),
+ vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2 == doctest::Approx(vector1.angle_to(vector2)),
"Vector2 slerp with different length input should return a vector with an interpolated angle.");
CHECK_MESSAGE(
vector1.cubic_interpolate(vector2, Vector2(), Vector2(7, 7), 0.5) == Vector2(2.375, 3.5),
@@ -125,19 +136,19 @@ TEST_CASE("[Vector2] Length methods") {
vector1.length_squared() == 200,
"Vector2 length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.length(), 10 * (real_t)Math_SQRT2),
+ vector1.length() == doctest::Approx(10 * (real_t)Math_SQRT2),
"Vector2 length should work as expected.");
CHECK_MESSAGE(
vector2.length_squared() == 1300,
"Vector2 length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector2.length(), (real_t)36.05551275463989293119),
+ vector2.length() == doctest::Approx((real_t)36.05551275463989293119),
"Vector2 length should work as expected.");
CHECK_MESSAGE(
vector1.distance_squared_to(vector2) == 500,
"Vector2 distance_squared_to should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.distance_to(vector2), (real_t)22.36067977499789696409),
+ vector1.distance_to(vector2) == doctest::Approx((real_t)22.36067977499789696409),
"Vector2 distance_to should work as expected.");
}
@@ -171,6 +182,15 @@ TEST_CASE("[Vector2] Normalization methods") {
CHECK_MESSAGE(
Vector2(1, 1).normalized().is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)),
"Vector2 normalized should work as expected.");
+
+ Vector2 vector = Vector2(3.2, -5.4);
+ vector.normalize();
+ CHECK_MESSAGE(
+ vector == Vector2(3.2, -5.4).normalized(),
+ "Vector2 normalize should convert same way as Vector2 normalized.");
+ CHECK_MESSAGE(
+ vector.is_equal_approx(Vector2(0.509802390301732898898, -0.860291533634174266891)),
+ "Vector2 normalize should work as expected.");
}
TEST_CASE("[Vector2] Operators") {
@@ -274,14 +294,16 @@ TEST_CASE("[Vector2] Operators") {
TEST_CASE("[Vector2] Other methods") {
const Vector2 vector = Vector2(1.2, 3.4);
CHECK_MESSAGE(
- Math::is_equal_approx(vector.aspect(), (real_t)1.2 / (real_t)3.4),
+ vector.aspect() == doctest::Approx((real_t)1.2 / (real_t)3.4),
"Vector2 aspect should work as expected.");
+
CHECK_MESSAGE(
vector.direction_to(Vector2()).is_equal_approx(-vector.normalized()),
"Vector2 direction_to should work as expected.");
CHECK_MESSAGE(
Vector2(1, 1).direction_to(Vector2(2, 2)).is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)),
"Vector2 direction_to should work as expected.");
+
CHECK_MESSAGE(
vector.posmod(2).is_equal_approx(Vector2(1.2, 1.4)),
"Vector2 posmod should work as expected.");
@@ -294,10 +316,21 @@ TEST_CASE("[Vector2] Other methods") {
CHECK_MESSAGE(
(-vector).posmodv(Vector2(2, 3)).is_equal_approx(Vector2(0.8, 2.6)),
"Vector2 posmodv should work as expected.");
+
+ CHECK_MESSAGE(
+ vector.rotated(Math_TAU).is_equal_approx(Vector2(1.2, 3.4)),
+ "Vector2 rotated should work as expected.");
CHECK_MESSAGE(
vector.rotated(Math_TAU / 4).is_equal_approx(Vector2(-3.4, 1.2)),
"Vector2 rotated should work as expected.");
CHECK_MESSAGE(
+ vector.rotated(Math_TAU / 3).is_equal_approx(Vector2(-3.544486372867091398996, -0.660769515458673623883)),
+ "Vector2 rotated should work as expected.");
+ CHECK_MESSAGE(
+ vector.rotated(Math_TAU / 2).is_equal_approx(vector.rotated(Math_TAU / -2)),
+ "Vector2 rotated should work as expected.");
+
+ CHECK_MESSAGE(
vector.snapped(Vector2(1, 1)) == Vector2(1, 3),
"Vector2 snapped to integers should be the same as rounding.");
CHECK_MESSAGE(
@@ -306,23 +339,57 @@ TEST_CASE("[Vector2] Other methods") {
CHECK_MESSAGE(
vector.snapped(Vector2(0.25, 0.25)) == Vector2(1.25, 3.5),
"Vector2 snapped to 0.25 should give exact results.");
+
+ CHECK_MESSAGE(
+ Vector2(1.2, 2.5).is_equal_approx(vector.min(Vector2(3.0, 2.5))),
+ "Vector2 min should return expected value.");
+
+ CHECK_MESSAGE(
+ Vector2(5.3, 3.4).is_equal_approx(vector.max(Vector2(5.3, 2.0))),
+ "Vector2 max should return expected value.");
}
TEST_CASE("[Vector2] Plane methods") {
const Vector2 vector = Vector2(1.2, 3.4);
const Vector2 vector_y = Vector2(0, 1);
+ const Vector2 vector_normal = Vector2(0.95879811270838721622267, 0.2840883296913739899919);
+ const Vector2 vector_non_normal = Vector2(5.4, 1.6);
CHECK_MESSAGE(
vector.bounce(vector_y) == Vector2(1.2, -3.4),
"Vector2 bounce on a plane with normal of the Y axis should.");
CHECK_MESSAGE(
+ vector.bounce(vector_normal).is_equal_approx(Vector2(-2.85851197982345523329, 2.197477931904161412358)),
+ "Vector2 bounce with normal should return expected value.");
+ CHECK_MESSAGE(
vector.reflect(vector_y) == Vector2(-1.2, 3.4),
"Vector2 reflect on a plane with normal of the Y axis should.");
CHECK_MESSAGE(
+ vector.reflect(vector_normal).is_equal_approx(Vector2(2.85851197982345523329, -2.197477931904161412358)),
+ "Vector2 reflect with normal should return expected value.");
+ CHECK_MESSAGE(
vector.project(vector_y) == Vector2(0, 3.4),
- "Vector2 projected on the X axis should only give the Y component.");
+ "Vector2 projected on the Y axis should only give the Y component.");
+ CHECK_MESSAGE(
+ vector.project(vector_normal).is_equal_approx(Vector2(2.0292559899117276166, 0.60126103404791929382)),
+ "Vector2 projected on a normal should return expected value.");
CHECK_MESSAGE(
vector.slide(vector_y) == Vector2(1.2, 0),
"Vector2 slide on a plane with normal of the Y axis should set the Y to zero.");
+ CHECK_MESSAGE(
+ vector.slide(vector_normal).is_equal_approx(Vector2(-0.8292559899117276166456, 2.798738965952080706179)),
+ "Vector2 slide with normal should return expected value.");
+ // There's probably a better way to test these ones?
+ ERR_PRINT_OFF;
+ CHECK_MESSAGE(
+ vector.bounce(vector_non_normal).is_equal_approx(Vector2()),
+ "Vector2 bounce should return empty Vector2 with non-normalized input.");
+ CHECK_MESSAGE(
+ vector.reflect(vector_non_normal).is_equal_approx(Vector2()),
+ "Vector2 reflect should return empty Vector2 with non-normalized input.");
+ CHECK_MESSAGE(
+ vector.slide(vector_non_normal).is_equal_approx(Vector2()),
+ "Vector2 slide should return empty Vector2 with non-normalized input.");
+ ERR_PRINT_ON;
}
TEST_CASE("[Vector2] Rounding methods") {
@@ -367,12 +434,20 @@ TEST_CASE("[Vector2] Rounding methods") {
TEST_CASE("[Vector2] Linear algebra methods") {
const Vector2 vector_x = Vector2(1, 0);
const Vector2 vector_y = Vector2(0, 1);
+ const Vector2 a = Vector2(3.5, 8.5);
+ const Vector2 b = Vector2(5.2, 4.6);
CHECK_MESSAGE(
vector_x.cross(vector_y) == 1,
"Vector2 cross product of X and Y should give 1.");
CHECK_MESSAGE(
vector_y.cross(vector_x) == -1,
"Vector2 cross product of Y and X should give negative 1.");
+ CHECK_MESSAGE(
+ a.cross(b) == doctest::Approx((real_t)-28.1),
+ "Vector2 cross should return expected value.");
+ CHECK_MESSAGE(
+ Vector2(-a.x, a.y).cross(Vector2(b.x, -b.y)) == doctest::Approx((real_t)-28.1),
+ "Vector2 cross should return expected value.");
CHECK_MESSAGE(
vector_x.dot(vector_y) == 0.0,
@@ -383,7 +458,39 @@ TEST_CASE("[Vector2] Linear algebra methods") {
CHECK_MESSAGE(
(vector_x * 10).dot(vector_x * 10) == 100.0,
"Vector2 dot product of same direction vectors should behave as expected.");
+ CHECK_MESSAGE(
+ a.dot(b) == doctest::Approx((real_t)57.3),
+ "Vector2 dot should return expected value.");
+ CHECK_MESSAGE(
+ Vector2(-a.x, a.y).dot(Vector2(b.x, -b.y)) == doctest::Approx((real_t)-57.3),
+ "Vector2 dot should return expected value.");
+}
+
+TEST_CASE("[Vector2] Finite number checks") {
+ const double infinite[] = { NAN, INFINITY, -INFINITY };
+
+ CHECK_MESSAGE(
+ Vector2(0, 1).is_finite(),
+ "Vector2(0, 1) should be finite");
+
+ for (double x : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector2(x, 1).is_finite(),
+ "Vector2 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector2(0, x).is_finite(),
+ "Vector2 with one component infinite should not be finite.");
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector2(x, y).is_finite(),
+ "Vector2 with two components infinite should not be finite.");
+ }
+ }
}
+
} // namespace TestVector2
#endif // TEST_VECTOR2_H
diff --git a/tests/core/math/test_vector2i.h b/tests/core/math/test_vector2i.h
index 841bb793a4..c7a0dccdcc 100644
--- a/tests/core/math/test_vector2i.h
+++ b/tests/core/math/test_vector2i.h
@@ -37,6 +37,14 @@
namespace TestVector2i {
+TEST_CASE("[Vector2i] Constructor methods") {
+ const Vector2i vector_empty = Vector2i();
+ const Vector2i vector_zero = Vector2i(0, 0);
+ CHECK_MESSAGE(
+ vector_empty == vector_zero,
+ "Vector2i Constructor with no inputs should return a zero Vector2i.");
+}
+
TEST_CASE("[Vector2i] Axis methods") {
Vector2i vector = Vector2i(2, 3);
CHECK_MESSAGE(
@@ -71,13 +79,13 @@ TEST_CASE("[Vector2i] Length methods") {
vector1.length_squared() == 200,
"Vector2i length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.length(), 10 * Math_SQRT2),
+ vector1.length() == doctest::Approx(10 * Math_SQRT2),
"Vector2i length should work as expected.");
CHECK_MESSAGE(
vector2.length_squared() == 1300,
"Vector2i length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector2.length(), 36.05551275463989293119),
+ vector2.length() == doctest::Approx(36.05551275463989293119),
"Vector2i length should work as expected.");
}
@@ -119,8 +127,16 @@ TEST_CASE("[Vector2i] Operators") {
TEST_CASE("[Vector2i] Other methods") {
const Vector2i vector = Vector2i(1, 3);
CHECK_MESSAGE(
- Math::is_equal_approx(vector.aspect(), (real_t)1.0 / (real_t)3.0),
+ vector.aspect() == doctest::Approx((real_t)1.0 / (real_t)3.0),
"Vector2i aspect should work as expected.");
+
+ CHECK_MESSAGE(
+ Vector2i(1, 2) == vector.min(Vector2i(3, 2)),
+ "Vector2i min should return expected value.");
+
+ CHECK_MESSAGE(
+ Vector2i(5, 3) == vector.max(Vector2i(5, 2)),
+ "Vector2i max should return expected value.");
}
TEST_CASE("[Vector2i] Abs and sign methods") {
diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h
index 6f99fada2b..89d73ee6de 100644
--- a/tests/core/math/test_vector3.h
+++ b/tests/core/math/test_vector3.h
@@ -39,31 +39,39 @@
namespace TestVector3 {
+TEST_CASE("[Vector3] Constructor methods") {
+ const Vector3 vector_empty = Vector3();
+ const Vector3 vector_zero = Vector3(0.0, 0.0, 0.0);
+ CHECK_MESSAGE(
+ vector_empty == vector_zero,
+ "Vector3 Constructor with no inputs should return a zero Vector3.");
+}
+
TEST_CASE("[Vector3] Angle methods") {
const Vector3 vector_x = Vector3(1, 0, 0);
const Vector3 vector_y = Vector3(0, 1, 0);
const Vector3 vector_yz = Vector3(0, 1, 1);
CHECK_MESSAGE(
- Math::is_equal_approx(vector_x.angle_to(vector_y), (real_t)Math_TAU / 4),
+ vector_x.angle_to(vector_y) == doctest::Approx((real_t)Math_TAU / 4),
"Vector3 angle_to should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector_x.angle_to(vector_yz), (real_t)Math_TAU / 4),
+ vector_x.angle_to(vector_yz) == doctest::Approx((real_t)Math_TAU / 4),
"Vector3 angle_to should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector_yz.angle_to(vector_x), (real_t)Math_TAU / 4),
+ vector_yz.angle_to(vector_x) == doctest::Approx((real_t)Math_TAU / 4),
"Vector3 angle_to should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector_y.angle_to(vector_yz), (real_t)Math_TAU / 8),
+ vector_y.angle_to(vector_yz) == doctest::Approx((real_t)Math_TAU / 8),
"Vector3 angle_to should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector_x.signed_angle_to(vector_y, vector_y), (real_t)Math_TAU / 4),
+ vector_x.signed_angle_to(vector_y, vector_y) == doctest::Approx((real_t)Math_TAU / 4),
"Vector3 signed_angle_to edge case should be positive.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector_x.signed_angle_to(vector_yz, vector_y), (real_t)Math_TAU / -4),
+ vector_x.signed_angle_to(vector_yz, vector_y) == doctest::Approx((real_t)Math_TAU / -4),
"Vector3 signed_angle_to should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector_yz.signed_angle_to(vector_x, vector_y), (real_t)Math_TAU / 4),
+ vector_yz.signed_angle_to(vector_x, vector_y) == doctest::Approx((real_t)Math_TAU / 4),
"Vector3 signed_angle_to should work as expected.");
}
@@ -76,16 +84,12 @@ TEST_CASE("[Vector3] Axis methods") {
vector.min_axis_index() == Vector3::Axis::AXIS_X,
"Vector3 min_axis_index should work as expected.");
CHECK_MESSAGE(
- vector.get_axis(vector.max_axis_index()) == (real_t)5.6,
- "Vector3 get_axis should work as expected.");
+ vector[vector.max_axis_index()] == (real_t)5.6,
+ "Vector3 array operator should work as expected.");
CHECK_MESSAGE(
vector[vector.min_axis_index()] == (real_t)1.2,
"Vector3 array operator should work as expected.");
- vector.set_axis(Vector3::Axis::AXIS_Y, 4.7);
- CHECK_MESSAGE(
- vector.get_axis(Vector3::Axis::AXIS_Y) == (real_t)4.7,
- "Vector3 set_axis should work as expected.");
vector[Vector3::Axis::AXIS_Y] = 3.7;
CHECK_MESSAGE(
vector[Vector3::Axis::AXIS_Y] == (real_t)3.7,
@@ -123,10 +127,13 @@ TEST_CASE("[Vector3] Interpolation methods") {
Vector3(1, 1, 1).slerp(Vector3(), 0.5) == Vector3(0.5, 0.5, 0.5),
"Vector3 slerp with one input as zero should behave like a regular lerp.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.slerp(vector2, 0.5).length(), (real_t)6.25831088708303172),
+ Vector3(4, 6, 2).slerp(Vector3(8, 10, 3), 0.5).is_equal_approx(Vector3(5.90194219811429941053, 8.06758688849378394534, 2.558307894718317120038)),
+ "Vector3 slerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.slerp(vector2, 0.5).length() == doctest::Approx((real_t)6.25831088708303172),
"Vector3 slerp with different length input should return a vector with an interpolated length.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2, vector1.angle_to(vector2)),
+ vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2 == doctest::Approx(vector1.angle_to(vector2)),
"Vector3 slerp with different length input should return a vector with an interpolated angle.");
CHECK_MESSAGE(
vector1.cubic_interpolate(vector2, Vector3(), Vector3(7, 7, 7), 0.5) == Vector3(2.375, 3.5, 4.625),
@@ -146,19 +153,19 @@ TEST_CASE("[Vector3] Length methods") {
vector1.length_squared() == 300,
"Vector3 length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.length(), 10 * (real_t)Math_SQRT3),
+ vector1.length() == doctest::Approx(10 * (real_t)Math_SQRT3),
"Vector3 length should work as expected.");
CHECK_MESSAGE(
vector2.length_squared() == 2900,
"Vector3 length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector2.length(), (real_t)53.8516480713450403125),
+ vector2.length() == doctest::Approx((real_t)53.8516480713450403125),
"Vector3 length should work as expected.");
CHECK_MESSAGE(
vector1.distance_squared_to(vector2) == 1400,
"Vector3 distance_squared_to should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.distance_to(vector2), (real_t)37.41657386773941385584),
+ vector1.distance_to(vector2) == doctest::Approx((real_t)37.41657386773941385584),
"Vector3 distance_to should work as expected.");
}
@@ -195,6 +202,15 @@ TEST_CASE("[Vector3] Normalization methods") {
CHECK_MESSAGE(
Vector3(1, 1, 1).normalized().is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)),
"Vector3 normalized should work as expected.");
+
+ Vector3 vector = Vector3(3.2, -5.4, 6);
+ vector.normalize();
+ CHECK_MESSAGE(
+ vector == Vector3(3.2, -5.4, 6).normalized(),
+ "Vector3 normalize should convert same way as Vector3 normalized.");
+ CHECK_MESSAGE(
+ vector.is_equal_approx(Vector3(0.368522751763902980457, -0.621882143601586279522, 0.6909801595573180883585)),
+ "Vector3 normalize should work as expected.");
}
TEST_CASE("[Vector3] Operators") {
@@ -318,10 +334,21 @@ TEST_CASE("[Vector3] Other methods") {
CHECK_MESSAGE(
(-vector).posmodv(Vector3(2, 3, 4)).is_equal_approx(Vector3(0.8, 2.6, 2.4)),
"Vector3 posmodv should work as expected.");
+
+ CHECK_MESSAGE(
+ vector.rotated(Vector3(0, 1, 0), Math_TAU).is_equal_approx(vector),
+ "Vector3 rotated should work as expected.");
CHECK_MESSAGE(
vector.rotated(Vector3(0, 1, 0), Math_TAU / 4).is_equal_approx(Vector3(5.6, 3.4, -1.2)),
"Vector3 rotated should work as expected.");
CHECK_MESSAGE(
+ vector.rotated(Vector3(1, 0, 0), Math_TAU / 3).is_equal_approx(Vector3(1.2, -6.54974226119285642, 0.1444863728670914)),
+ "Vector3 rotated should work as expected.");
+ CHECK_MESSAGE(
+ vector.rotated(Vector3(0, 0, 1), Math_TAU / 2).is_equal_approx(vector.rotated(Vector3(0, 0, 1), Math_TAU / -2)),
+ "Vector3 rotated should work as expected.");
+
+ CHECK_MESSAGE(
vector.snapped(Vector3(1, 1, 1)) == Vector3(1, 3, 6),
"Vector3 snapped to integers should be the same as rounding.");
CHECK_MESSAGE(
@@ -332,18 +359,44 @@ TEST_CASE("[Vector3] Other methods") {
TEST_CASE("[Vector3] Plane methods") {
const Vector3 vector = Vector3(1.2, 3.4, 5.6);
const Vector3 vector_y = Vector3(0, 1, 0);
+ const Vector3 vector_normal = Vector3(0.88763458893247992491, 0.26300284116517923701, 0.37806658417494515320);
+ const Vector3 vector_non_normal = Vector3(5.4, 1.6, 2.3);
CHECK_MESSAGE(
vector.bounce(vector_y) == Vector3(1.2, -3.4, 5.6),
"Vector3 bounce on a plane with normal of the Y axis should.");
CHECK_MESSAGE(
+ vector.bounce(vector_normal).is_equal_approx(Vector3(-6.0369629829775736287, 1.25571467171034855444, 2.517589840583626047)),
+ "Vector3 bounce with normal should return expected value.");
+ CHECK_MESSAGE(
vector.reflect(vector_y) == Vector3(-1.2, 3.4, -5.6),
"Vector3 reflect on a plane with normal of the Y axis should.");
CHECK_MESSAGE(
+ vector.reflect(vector_normal).is_equal_approx(Vector3(6.0369629829775736287, -1.25571467171034855444, -2.517589840583626047)),
+ "Vector3 reflect with normal should return expected value.");
+ CHECK_MESSAGE(
vector.project(vector_y) == Vector3(0, 3.4, 0),
- "Vector3 projected on the X axis should only give the Y component.");
+ "Vector3 projected on the Y axis should only give the Y component.");
+ CHECK_MESSAGE(
+ vector.project(vector_normal).is_equal_approx(Vector3(3.61848149148878681437, 1.0721426641448257227776, 1.54120507970818697649)),
+ "Vector3 projected on a normal should return expected value.");
CHECK_MESSAGE(
vector.slide(vector_y) == Vector3(1.2, 0, 5.6),
"Vector3 slide on a plane with normal of the Y axis should set the Y to zero.");
+ CHECK_MESSAGE(
+ vector.slide(vector_normal).is_equal_approx(Vector3(-2.41848149148878681437, 2.32785733585517427722237, 4.0587949202918130235)),
+ "Vector3 slide with normal should return expected value.");
+ // There's probably a better way to test these ones?
+ ERR_PRINT_OFF;
+ CHECK_MESSAGE(
+ vector.bounce(vector_non_normal).is_equal_approx(Vector3()),
+ "Vector3 bounce should return empty Vector3 with non-normalized input.");
+ CHECK_MESSAGE(
+ vector.reflect(vector_non_normal).is_equal_approx(Vector3()),
+ "Vector3 reflect should return empty Vector3 with non-normalized input.");
+ CHECK_MESSAGE(
+ vector.slide(vector_non_normal).is_equal_approx(Vector3()),
+ "Vector3 slide should return empty Vector3 with non-normalized input.");
+ ERR_PRINT_ON;
}
TEST_CASE("[Vector3] Rounding methods") {
@@ -389,6 +442,8 @@ TEST_CASE("[Vector3] Linear algebra methods") {
const Vector3 vector_x = Vector3(1, 0, 0);
const Vector3 vector_y = Vector3(0, 1, 0);
const Vector3 vector_z = Vector3(0, 0, 1);
+ const Vector3 a = Vector3(3.5, 8.5, 2.3);
+ const Vector3 b = Vector3(5.2, 4.6, 7.8);
CHECK_MESSAGE(
vector_x.cross(vector_y) == vector_z,
"Vector3 cross product of X and Y should give Z.");
@@ -401,6 +456,12 @@ TEST_CASE("[Vector3] Linear algebra methods") {
CHECK_MESSAGE(
vector_z.cross(vector_x) == vector_y,
"Vector3 cross product of Z and X should give Y.");
+ CHECK_MESSAGE(
+ a.cross(b).is_equal_approx(Vector3(55.72, -15.34, -28.1)),
+ "Vector3 cross should return expected value.");
+ CHECK_MESSAGE(
+ Vector3(-a.x, a.y, -a.z).cross(Vector3(b.x, -b.y, b.z)).is_equal_approx(Vector3(55.72, 15.34, -28.1)),
+ "Vector2 cross should return expected value.");
CHECK_MESSAGE(
vector_x.dot(vector_y) == 0.0,
@@ -411,7 +472,58 @@ TEST_CASE("[Vector3] Linear algebra methods") {
CHECK_MESSAGE(
(vector_x * 10).dot(vector_x * 10) == 100.0,
"Vector3 dot product of same direction vectors should behave as expected.");
+ CHECK_MESSAGE(
+ a.dot(b) == doctest::Approx((real_t)75.24),
+ "Vector3 dot should return expected value.");
+ CHECK_MESSAGE(
+ Vector3(-a.x, a.y, -a.z).dot(Vector3(b.x, -b.y, b.z)) == doctest::Approx((real_t)-75.24),
+ "Vector3 dot should return expected value.");
+}
+
+TEST_CASE("[Vector3] Finite number checks") {
+ const double infinite[] = { NAN, INFINITY, -INFINITY };
+
+ CHECK_MESSAGE(
+ Vector3(0, 1, 2).is_finite(),
+ "Vector3(0, 1, 2) should be finite");
+
+ for (double x : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector3(x, 1, 2).is_finite(),
+ "Vector3 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector3(0, x, 2).is_finite(),
+ "Vector3 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector3(0, 1, x).is_finite(),
+ "Vector3 with one component infinite should not be finite.");
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector3(x, y, 2).is_finite(),
+ "Vector3 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector3(x, 1, y).is_finite(),
+ "Vector3 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector3(0, x, y).is_finite(),
+ "Vector3 with two components infinite should not be finite.");
+ }
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ for (double z : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector3(x, y, z).is_finite(),
+ "Vector3 with three components infinite should not be finite.");
+ }
+ }
+ }
}
+
} // namespace TestVector3
#endif // TEST_VECTOR3_H
diff --git a/tests/core/math/test_vector3i.h b/tests/core/math/test_vector3i.h
index b1c6944eba..56578f99eb 100644
--- a/tests/core/math/test_vector3i.h
+++ b/tests/core/math/test_vector3i.h
@@ -36,6 +36,14 @@
namespace TestVector3i {
+TEST_CASE("[Vector3i] Constructor methods") {
+ const Vector3i vector_empty = Vector3i();
+ const Vector3i vector_zero = Vector3i(0, 0, 0);
+ CHECK_MESSAGE(
+ vector_empty == vector_zero,
+ "Vector3i Constructor with no inputs should return a zero Vector3i.");
+}
+
TEST_CASE("[Vector3i] Axis methods") {
Vector3i vector = Vector3i(1, 2, 3);
CHECK_MESSAGE(
@@ -45,16 +53,12 @@ TEST_CASE("[Vector3i] Axis methods") {
vector.min_axis_index() == Vector3i::Axis::AXIS_X,
"Vector3i min_axis_index should work as expected.");
CHECK_MESSAGE(
- vector.get_axis(vector.max_axis_index()) == 3,
- "Vector3i get_axis should work as expected.");
+ vector[vector.max_axis_index()] == 3,
+ "Vector3i array operator should work as expected.");
CHECK_MESSAGE(
vector[vector.min_axis_index()] == 1,
"Vector3i array operator should work as expected.");
- vector.set_axis(Vector3i::Axis::AXIS_Y, 4);
- CHECK_MESSAGE(
- vector.get_axis(Vector3i::Axis::AXIS_Y) == 4,
- "Vector3i set_axis should work as expected.");
vector[Vector3i::Axis::AXIS_Y] = 5;
CHECK_MESSAGE(
vector[Vector3i::Axis::AXIS_Y] == 5,
@@ -78,13 +82,13 @@ TEST_CASE("[Vector3i] Length methods") {
vector1.length_squared() == 300,
"Vector3i length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.length(), 10 * Math_SQRT3),
+ vector1.length() == doctest::Approx(10 * Math_SQRT3),
"Vector3i length should work as expected.");
CHECK_MESSAGE(
vector2.length_squared() == 2900,
"Vector3i length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector2.length(), 53.8516480713450403125),
+ vector2.length() == doctest::Approx(53.8516480713450403125),
"Vector3i length should work as expected.");
}
diff --git a/tests/core/math/test_vector4.h b/tests/core/math/test_vector4.h
index ccf991401b..6ed85661cb 100644
--- a/tests/core/math/test_vector4.h
+++ b/tests/core/math/test_vector4.h
@@ -38,6 +38,14 @@
namespace TestVector4 {
+TEST_CASE("[Vector4] Constructor methods") {
+ const Vector4 vector_empty = Vector4();
+ const Vector4 vector_zero = Vector4(0.0, 0.0, 0.0, 0.0);
+ CHECK_MESSAGE(
+ vector_empty == vector_zero,
+ "Vector4 Constructor with no inputs should return a zero Vector4.");
+}
+
TEST_CASE("[Vector4] Axis methods") {
Vector4 vector = Vector4(1.2, 3.4, 5.6, -0.9);
CHECK_MESSAGE(
@@ -47,16 +55,12 @@ TEST_CASE("[Vector4] Axis methods") {
vector.min_axis_index() == Vector4::Axis::AXIS_W,
"Vector4 min_axis_index should work as expected.");
CHECK_MESSAGE(
- vector.get_axis(vector.max_axis_index()) == (real_t)5.6,
- "Vector4 get_axis should work as expected.");
+ vector[vector.max_axis_index()] == (real_t)5.6,
+ "Vector4 array operator should work as expected.");
CHECK_MESSAGE(
vector[vector.min_axis_index()] == (real_t)-0.9,
"Vector4 array operator should work as expected.");
- vector.set_axis(Vector4::Axis::AXIS_Y, 4.7);
- CHECK_MESSAGE(
- vector.get_axis(Vector4::Axis::AXIS_Y) == (real_t)4.7,
- "Vector4 set_axis should work as expected.");
vector[Vector4::Axis::AXIS_Y] = 3.7;
CHECK_MESSAGE(
vector[Vector4::Axis::AXIS_Y] == (real_t)3.7,
@@ -87,19 +91,19 @@ TEST_CASE("[Vector4] Length methods") {
vector1.length_squared() == 400,
"Vector4 length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.length(), 20),
+ vector1.length() == doctest::Approx(20),
"Vector4 length should work as expected.");
CHECK_MESSAGE(
vector2.length_squared() == 5400,
"Vector4 length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector2.length(), (real_t)73.484692283495),
+ vector2.length() == doctest::Approx((real_t)73.484692283495),
"Vector4 length should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.distance_to(vector2), (real_t)54.772255750517),
+ vector1.distance_to(vector2) == doctest::Approx((real_t)54.772255750517),
"Vector4 distance_to should work as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.distance_squared_to(vector2), 3000),
+ vector1.distance_squared_to(vector2) == doctest::Approx(3000),
"Vector4 distance_squared_to should work as expected.");
}
@@ -307,9 +311,87 @@ TEST_CASE("[Vector4] Linear algebra methods") {
(vector_x * 10).dot(vector_x * 10) == 100.0,
"Vector4 dot product of same direction vectors should behave as expected.");
CHECK_MESSAGE(
- Math::is_equal_approx((vector1 * 2).dot(vector2 * 4), (real_t)-25.9 * 8),
+ (vector1 * 2).dot(vector2 * 4) == doctest::Approx((real_t)-25.9 * 8),
"Vector4 dot product should work as expected.");
}
+
+TEST_CASE("[Vector4] Finite number checks") {
+ const double infinite[] = { NAN, INFINITY, -INFINITY };
+
+ CHECK_MESSAGE(
+ Vector4(0, 1, 2, 3).is_finite(),
+ "Vector4(0, 1, 2, 3) should be finite");
+
+ for (double x : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, 1, 2, 3).is_finite(),
+ "Vector4 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, x, 2, 3).is_finite(),
+ "Vector4 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, 1, x, 3).is_finite(),
+ "Vector4 with one component infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, 1, 2, x).is_finite(),
+ "Vector4 with one component infinite should not be finite.");
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, y, 2, 3).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, 1, y, 3).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, 1, 2, y).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, x, y, 3).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, x, 2, y).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, 1, x, y).is_finite(),
+ "Vector4 with two components infinite should not be finite.");
+ }
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ for (double z : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector4(0, x, y, z).is_finite(),
+ "Vector4 with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, 1, y, z).is_finite(),
+ "Vector4 with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, y, 2, z).is_finite(),
+ "Vector4 with three components infinite should not be finite.");
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, y, z, 3).is_finite(),
+ "Vector4 with three components infinite should not be finite.");
+ }
+ }
+ }
+
+ for (double x : infinite) {
+ for (double y : infinite) {
+ for (double z : infinite) {
+ for (double w : infinite) {
+ CHECK_FALSE_MESSAGE(
+ Vector4(x, y, z, w).is_finite(),
+ "Vector4 with four components infinite should not be finite.");
+ }
+ }
+ }
+ }
+}
+
} // namespace TestVector4
#endif // TEST_VECTOR4_H
diff --git a/tests/core/math/test_vector4i.h b/tests/core/math/test_vector4i.h
index ac63001b24..30d38607dd 100644
--- a/tests/core/math/test_vector4i.h
+++ b/tests/core/math/test_vector4i.h
@@ -36,6 +36,14 @@
namespace TestVector4i {
+TEST_CASE("[Vector4i] Constructor methods") {
+ const Vector4i vector_empty = Vector4i();
+ const Vector4i vector_zero = Vector4i(0, 0, 0, 0);
+ CHECK_MESSAGE(
+ vector_empty == vector_zero,
+ "Vector4i Constructor with no inputs should return a zero Vector4i.");
+}
+
TEST_CASE("[Vector4i] Axis methods") {
Vector4i vector = Vector4i(1, 2, 3, 4);
CHECK_MESSAGE(
@@ -45,16 +53,12 @@ TEST_CASE("[Vector4i] Axis methods") {
vector.min_axis_index() == Vector4i::Axis::AXIS_X,
"Vector4i min_axis_index should work as expected.");
CHECK_MESSAGE(
- vector.get_axis(vector.max_axis_index()) == 4,
- "Vector4i get_axis should work as expected.");
+ vector[vector.max_axis_index()] == 4,
+ "Vector4i array operator should work as expected.");
CHECK_MESSAGE(
vector[vector.min_axis_index()] == 1,
"Vector4i array operator should work as expected.");
- vector.set_axis(Vector4i::Axis::AXIS_Y, 5);
- CHECK_MESSAGE(
- vector.get_axis(Vector4i::Axis::AXIS_Y) == 5,
- "Vector4i set_axis should work as expected.");
vector[Vector4i::Axis::AXIS_Y] = 5;
CHECK_MESSAGE(
vector[Vector4i::Axis::AXIS_Y] == 5,
@@ -78,13 +82,13 @@ TEST_CASE("[Vector4i] Length methods") {
vector1.length_squared() == 400,
"Vector4i length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector1.length(), 20),
+ vector1.length() == doctest::Approx(20),
"Vector4i length should work as expected.");
CHECK_MESSAGE(
vector2.length_squared() == 5400,
"Vector4i length_squared should work as expected and return exact result.");
CHECK_MESSAGE(
- Math::is_equal_approx(vector2.length(), 73.4846922835),
+ vector2.length() == doctest::Approx(73.4846922835),
"Vector4i length should work as expected.");
}