diff --git a/CMakePresets.json b/CMakePresets.json index f067ed3..8780fa2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -239,7 +239,7 @@ "hidden": true, "inherits": ["darwin-base", "vcpkg-base"], "cacheVariables": { - "VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples;lua" + "VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples;lua;gcem" } }, { diff --git a/include/omath/linear_algebra/vector2.hpp b/include/omath/linear_algebra/vector2.hpp index e283d51..74ef70b 100644 --- a/include/omath/linear_algebra/vector2.hpp +++ b/include/omath/linear_algebra/vector2.hpp @@ -3,6 +3,7 @@ // #pragma once +#include "omath/internal/optional_constexpr_math.hpp" #include #include #include @@ -116,9 +117,13 @@ namespace omath // Basic vector operations [[nodiscard("You must use distance")]] - Type distance_to(const Vector2& other) const noexcept + OMATH_CONSTEXPR Type distance_to(const Vector2& other) const noexcept { +#ifdef OMATH_USE_GCEM + return gcem::sqrt(distance_to_sqr(other)); +#else return std::sqrt(distance_to_sqr(other)); +#endif } [[nodiscard("You must use squared distance")]] @@ -136,7 +141,11 @@ namespace omath #ifndef _MSC_VER [[nodiscard("You must use length")]] constexpr Type length() const noexcept { +#ifdef OMATH_USE_GCEM + return gcem::hypot(this->x, this->y); +#else return std::hypot(this->x, this->y); +#endif } [[nodiscard("You must use normalized vector")]] constexpr Vector2 normalized() const noexcept @@ -146,13 +155,17 @@ namespace omath } #else [[nodiscard("You must use length")]] - Type length() const noexcept + OMATH_CONSTEXPR Type length() const noexcept { +#ifdef OMATH_USE_GCEM + return gcem::hypot(x, y); +#else return std::hypot(x, y); +#endif } [[nodiscard("You must use normalized vector")]] - Vector2 normalized() const noexcept + OMATH_CONSTEXPR Vector2 normalized() const noexcept { const Type len = length(); return len > static_cast(0) ? *this / len : *this; @@ -216,24 +229,24 @@ namespace omath } [[nodiscard("You must use comparison result")]] - bool operator<(const Vector2& other) const noexcept + OMATH_CONSTEXPR bool operator<(const Vector2& other) const noexcept { return length() < other.length(); } [[nodiscard("You must use comparison result")]] - bool operator>(const Vector2& other) const noexcept + OMATH_CONSTEXPR bool operator>(const Vector2& other) const noexcept { return length() > other.length(); } [[nodiscard("You must use comparison result")]] - bool operator<=(const Vector2& other) const noexcept + OMATH_CONSTEXPR bool operator<=(const Vector2& other) const noexcept { return length() <= other.length(); } [[nodiscard("You must use comparison result")]] - bool operator>=(const Vector2& other) const noexcept + OMATH_CONSTEXPR bool operator>=(const Vector2& other) const noexcept { return length() >= other.length(); } diff --git a/include/omath/linear_algebra/vector3.hpp b/include/omath/linear_algebra/vector3.hpp index 6dac417..405c8f3 100644 --- a/include/omath/linear_algebra/vector3.hpp +++ b/include/omath/linear_algebra/vector3.hpp @@ -140,20 +140,24 @@ namespace omath } #ifndef _MSC_VER - [[nodiscard("You must use length")]] constexpr Type length() const + [[nodiscard("You must use length")]] constexpr Type length() const noexcept { +#ifdef OMATH_USE_GCEM + return gcem::sqrt(this->x * this->x + this->y * this->y + z * z); +#else return std::hypot(this->x, this->y, z); +#endif } - [[nodiscard("You must use 2D length")]] constexpr Type length_2d() const + [[nodiscard("You must use 2D length")]] constexpr Type length_2d() const noexcept { return Vector2::length(); } - [[nodiscard("You must use distance")]] Type distance_to(const Vector3& other) const + [[nodiscard("You must use distance")]] OMATH_CONSTEXPR Type distance_to(const Vector3& other) const noexcept { return (*this - other).length(); } - [[nodiscard("You must use normalized vector")]] constexpr Vector3 normalized() const + [[nodiscard("You must use normalized vector")]] constexpr Vector3 normalized() const noexcept { const Type length_value = this->length(); @@ -161,13 +165,17 @@ namespace omath } #else [[nodiscard("You must use length")]] - Type length() const noexcept + OMATH_CONSTEXPR Type length() const noexcept { +#ifdef OMATH_USE_GCEM + return gcem::sqrt(this->x * this->x + this->y * this->y + this->z * this->z); +#else return std::hypot(this->x, this->y, z); +#endif } [[nodiscard("You must use normalized vector")]] - Vector3 normalized() const noexcept + OMATH_CONSTEXPR Vector3 normalized() const noexcept { const Type len = this->length(); @@ -175,13 +183,13 @@ namespace omath } [[nodiscard("You must use 2D length")]] - Type length_2d() const noexcept + OMATH_CONSTEXPR Type length_2d() const noexcept { return Vector2::length(); } [[nodiscard("You must use distance")]] - Type distance_to(const Vector3& v_other) const noexcept + OMATH_CONSTEXPR Type distance_to(const Vector3& v_other) const noexcept { return (*this - v_other).length(); } @@ -249,24 +257,28 @@ namespace omath } [[nodiscard("You must use direction check result")]] - bool point_to_same_direction(const Vector3& other) const + OMATH_CONSTEXPR bool point_to_same_direction(const Vector3& other) const { return dot(other) > static_cast(0); } [[nodiscard("You must use angle between vectors")]] - std::expected, Vector3Error> + OMATH_CONSTEXPR std::expected, Vector3Error> angle_between(const Vector3& other) const noexcept { const auto bottom = length() * other.length(); if (bottom == static_cast(0)) return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE); - +#ifdef OMATH_USE_GCEM + return Angle::from_radians(gcem::acos(dot(other) / bottom)); +#else return Angle::from_radians(std::acos(dot(other) / bottom)); +#endif } [[nodiscard("You must use perpendicularity check result")]] - bool is_perpendicular(const Vector3& other, Type epsilon = static_cast(0.0001)) const noexcept + OMATH_CONSTEXPR bool is_perpendicular(const Vector3& other, + Type epsilon = static_cast(0.0001)) const noexcept { if (const auto angle = angle_between(other)) return std::abs(angle->as_degrees() - static_cast(90)) <= epsilon; @@ -287,25 +299,25 @@ namespace omath } [[nodiscard("You must use comparison result")]] - bool operator<(const Vector3& other) const noexcept + OMATH_CONSTEXPR bool operator<(const Vector3& other) const noexcept { return length() < other.length(); } [[nodiscard("You must use comparison result")]] - bool operator>(const Vector3& other) const noexcept + OMATH_CONSTEXPR bool operator>(const Vector3& other) const noexcept { return length() > other.length(); } [[nodiscard("You must use comparison result")]] - bool operator<=(const Vector3& other) const noexcept + OMATH_CONSTEXPR bool operator<=(const Vector3& other) const noexcept { return length() <= other.length(); } [[nodiscard("You must use comparison result")]] - bool operator>=(const Vector3& other) const noexcept + OMATH_CONSTEXPR bool operator>=(const Vector3& other) const noexcept { return length() >= other.length(); } @@ -321,7 +333,7 @@ namespace omath template<> struct std::hash> { [[nodiscard("You must use hash value")]] - std::size_t operator()(const omath::Vector3& vec) const noexcept + constexpr std::size_t operator()(const omath::Vector3& vec) const noexcept { std::size_t hash = 0; constexpr std::hash hasher; diff --git a/include/omath/trigonometry/angle.hpp b/include/omath/trigonometry/angle.hpp index 7b687ee..146a309 100644 --- a/include/omath/trigonometry/angle.hpp +++ b/include/omath/trigonometry/angle.hpp @@ -3,11 +3,11 @@ // #pragma once +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/trigonometry/angles.hpp" #include #include #include -#include "omath/internal/optional_constexpr_math.hpp" namespace omath { @@ -104,13 +104,18 @@ namespace omath } [[nodiscard]] - Type atan() const noexcept + OMATH_CONSTEXPR Type atan() const noexcept { +#ifdef OMATH_USE_GCEM + return gcem::atan(as_radians()); +#else return std::atan(as_radians()); + +#endif } [[nodiscard]] - Type cot() const noexcept + OMATH_CONSTEXPR Type cot() const noexcept { return cos() / sin(); } diff --git a/tests/general/unit_test_vector2.cpp b/tests/general/unit_test_vector2.cpp index 91d9e61..52f606c 100644 --- a/tests/general/unit_test_vector2.cpp +++ b/tests/general/unit_test_vector2.cpp @@ -2,10 +2,10 @@ // Created by Vlad on 02.09.2024. // -#include #include // For FLT_MAX and FLT_MIN #include // For std::isinf and std::isnan #include +#include using namespace omath; @@ -399,7 +399,6 @@ TEST_F(UnitTestVector2, GreaterEqualOperator) EXPECT_TRUE(omath::Vector2(1.f, 1.f) >= omath::Vector2{}); } - // ── Cast operator tests ────────────────────────────────────────────────────── TEST(Vector2Cast, FloatToDouble) @@ -463,3 +462,9 @@ static_assert(Vector2(1.0f, 2.0f).length_sqr() == 5.0f, "LengthSqr should be 5") static_assert(Vector2(1.0f, 2.0f).dot(Vector2(4.0f, 5.0f)) == 14.0f, "Dot product should be 14"); static_assert(Vector2(4.0f, 5.0f).distance_to_sqr(Vector2(1.0f, 2.0f)) == 18.0f, "DistToSqr should be 18"); static_assert(Vector2(-1.0f, -2.0f).abs() == Vector2(1.0f, 2.0f), "Abs should convert negative values to positive"); + +#ifdef OMATH_USE_GCEM +static_assert(Vector2(3.0f, 4.0f).length() == 5.0f, "Length should be constexpr with gcem"); +static_assert(Vector2(0.0f, 0.0f).distance_to(Vector2(3.0f, 4.0f)) == 5.0f, "Distance should be constexpr with gcem"); +static_assert(Vector2(1.0f, 1.0f) < Vector2(3.0f, 4.0f), "Comparison should be constexpr with gcem"); +#endif diff --git a/tests/general/unit_test_vector3.cpp b/tests/general/unit_test_vector3.cpp index 158f1c0..e9c2b75 100644 --- a/tests/general/unit_test_vector3.cpp +++ b/tests/general/unit_test_vector3.cpp @@ -2,11 +2,11 @@ // Created by Vlad on 01.09.2024. // -#include #include // For FLT_MAX, FLT_MIN #include #include #include // For std::numeric_limits +#include using namespace omath; @@ -31,35 +31,33 @@ TEST(Vector3More, ArithmeticAndDotCross) constexpr Vector3 a{1.f, 0.f, 0.f}; constexpr Vector3 b{0.f, 1.f, 0.f}; const auto c = a + b; - constexpr Vector3 expect_c{1.f,1.f,0.f}; + constexpr Vector3 expect_c{1.f, 1.f, 0.f}; EXPECT_EQ(c, expect_c); const auto d = a - b; - constexpr Vector3 expect_d{1.f,-1.f,0.f}; + constexpr Vector3 expect_d{1.f, -1.f, 0.f}; EXPECT_EQ(d, expect_d); const auto e = a * 2.f; - constexpr Vector3 expect_e{2.f,0.f,0.f}; + constexpr Vector3 expect_e{2.f, 0.f, 0.f}; EXPECT_EQ(e, expect_e); EXPECT_FLOAT_EQ(a.dot(b), 0.f); // manual cross product check - const auto cr = Vector3{ a.y * b.z - a.z * b.y, - a.z * b.x - a.x * b.z, - a.x * b.y - a.y * b.x }; - constexpr Vector3 expect_cr{0.f,0.f,1.f}; + const auto cr = Vector3{a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; + constexpr Vector3 expect_cr{0.f, 0.f, 1.f}; EXPECT_EQ(cr, expect_cr); } TEST(Vector3More, NormalizationEdgeCases) { - constexpr Vector3 z{0.0,0.0,0.0}; + constexpr Vector3 z{0.0, 0.0, 0.0}; const auto zn = z.normalized(); EXPECT_DOUBLE_EQ(zn.x, 0.0); EXPECT_DOUBLE_EQ(zn.y, 0.0); EXPECT_DOUBLE_EQ(zn.z, 0.0); - constexpr Vector3 v{3.0,4.0,0.0}; + constexpr Vector3 v{3.0, 4.0, 0.0}; const auto vn = v.normalized(); EXPECT_NEAR(vn.x, 0.6, 1e-12); EXPECT_NEAR(vn.y, 0.8, 1e-12); @@ -481,16 +479,14 @@ TEST_F(UnitTestVector3, AsTuple) // Test AsTuple method TEST_F(UnitTestVector3, AngleBeatween) { - EXPECT_NEAR(Vector3(0.0f, 0.0f, 1.0f).angle_between({1, 0, 0}).value().as_degrees(), - 90.0f, 0.001f); - EXPECT_NEAR(Vector3(0.0f, 0.0f, 1.0f).angle_between({0.0f, 0.0f, 1.0f}).value().as_degrees(), - 0.0f, 0.001f); + EXPECT_NEAR(Vector3(0.0f, 0.0f, 1.0f).angle_between({1, 0, 0}).value().as_degrees(), 90.0f, 0.001f); + EXPECT_NEAR(Vector3(0.0f, 0.0f, 1.0f).angle_between({0.0f, 0.0f, 1.0f}).value().as_degrees(), 0.0f, 0.001f); EXPECT_FALSE(Vector3(0.0f, 0.0f, 0.0f).angle_between({0.0f, 0.0f, 1.0f}).has_value()); } TEST_F(UnitTestVector3, IsPerpendicular) { - EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).is_perpendicular({1, 0 ,0}), true); + EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).is_perpendicular({1, 0, 0}), true); EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).is_perpendicular({0.0f, 0.0f, 1.0f}), false); EXPECT_FALSE(Vector3(0.0f, 0.0f, 0.0f).is_perpendicular({0.0f, 0.0f, 1.0f})); } @@ -585,4 +581,19 @@ TEST(Vector3Cast, SameTypeRoundtrip) static_assert(Vector3(1.0f, 2.0f, 3.0f).length_sqr() == 14.0f, "LengthSqr should be 14"); static_assert(Vector3(1.0f, 2.0f, 3.0f).dot(Vector3(4.0f, 5.0f, 6.0f)) == 32.0f, "Dot product should be 32"); static_assert(Vector3(4.0f, 5.0f, 6.0f).distance_to_sqr(Vector3(1.0f, 2.0f, 3.0f)) == 27.0f, "DistToSqr should be 27"); -static_assert(Vector3(-1.0f, -2.0f, -3.0f).abs() == Vector3(1.0f, 2.0f, 3.0f), "Abs should convert negative values to positive"); +static_assert(Vector3(-1.0f, -2.0f, -3.0f).abs() == Vector3(1.0f, 2.0f, 3.0f), + "Abs should convert negative values to positive"); + +#ifdef OMATH_USE_GCEM +static_assert(Vector3(1.0f, 2.0f, 2.0f).length() == 3.0f, "Length should be constexpr with gcem"); +static_assert(Vector3(0.0f, 0.0f, 0.0f).distance_to(Vector3(1.0f, 2.0f, 2.0f)) == 3.0f, + "Distance should be constexpr with gcem"); +static_assert(Vector3(1.0f, 1.0f, 1.0f) < Vector3(3.0f, 4.0f, 5.0f), "Comparison should be constexpr with gcem"); +static_assert( + [] + { + constexpr auto angle = Vector3(1.0f, 0.0f, 0.0f).angle_between(Vector3(0.0f, 1.0f, 0.0f)); + return angle.has_value() && angle->as_degrees() > 89.999f && angle->as_degrees() < 90.001f; + }(), + "Angle between should be constexpr with gcem"); +#endif