diff --git a/include/gkit/math/constants.hpp b/include/gkit/math/constants.hpp new file mode 100644 index 0000000..fc42868 --- /dev/null +++ b/include/gkit/math/constants.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include + +namespace gkit::math { + // Mathematical constants (IEEE 754 standard) + // Pi multiples + constexpr float PI_32 = std::numbers::pi_v; + constexpr float TWO_PI_32 = PI_32 * 2.0f; + constexpr float HALF_PI_32 = PI_32 * 0.5f; + constexpr float INV_PI_32 = std::numbers::inv_pi_v; + + constexpr float PI_64 = std::numbers::pi; + constexpr float TWO_PI_64 = PI_64 * 2.0; + constexpr float HALF_PI_64 = PI_64 * 0.5; + constexpr float INV_PI_64 = std::numbers::inv_pi; + + // Natural constants + constexpr float E_32 = std::numbers::e_v; + constexpr float E_64 = std::numbers::e; + + // Golden ratio + constexpr float PHI_32 = std::numbers::phi_v; + constexpr float PHI_64 = std::numbers::phi; + + // Powers and roots + constexpr float SQRT_2_32 = std::numbers::sqrt2_v; + constexpr float SQRT_3_32 = std::numbers::sqrt3_v; + constexpr float SQRT_5_32 = 2.2360679774997897f; // sqrt(5) + constexpr float LN_2_32 = std::numbers::ln2_v; + constexpr float LN_10_32 = std::numbers::ln10_v; + + constexpr float SQRT_2_64 = std::numbers::sqrt2; + constexpr float SQRT_3_64 = std::numbers::sqrt3; + constexpr float SQRT_5_64 = 2.2360679774997896964091736687313; // sqrt(5) + constexpr float LN_2_64 = std::numbers::ln2; + constexpr float LN_10_64 = std::numbers::ln10; + + // Angle conversion constants + constexpr float DEG_TO_RAD_32 = gkit::math::PI_32 / 180.0f; + constexpr float RAD_TO_DEG_32 = 180.0f / gkit::math::PI_32; + + constexpr float DEG_TO_RAD_64 = gkit::math::PI_64 / 180.0; + constexpr float RAD_TO_DEG_64 = 180.0 / gkit::math::PI_64; + + // Common numeric constants + constexpr float ZERO_32 = 0.0f; + constexpr float ONE_32 = 1.0f; + constexpr float NEG_ONE_32 = -1.0f; + constexpr float TWO_32 = 2.0f; + constexpr float THREE_32 = 3.0f; + constexpr float FOUR_32 = 4.0f; + constexpr float FIVE_32 = 5.0f; + constexpr float TEN_32 = 10.0f; + constexpr float HUNDRED_32 = 100.0f; + constexpr float THOUSAND_32 = 1000.0f; + constexpr float HALF_32 = 0.5f; + constexpr float QUARTER_32 = 0.25f; + constexpr float THIRD_32 = 0.3333333333333333333333333333333f; + + constexpr float ZERO_64 = 0.0; + constexpr float ONE_64 = 1.0; + constexpr float NEG_ONE_64 = -1.0; + constexpr float TWO_64 = 2.0; + constexpr float THREE_64 = 3.0; + constexpr float FOUR_64 = 4.0; + constexpr float FIVE_64 = 5.0; + constexpr float TEN_64 = 10.0; + constexpr float HUNDRED_64 = 100.0; + constexpr float THOUSAND_64 = 1000.0; + constexpr float HALF_64 = 0.5; + constexpr float QUARTER_64 = 0.25; + constexpr float THIRD_64 = 0.3333333333333333333333333333333; + + // Integers + constexpr int ZERO_I32 = 0; + constexpr int ONE_I32 = 1; + constexpr int NEG_ONE_I32 = -1; + constexpr int TWO_I32 = 2; + constexpr int THREE_I32 = 3; +} // namespace gkit::math \ No newline at end of file diff --git a/include/gkit/math/scalar.hpp b/include/gkit/math/scalar.hpp new file mode 100644 index 0000000..e75217a --- /dev/null +++ b/include/gkit/math/scalar.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace gkit::math { + // Common limits for scalar types + template + struct ScalarLimits { + static constexpr T min_v = std::numeric_limits::min(); + static constexpr T max_v = std::numeric_limits::max(); + static constexpr T lowest_v = std::numeric_limits::lowest(); + static constexpr T epsilon_v = std::numeric_limits::epsilon(); + }; + + // Floating-point special values using numeric_limits + constexpr float EPSILON32 = std::numeric_limits::epsilon(); + constexpr float MIN32 = std::numeric_limits::min(); + constexpr float MAX32 = std::numeric_limits::max(); + + constexpr float EPSILON64 = std::numeric_limits::epsilon(); + constexpr float MIN64 = std::numeric_limits::min(); + constexpr float MAX64 = std::numeric_limits::max(); + + // Integer special values using numeric_limits + constexpr int8_t I8_MIN = std::numeric_limits::min(); + constexpr int8_t I8_MAX = std::numeric_limits::max(); + constexpr uint8_t U8_MAX = std::numeric_limits::max(); + + constexpr int16_t I16_MIN = std::numeric_limits::min(); + constexpr int16_t I16_MAX = std::numeric_limits::max(); + constexpr uint16_t U16_MAX = std::numeric_limits::max(); + + constexpr int32_t I32_MIN = std::numeric_limits::min(); + constexpr int32_t I32_MAX = std::numeric_limits::max(); + constexpr uint32_t U32_MAX = std::numeric_limits::max(); + + constexpr int64_t I64_MIN = std::numeric_limits::min(); + constexpr int64_t I64_MAX = std::numeric_limits::max(); + constexpr uint64_t U64_MAX = std::numeric_limits::max(); +} // namespace gkit::math \ No newline at end of file diff --git a/include/gkit/math/vector2.hpp b/include/gkit/math/vector2.hpp index 76e2640..81d4a9e 100644 --- a/include/gkit/math/vector2.hpp +++ b/include/gkit/math/vector2.hpp @@ -1,44 +1,71 @@ #pragma once #include -#include #include + namespace gkit::math { class Vector2 final { public: + float x = 0.0f; + float y = 0.0f; + Vector2() noexcept = default; + explicit Vector2(float v) noexcept; Vector2(float x, float y) noexcept; Vector2(const Vector2& other) noexcept; Vector2(const Vector2&& other) noexcept; ~Vector2() noexcept = default; - public: // Arithmetic operators - inline auto operator= (const Vector2& other) noexcept -> Vector2& { this->x = other.x; this->y = other.y; return *this; } - inline auto operator= (const Vector2&&other) noexcept -> Vector2& { this->x = other.x; this->y = other.y; return *this; } + public: // Arithmetic operators + inline auto operator=(const Vector2& other) noexcept -> Vector2& { this->x = other.x; this->y = other.y; return *this; } + inline auto operator=(const Vector2&& other) noexcept -> Vector2& { this->x = other.x; this->y = other.y; return *this; } inline auto operator==(const Vector2& other) noexcept -> bool { return this->x == other.x && this->y == other.y; } inline auto operator!=(const Vector2& other) noexcept -> bool { return this->x != other.x || this->y != other.y; } - inline auto operator+ (const Vector2& other) noexcept -> Vector2 { return Vector2(this->x + other.x, this->y + other.y); } - inline auto operator- (const Vector2& other) noexcept -> Vector2 { return Vector2(this->x - other.x, this->y - other.y); } + inline auto operator+(const Vector2& other) noexcept -> Vector2 { return Vector2(this->x + other.x, this->y + other.y); } + inline auto operator-(const Vector2& other) noexcept -> Vector2 { return Vector2(this->x - other.x, this->y - other.y); } inline auto operator+=(const Vector2& other) noexcept -> const Vector2& { this->x += other.x; this->y += other.y; return *this; } inline auto operator-=(const Vector2& other) noexcept -> const Vector2& { this->x -= other.x; this->y -= other.y; return *this; } - inline auto operator* (const int32_t n) noexcept -> Vector2 { return Vector2(this->x * n, this->y * n); } - inline auto operator/ (const int32_t n) noexcept -> Vector2 { return Vector2(this->x / n, this->y / n); } - inline auto operator*=(const int32_t n) noexcept -> const Vector2& { this->x *= n; this->y *= n; return *this; } - inline auto operator/=(const int32_t n) noexcept -> const Vector2& { this->x /= n; this->y /= n; return *this; } + inline auto operator*(float s) noexcept -> Vector2 { return {this->x * s, this->y * s}; } + inline auto operator/(float s) noexcept -> Vector2 { return {this->x / s, this->y / s}; } + inline auto operator*=(float s) noexcept -> const Vector2& { this->x *= s; this->y *= s; return *this; } + inline auto operator/=(float s) noexcept -> const Vector2& { this->x /= s; this->y /= s; return *this; } + inline auto operator-() noexcept -> Vector2 { return {-this->x, -this->y}; } - public: - static auto zero() -> const Vector2&; - auto normalization() -> void; -#ifdef _MSC_VER - inline auto length() const -> float { return std::sqrt(x * x + y * y); } -#else - inline constexpr auto length() const -> float { return std::sqrt(x * x + y * y); } -#endif - inline auto properties() -> auto { return std::tie(x, y);} - - private: - float x = 0.f, - y = 0.f; + public: // Properties + [[nodiscard]] inline auto length() const -> float { return std::sqrt(x * x + y * y); } + [[nodiscard]] inline constexpr auto length_sq() const -> float { return x * x + y * y; } + inline auto properties() -> auto { return std::tie(x, y); } + + public: // Operations + inline static auto zero() noexcept -> Vector2 { return {0.0f, 0.0f}; } + inline static auto one() noexcept -> Vector2 { return {1.0f, 1.0f}; } + + + static inline auto dot(const Vector2& a, const Vector2& b) noexcept -> float { return a.x * b.x + a.y * b.y; } + static inline auto cross(const Vector2& a, const Vector2& b) noexcept -> float { return a.x * b.y - a.y * b.x; } + static inline auto normalize(const Vector2& v) noexcept -> Vector2 { + float len = v.length(); + return (len > 0.0f) ? Vector2{v.x / len, v.y / len} : Vector2{0.0f, 0.0f}; + } + static inline auto lerp(const Vector2& a, const Vector2& b, float t) noexcept -> Vector2 { + return {a.x + t * (b.x - a.x), a.y + t * (b.y - a.y)}; + } + static inline auto min(const Vector2& a, const Vector2& b) noexcept -> Vector2 { + return {(a.x < b.x) ? a.x : b.x, (a.y < b.y) ? a.y : b.y}; + } + static inline auto max(const Vector2& a, const Vector2& b) noexcept -> Vector2 { + return {(a.x > b.x) ? a.x : b.x, (a.y > b.y) ? a.y : b.y}; + } + static inline auto perp(const Vector2& v) noexcept -> Vector2 { return {-v.y, v.x}; } + static inline auto reflect(const Vector2& v, const Vector2& n) noexcept -> Vector2 { + float d = 2.0f * dot(v, n); + return {v.x - d * n.x, v.y - d * n.y}; + } + static inline auto distance(const Vector2& a, const Vector2& b) noexcept -> float { + float dx = b.x - a.x; + float dy = b.y - a.y; + return std::sqrt(dx * dx + dy * dy); + } }; // class Vector2 -} // namespace math +} // namespace gkit::math \ No newline at end of file diff --git a/src/math/vector2.cpp b/src/math/vector2.cpp index 94646aa..c50c559 100644 --- a/src/math/vector2.cpp +++ b/src/math/vector2.cpp @@ -1,18 +1,6 @@ -#include - +#include "gkit/math/vector2.hpp" +gkit::math::Vector2::Vector2(float v) noexcept : x(v), y(v) { } gkit::math::Vector2::Vector2(float x, float y) noexcept : x(x), y(y) { } gkit::math::Vector2::Vector2(const gkit::math::Vector2& other) noexcept : x(other.x), y(other.y) { } gkit::math::Vector2::Vector2(const gkit::math::Vector2&& other) noexcept : x(other.x), y(other.y) { } - - -auto gkit::math::Vector2::normalization() -> void { - auto len = this->length(); - this->x /= len; this->y /= len; -} - - -auto gkit::math::Vector2::zero() -> const Vector2& { - static Vector2 zero = Vector2(0.0f, 0.0f); - return zero; -} diff --git a/test/math/test_math.cpp b/test/math/test_math.cpp new file mode 100644 index 0000000..f997a7a --- /dev/null +++ b/test/math/test_math.cpp @@ -0,0 +1,76 @@ +#include +#include +#include "gkit/math/constants.hpp" +#include "gkit/math/scalar.hpp" + +using namespace gkit::math; + +int main() { + // ====== test scalar.hpp ====== + + // ScalarLimits tests + assert(ScalarLimits::min_v == -2147483647 - 1); + assert(ScalarLimits::max_v == 2147483647); + assert(ScalarLimits::epsilon_v > 0); + + // fp constants tests + assert(EPSILON32 > 0); + assert(MIN32 > 0); + assert(MAX32 > MIN32); + + // integer constants tests + assert(I8_MIN == -128); + assert(I8_MAX == 127); + assert(U8_MAX == 255); + assert(I16_MIN == -32768); + assert(I16_MAX == 32767); + assert(U16_MAX == 65535); + + // ====== Test constants.hpp ====== + + // Math constants tests + assert(gkit::math::PI_32 > 3.14f && gkit::math::PI_32 < 3.15f); + assert(gkit::math::TWO_PI_32 > 6.28f && gkit::math::TWO_PI_32 < 6.29f); + assert(gkit::math::HALF_PI_32 > 1.57f && gkit::math::HALF_PI_32 < 1.58f); + assert(gkit::math::INV_PI_32 > 0.31f && gkit::math::INV_PI_32 < 0.32f); + assert(gkit::math::E_32 > 2.71f && gkit::math::E_32 < 2.72f); + assert(gkit::math::PHI_32 > 1.61f && gkit::math::PHI_32 < 1.62f); + assert(gkit::math::SQRT_2_32 > 1.41f && gkit::math::SQRT_2_32 < 1.42f); + assert(gkit::math::LN_2_32 > 0.69f && gkit::math::LN_2_32 < 0.70f); + + // Angle conversion tests + assert(gkit::math::DEG_TO_RAD_32 > 0.017f && gkit::math::DEG_TO_RAD_32 < 0.018f); + assert(gkit::math::RAD_TO_DEG_32 > 57.2f && gkit::math::RAD_TO_DEG_32 < 57.3f); + + // Numeric constants tests + assert(gkit::math::ZERO_32 == 0.0f); + assert(gkit::math::ONE_32 == 1.0f); + assert(gkit::math::NEG_ONE_32 == -1.0f); + assert(gkit::math::TWO_32 == 2.0f); + assert(gkit::math::HALF_32 == 0.5f); + assert(gkit::math::QUARTER_32 == 0.25f); + + assert(gkit::math::ZERO_I32 == 0); + assert(gkit::math::ONE_I32 == 1); + assert(gkit::math::NEG_ONE_I32 == -1); + assert(gkit::math::TWO_I32 == 2); + + // Print test output + printf("=== scalar.hpp tests ===\n"); + printf("int32 min: %d, max: %d\n", + gkit::math::ScalarLimits::min_v, + gkit::math::ScalarLimits::max_v); + printf("float32 epsilon: %.10f\n", gkit::math::ScalarLimits::epsilon_v); + + printf("\n=== constants.hpp tests ===\n"); + printf("PI_32: %.10f\n", gkit::math::PI_32); + printf("TWO_PI_32: %.10f\n", gkit::math::TWO_PI_32); + printf("E_32: %.10f\n", gkit::math::E_32); + printf("PHI_32: %.10f\n", gkit::math::PHI_32); + printf("SQRT_2_32: %.10f\n", gkit::math::SQRT_2_32); + printf("DEG_TO_RAD_32: %.10f\n", gkit::math::DEG_TO_RAD_32); + printf("RAD_TO_DEG_32: %.10f\n", gkit::math::RAD_TO_DEG_32); + + printf("\nAll tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/test/math/test_vector2.cpp b/test/math/test_vector2.cpp index 19f4842..c9f5cd0 100644 --- a/test/math/test_vector2.cpp +++ b/test/math/test_vector2.cpp @@ -1,32 +1,120 @@ -#include +#include "gkit/math/vector2.hpp" +#include "gkit/math/scalar.hpp" + +#include +#include #include #include #include + using gkit::math::Vector2; -auto vec_str(Vector2& vec) -> std::string { - auto [x, y] = vec.properties(); - return std::format("x = {}, y = {}", x, y); +auto vec_str(const Vector2& vec) -> std::string { + return std::format("x = {:.4f}, y = {:.4f}", vec.x, vec.y); } auto main() -> int { + // Basic constructors Vector2 vec1(1.0f, 2.0f); std::cout << "vec1: " << vec_str(vec1) << std::endl; + + // Fill constructor + Vector2 vec_fill(5.0f); + assert(vec_fill.x == 5.0f && vec_fill.y == 5.0f); + std::cout << "vec_fill (Vector2(5.0f)): " << vec_str(vec_fill) << std::endl; + Vector2 vec2(3.0f, 4.0f); std::cout << "vec2: " << vec_str(vec2) << std::endl; - Vector2 vec3 = vec1 + vec2; - std::cout << "vec1 + vec2: " << vec_str(vec3) << std::endl; - Vector2 vec4 = vec1 - vec2; - std::cout << "vec1 - vec2: " << vec_str(vec4) << std::endl; - Vector2 vec5 = vec1 * 10; - std::cout << "vec1 * 10: " << vec_str(vec5) << std::endl; - Vector2 vec6 = vec1 / 10; - std::cout << "vec1 / 10: " << vec_str(vec6) << std::endl; + // Arithmetic operators + Vector2 vec_add = vec1 + vec2; + std::cout << "vec1 + vec2: " << vec_str(vec_add) << std::endl; + assert(vec_add.x == 4.0f && vec_add.y == 6.0f); + + Vector2 vec_sub = vec1 - vec2; + std::cout << "vec1 - vec2: " << vec_str(vec_sub) << std::endl; + assert(vec_sub.x == -2.0f && vec_sub.y == -2.0f); + + Vector2 vec_mul = vec1 * 10.0f; + std::cout << "vec1 * 10: " << vec_str(vec_mul) << std::endl; + assert(vec_mul.x == 10.0f && vec_mul.y == 20.0f); + Vector2 vec_div = vec1 / 10.0f; + std::cout << "vec1 / 10: " << vec_str(vec_div) << std::endl; + assert(std::abs(vec_div.x - 0.1f) < gkit::math::EPSILON32 && std::abs(vec_div.y - 0.2f) < gkit::math::EPSILON32); + + // Length std::cout << "vec1.length(): " << vec1.length() << std::endl; - vec1.normalization(); - std::cout << "vec1.normalization(): " << vec_str(vec1) << std::endl; - std::cout << "vec1.length() after normalization(): " << vec1.length() << std::endl; + assert(std::abs(vec1.length() - std::sqrt(5.0f)) < gkit::math::EPSILON32); + + // Length squared + std::cout << "vec1.length_sq(): " << vec1.length_sq() << std::endl; + assert(std::abs(vec1.length_sq() - 5.0f) < gkit::math::EPSILON32); + + // Normalization + vec1 = Vector2::normalize(vec1); + std::cout << "vec1 after normalize: " << vec_str(vec1) << std::endl; + assert(std::abs(vec1.length() - 1.0f) < gkit::math::EPSILON32); + + // Reset for further tests + Vector2 a(1.0f, 0.0f); + Vector2 b(0.0f, 1.0f); + + // Dot product + float dot_result = Vector2::dot(a, b); + std::cout << "Vector2::dot(a, b): " << dot_result << std::endl; + assert(dot_result == 0.0f); + + float dot_a = Vector2::dot(a, a); + assert(dot_a == 1.0f); + + // Cross product + float cross_result = Vector2::cross(a, b); + std::cout << "Vector2::cross(a, b): " << cross_result << std::endl; + assert(cross_result == 1.0f); + + // Normalize (static) + Vector2 c(3.0f, 4.0f); + Vector2 c_normalized = Vector2::normalize(c); + std::cout << "Vector2::normalize(3,4): " << vec_str(c_normalized) << std::endl; + assert(std::abs(c_normalized.length() - 1.0f) < gkit::math::EPSILON32); + + // Lerp + Vector2 lerp_result = Vector2::lerp(a, b, 0.5f); + std::cout << "Vector2::lerp((1,0), (0,1), 0.5): " << vec_str(lerp_result) << std::endl; + assert(std::abs(lerp_result.x - 0.5f) < gkit::math::EPSILON32); + assert(std::abs(lerp_result.y - 0.5f) < gkit::math::EPSILON32); + + // Min / Max + Vector2 min_result = Vector2::min(a, b); + std::cout << "Vector2::min((1,0), (0,1)): " << vec_str(min_result) << std::endl; + assert(min_result.x == 0.0f && min_result.y == 0.0f); + + Vector2 max_result = Vector2::max(a, b); + std::cout << "Vector2::max((1,0), (0,1)): " << vec_str(max_result) << std::endl; + assert(max_result.x == 1.0f && max_result.y == 1.0f); + + // Perp + Vector2 perp_result = Vector2::perp(a); + std::cout << "Vector2::perp((1,0)): " << vec_str(perp_result) << std::endl; + assert(perp_result.x == 0.0f && perp_result.y == 1.0f); + + // Reflect + Vector2 v(1.0f, 1.0f); + Vector2 n(0.0f, 1.0f); // Reflect off horizontal surface + Vector2 reflect_result = Vector2::reflect(v, n); + std::cout << "Vector2::reflect((1,1), (0,1)): " << vec_str(reflect_result) << std::endl; + assert(std::abs(reflect_result.x - 1.0f) < gkit::math::EPSILON32); + assert(std::abs(reflect_result.y - (-1.0f)) < gkit::math::EPSILON32); + + // Distance + Vector2 p1(0.0f, 0.0f); + Vector2 p2(3.0f, 4.0f); + float dist = Vector2::distance(p1, p2); + std::cout << "Vector2::distance((0,0), (3,4)): " << dist << std::endl; + assert(std::abs(dist - 5.0f) < gkit::math::EPSILON32); + + std::cout << "\nAll tests passed!" << std::endl; + return 0; } \ No newline at end of file