From 1656eb2b2d04998366d6d8a8d1d21cbd5a1acb5c Mon Sep 17 00:00:00 2001 From: YuanSang <142496327+YuanSang0512@users.noreply.github.com> Date: Thu, 16 Apr 2026 17:33:42 +0800 Subject: [PATCH 1/5] =?UTF-8?q?Add=20vector3(Point3=E3=80=81Color3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/gkit/math/vector3.hpp | 130 ++++++++++++++++++++++++++-------- src/math/vector3.cpp | 19 +---- test/math/test_vector3.cpp | 14 +++- 3 files changed, 118 insertions(+), 45 deletions(-) diff --git a/include/gkit/math/vector3.hpp b/include/gkit/math/vector3.hpp index 57619c8..e2869f9 100644 --- a/include/gkit/math/vector3.hpp +++ b/include/gkit/math/vector3.hpp @@ -1,44 +1,118 @@ #pragma once +#include "gkit/math/vector2.hpp" + #include #include #include + namespace gkit::math { - class Vector3 final { + class Vector3 { public: + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + Vector3() noexcept = default; - Vector3(float x, float y, float z) noexcept; - Vector3(const Vector3& other) noexcept; + explicit Vector3(float v) noexcept; + Vector3(float x, float y, float z) noexcept; + Vector3(const Vector2& v, float z) noexcept; + Vector3(const Vector3& other) noexcept = default; Vector3(const Vector3&& other) noexcept; + ~Vector3() noexcept = default; public: // Arithmetic operators - inline auto operator=(const Vector3& other) noexcept -> Vector3& { this->x = other.x; this->y = other.y; this->z = other.z; return *this;}; - inline auto operator=(const Vector3&& other) noexcept -> Vector3& { this->x = other.x; this->y = other.y; this->z = other.z; return *this;} - inline auto operator==(const Vector3& other) noexcept -> bool { return (this->x == other.x) && (this->y == other.y) && (this->z == other.z);} - inline auto operator!=(const Vector3& other) noexcept -> bool { return (this->x != other.x) || (this->y != other.y) || (this->z != other.z);} - inline auto operator+(const Vector3& other) noexcept -> Vector3 { return Vector3(x + other.x, y + other.y, z + other.z); } - inline auto operator-(const Vector3& other) noexcept -> Vector3 { return Vector3(x - other.x, y - other.y, z - other.z);} + inline auto operator=(const Vector3& other) noexcept -> Vector3& = default; + inline auto operator==(const Vector3& other) noexcept -> bool { return (this->x == other.x) && (this->y == other.y) && (this->z == other.z); } + inline auto operator!=(const Vector3& other) noexcept -> bool { return (this->x != other.x) || (this->y != other.y) || (this->z != other.z); } + inline auto operator+(const Vector3& other) noexcept -> Vector3 { return Vector3(this->x + other.x, this->y + other.y, this->z + other.z); } + inline auto operator-(const Vector3& other) noexcept -> Vector3 { return Vector3(this->x - other.x, this->y - other.y, this->z - other.z); } inline auto operator+=(const Vector3& other) noexcept -> const Vector3& { this->x += other.x; this->y += other.y; this->z += other.z; return *this; } inline auto operator-=(const Vector3& other) noexcept -> const Vector3& { this->x -= other.x; this->y -= other.y; this->z -= other.z; return *this; } - inline auto operator*(const int32_t n) noexcept -> Vector3 { return Vector3(x * n, y * n, z * n);} - inline auto operator/(const int32_t n) noexcept -> Vector3 { return Vector3(x / n, y / n, z / n);} - inline auto operator*=(const int32_t n) noexcept -> const Vector3& { this->x *= n; this->y *= n; this->z *= n; return *this;} - inline auto operator/=(const int32_t n) noexcept -> const Vector3& { this->x /= n; this->y /= n; this->z /= n; return *this;} - - public: - static auto zero() -> const Vector3&; - auto normalization() -> void; -#ifdef _MSC_VER - inline auto length() const -> float { return std::sqrt(x * x + y * y + z * z); } -#else - inline constexpr auto length() const -> float { return std::sqrt(x * x + y * y + z * z); } -#endif - inline auto properties() -> auto { return std::tie(x, y, z);} - - private: - float x = 0.f, - y = 0.f, - z = 0.f; + inline auto operator*(float s) noexcept -> Vector3 { return {this->x * s, this->y * s, this->z * s}; } + inline auto operator/(float s) noexcept -> Vector3 { return {this->x / s, this->y / s, this->z / s}; } + inline auto operator*=(float s) noexcept -> const Vector3& { this->x *= s; this->y *= s; this->z *= s; return *this; } + inline auto operator/=(float s) noexcept -> const Vector3& { this->x /= s; this->y /= s; this->z /= s; return *this; } + inline auto operator-() noexcept -> Vector3 { return {-this->x, -this->y, -this->z}; } + + public: // Properties + inline static auto zero() noexcept -> Vector3 { return {0.0f, 0.0f, 0.0f}; } + inline static auto one() noexcept -> Vector3 { return {1.0f, 1.0f, 1.0f}; } + + [[nodiscard]] inline auto length() const -> float { return std::sqrt(x * x + y * y + z * z); } + [[nodiscard]] inline constexpr auto length_sq() const -> float { return x * x + y * y + z * z; } + inline auto properties() -> auto { return std::tie(x, y, z); } + + public: // Operations + static inline auto dot(const Vector3& a, const Vector3& b) noexcept -> float { return a.x * b.x + a.y * b.y + a.z * b.z; } + static inline auto cross(const Vector3& a, const Vector3& b) noexcept -> Vector3 { + return { + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x + }; + } + static inline auto normalize(const Vector3& v) noexcept -> Vector3 { + float len = v.length(); + return (len > 0.0f) ? Vector3{v.x / len, v.y / len, v.z / len} : Vector3{0.0f, 0.0f, 0.0f}; + } + static inline auto lerp(const Vector3& a, const Vector3& b, float t) noexcept -> Vector3 { + return {a.x + t * (b.x - a.x), a.y + t * (b.y - a.y), a.z + t * (b.z - a.z)}; + } + static inline auto min(const Vector3& a, const Vector3& b) noexcept -> Vector3 { + return {(a.x < b.x) ? a.x : b.x, (a.y < b.y) ? a.y : b.y, (a.z < b.z) ? a.z : b.z}; + } + static inline auto max(const Vector3& a, const Vector3& b) noexcept -> Vector3 { + return {(a.x > b.x) ? a.x : b.x, (a.y > b.y) ? a.y : b.y, (a.z > b.z) ? a.z : b.z}; + } + static inline auto reflect(const Vector3& v, const Vector3& n) noexcept -> Vector3 { + float d = 2.0f * dot(v, n); + return {v.x - d * n.x, v.y - d * n.y, v.z - d * n.z}; + } + static inline auto distance(const Vector3& a, const Vector3& b) noexcept -> float { + float dx = b.x - a.x; + float dy = b.y - a.y; + float dz = b.z - a.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + static inline auto angle(const Vector3& a, const Vector3& b) noexcept -> float { + float d = Vector3::dot(a, b); + float len_a = a.length(); + float len_b = b.length(); + return (len_a * len_b > 0.0f) ? std::acos(d / (len_a * len_b)) : 0.0f; + } + static inline auto component_mul(const Vector3& a, const Vector3& b) noexcept -> Vector3 { + return {a.x * b.x, a.y * b.y, a.z * b.z}; + } }; // class Vector3 + + struct Point3 : Vector3 { + using Vector3::Vector3; + }; + + struct Color3 : Vector3 { + using Vector3::Vector3; + + [[nodiscard]] inline auto to_rgb888() const noexcept -> uint32_t { + return (static_cast(x * 255.0f) << 16) | + (static_cast(y * 255.0f) << 8) | + (static_cast(z * 255.0f)); + } + + [[nodiscard]] inline auto to_rgba8888(float alpha = 1.0f) const noexcept -> uint32_t { + return (static_cast(x * 255.0f) << 24) | + (static_cast(y * 255.0f) << 16) | + (static_cast(z * 255.0f) << 8) | + (static_cast(alpha * 255.0f)); + } + + [[nodiscard]] static inline auto from_rgb888(uint32_t rgb) noexcept -> Color3 { + return { + ((rgb >> 16) & 0xFF) / 255.0f, + ((rgb >> 8) & 0xFF) / 255.0f, + (rgb & 0xFF) / 255.0f + }; + } + }; } // namespace gkit::math \ No newline at end of file diff --git a/src/math/vector3.cpp b/src/math/vector3.cpp index fdc7fac..6a5f597 100644 --- a/src/math/vector3.cpp +++ b/src/math/vector3.cpp @@ -1,18 +1,5 @@ #include "gkit/math/vector3.hpp" - -gkit::math::Vector3::Vector3(float x, float y, float z) noexcept : x(x), y(y), z(z) {} -gkit::math::Vector3::Vector3(const Vector3& other) noexcept : x(other.x), y(other.y), z(other.z) {} -gkit::math::Vector3::Vector3(const Vector3&& other) noexcept : x(other.x), y(other.y), z(other.z) {} - - -auto gkit::math::Vector3::normalization() -> void { - auto len = this->length(); - this->x /= len; this->y /= len; this->z /= len; -} - - -auto gkit::math::Vector3::zero() -> const Vector3& { - static Vector3 zero(0.0f, 0.0f, 0.0f); - return zero; -} +gkit::math::Vector3::Vector3(float v) noexcept : x(v), y(v), z(v) { } +gkit::math::Vector3::Vector3(float x, float y, float z) noexcept : x(x), y(y), z(z) { } +gkit::math::Vector3::Vector3(const gkit::math::Vector2& v, float z) noexcept : x(v.x), y(v.y), z(z) { } \ No newline at end of file diff --git a/test/math/test_vector3.cpp b/test/math/test_vector3.cpp index cd87c2b..ae670e6 100644 --- a/test/math/test_vector3.cpp +++ b/test/math/test_vector3.cpp @@ -36,7 +36,19 @@ auto main() -> int { // other methods std::cout << "vec1.length(): " << vec1.length() << std::endl; - vec1.normalization(); + vec1 = vec1.normalize(vec1); std::cout << "vec1.normalization(): " << vec_str(vec1) << std::endl; std::cout << "vec1.length() after normalization: " << vec1.length() << std::endl; + + // Type aliases test + gkit::math::Point3 origin{0.0f, 0.0f, 0.0f}; + std::cout << "Point3 origin: " << vec_str(origin) << std::endl; + + gkit::math::Color3 red{1.0f, 0.0f, 0.0f}; + std::cout << "Color3 red: " << vec_str(red) << std::endl; + + gkit::math::Color3 white{1.0f, 1.0f, 1.0f}; + std::cout << "Color3 white: " << vec_str(white) << std::endl; + auto temp = white.to_rgb888(); + std::cout << "white.to_rgb888: " << temp << std::endl; } \ No newline at end of file From eedacbb3d1460a21fc56a986c4b24180def3042a Mon Sep 17 00:00:00 2001 From: YuanSang <142496327+YuanSang0512@users.noreply.github.com> Date: Thu, 16 Apr 2026 20:37:49 +0800 Subject: [PATCH 2/5] Add vector4; Remove Color --- include/gkit/math/vector2.hpp | 3 +- include/gkit/math/vector3.hpp | 33 +-------------- include/gkit/math/vector4.hpp | 75 +++++++++++++++++++++++++++++++++++ src/math/CMakeLists.txt | 1 + src/math/vector4.cpp | 6 +++ test/math/test_vector3.cpp | 12 ------ test/math/test_vector4.cpp | 42 ++++++++++++++++++++ 7 files changed, 128 insertions(+), 44 deletions(-) create mode 100644 include/gkit/math/vector4.hpp create mode 100644 src/math/vector4.cpp create mode 100644 test/math/test_vector4.cpp diff --git a/include/gkit/math/vector2.hpp b/include/gkit/math/vector2.hpp index 81d4a9e..bc0babf 100644 --- a/include/gkit/math/vector2.hpp +++ b/include/gkit/math/vector2.hpp @@ -35,7 +35,8 @@ namespace gkit::math { 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); } + [[nodiscard]] inline auto properties() const -> auto { return std::tie(x, y); } + [[nodiscard]] inline auto properties() -> auto { return std::tie(x, y); } public: // Operations inline static auto zero() noexcept -> Vector2 { return {0.0f, 0.0f}; } diff --git a/include/gkit/math/vector3.hpp b/include/gkit/math/vector3.hpp index e2869f9..e01f003 100644 --- a/include/gkit/math/vector3.hpp +++ b/include/gkit/math/vector3.hpp @@ -2,7 +2,6 @@ #include "gkit/math/vector2.hpp" -#include #include #include @@ -42,7 +41,8 @@ namespace gkit::math { [[nodiscard]] inline auto length() const -> float { return std::sqrt(x * x + y * y + z * z); } [[nodiscard]] inline constexpr auto length_sq() const -> float { return x * x + y * y + z * z; } - inline auto properties() -> auto { return std::tie(x, y, z); } + [[nodiscard]] inline auto properties() const -> auto { return std::tie(x, y, z); } + [[nodiscard]] inline auto properties() -> auto { return std::tie(x, y, z); } public: // Operations static inline auto dot(const Vector3& a, const Vector3& b) noexcept -> float { return a.x * b.x + a.y * b.y + a.z * b.z; } @@ -86,33 +86,4 @@ namespace gkit::math { return {a.x * b.x, a.y * b.y, a.z * b.z}; } }; // class Vector3 - - struct Point3 : Vector3 { - using Vector3::Vector3; - }; - - struct Color3 : Vector3 { - using Vector3::Vector3; - - [[nodiscard]] inline auto to_rgb888() const noexcept -> uint32_t { - return (static_cast(x * 255.0f) << 16) | - (static_cast(y * 255.0f) << 8) | - (static_cast(z * 255.0f)); - } - - [[nodiscard]] inline auto to_rgba8888(float alpha = 1.0f) const noexcept -> uint32_t { - return (static_cast(x * 255.0f) << 24) | - (static_cast(y * 255.0f) << 16) | - (static_cast(z * 255.0f) << 8) | - (static_cast(alpha * 255.0f)); - } - - [[nodiscard]] static inline auto from_rgb888(uint32_t rgb) noexcept -> Color3 { - return { - ((rgb >> 16) & 0xFF) / 255.0f, - ((rgb >> 8) & 0xFF) / 255.0f, - (rgb & 0xFF) / 255.0f - }; - } - }; } // namespace gkit::math \ No newline at end of file diff --git a/include/gkit/math/vector4.hpp b/include/gkit/math/vector4.hpp new file mode 100644 index 0000000..4fd5b1c --- /dev/null +++ b/include/gkit/math/vector4.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "gkit/math/vector3.hpp" + +#include +#include + + +namespace gkit::math { + class Vector4 { + public: + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + float w = 0.0f; + + Vector4() noexcept = default; + explicit Vector4(float v) noexcept; + Vector4(float x, float y, float z, float w) noexcept; + Vector4(const Vector3& v, float w) noexcept; + Vector4(const Vector2& v, float z, float w) noexcept; + Vector4(const Vector4& other) noexcept = default; + Vector4(const Vector4&& other) noexcept; + ~Vector4() noexcept = default; + + public: // Arithmetic operators + inline auto operator=(const Vector4& other) noexcept -> Vector4& = default; + inline auto operator==(const Vector4& other) noexcept -> bool { return (this->x == other.x) && (this->y == other.y) && (this->z == other.z) && (this->w == other.w); } + inline auto operator!=(const Vector4& other) noexcept -> bool { return (this->x != other.x) || (this->y != other.y) || (this->z != other.z) || (this->w != other.w); } + inline auto operator+(const Vector4& other) noexcept -> Vector4 { return Vector4(this->x + other.x, this->y + other.y, this->z + other.z, this->w + other.w); } + inline auto operator-(const Vector4& other) noexcept -> Vector4 { return Vector4(this->x - other.x, this->y - other.y, this->z - other.z, this->w - other.w); } + inline auto operator+=(const Vector4& other) noexcept -> const Vector4& { this->x += other.x; this->y += other.y; this->z += other.z; this->w += other.w; return *this; } + inline auto operator-=(const Vector4& other) noexcept -> const Vector4& { this->x -= other.x; this->y -= other.y; this->z -= other.z; this->w -= other.w; return *this; } + inline auto operator*(float s) noexcept -> Vector4 { return {this->x * s, this->y * s, this->z * s, this->w * s}; } + inline auto operator/(float s) noexcept -> Vector4 { return {this->x / s, this->y / s, this->z / s, this->w / s}; } + inline auto operator*=(float s) noexcept -> const Vector4& { this->x *= s; this->y *= s; this->z *= s; this->w *= s; return *this; } + inline auto operator/=(float s) noexcept -> const Vector4& { this->x /= s; this->y /= s; this->z /= s; this->w /= s; return *this; } + inline auto operator-() noexcept -> Vector4 { return {-this->x, -this->y, -this->z, -this->w}; } + + public: // Properties + inline static auto zero() noexcept -> Vector4 { return {0.0f, 0.0f, 0.0f, 0.0f}; } + inline static auto one() noexcept -> Vector4 { return {1.0f, 1.0f, 1.0f, 1.0f}; } + + [[nodiscard]] inline auto length() const -> float { return std::sqrt(x * x + y * y + z * z + w * w); } + [[nodiscard]] inline constexpr auto length_sq() const -> float { return x * x + y * y + z * z + w * w; } + [[nodiscard]] inline auto properties() const -> auto { return std::tie(x, y, z, w); } + [[nodiscard]] inline auto properties() -> auto { return std::tie(x, y, z, w); } + + public: // Operations + static inline auto dot(const Vector4& a, const Vector4& b) noexcept -> float { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } + static inline auto normalize(const Vector4& v) noexcept -> Vector4 { + float len = v.length(); + return (len > 0.0f) ? Vector4{v.x / len, v.y / len, v.z / len, v.w / len} : Vector4{0.0f, 0.0f, 0.0f, 0.0f}; + } + static inline auto lerp(const Vector4& a, const Vector4& b, float t) noexcept -> Vector4 { + return {a.x + t * (b.x - a.x), a.y + t * (b.y - a.y), a.z + t * (b.z - a.z), a.w + t * (b.w - a.w)}; + } + static inline auto min(const Vector4& a, const Vector4& b) noexcept -> Vector4 { + return {(a.x < b.x) ? a.x : b.x, (a.y < b.y) ? a.y : b.y, (a.z < b.z) ? a.z : b.z, (a.w < b.w) ? a.w : b.w}; + } + static inline auto max(const Vector4& a, const Vector4& b) noexcept -> Vector4 { + return {(a.x > b.x) ? a.x : b.x, (a.y > b.y) ? a.y : b.y, (a.z > b.z) ? a.z : b.z, (a.w > b.w) ? a.w : b.w}; + } + static inline auto distance(const Vector4& a, const Vector4& b) noexcept -> float { + float dx = b.x - a.x; + float dy = b.y - a.y; + float dz = b.z - a.z; + float dw = b.w - a.w; + return std::sqrt(dx * dx + dy * dy + dz * dz + dw * dw); + } + static inline auto component_mul(const Vector4& a, const Vector4& b) noexcept -> Vector4 { + return {a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w}; + } + }; // class Vector4 +} // namespace gkit::math \ No newline at end of file diff --git a/src/math/CMakeLists.txt b/src/math/CMakeLists.txt index dffcb25..242f81e 100644 --- a/src/math/CMakeLists.txt +++ b/src/math/CMakeLists.txt @@ -3,6 +3,7 @@ set (GKIT_MATH "gkit_math") set (MATH_SRC "./vector2.cpp" "./vector3.cpp" + "./vector4.cpp" ) add_library(${GKIT_MATH} OBJECT ${MATH_SRC}) diff --git a/src/math/vector4.cpp b/src/math/vector4.cpp new file mode 100644 index 0000000..a1947da --- /dev/null +++ b/src/math/vector4.cpp @@ -0,0 +1,6 @@ +#include "gkit/math/vector4.hpp" + +gkit::math::Vector4::Vector4(float v) noexcept : x(v), y(v), z(v), w(v) { } +gkit::math::Vector4::Vector4(float x, float y, float z, float w) noexcept : x(x), y(y), z(z), w(w) { } +gkit::math::Vector4::Vector4(const gkit::math::Vector3& v, float w) noexcept : x(v.x), y(v.y), z(v.z), w(w) { } +gkit::math::Vector4::Vector4(const gkit::math::Vector2& v, float z, float w) noexcept : x(v.x), y(v.y), z(z), w(w) { } \ No newline at end of file diff --git a/test/math/test_vector3.cpp b/test/math/test_vector3.cpp index ae670e6..ecc6287 100644 --- a/test/math/test_vector3.cpp +++ b/test/math/test_vector3.cpp @@ -39,16 +39,4 @@ auto main() -> int { vec1 = vec1.normalize(vec1); std::cout << "vec1.normalization(): " << vec_str(vec1) << std::endl; std::cout << "vec1.length() after normalization: " << vec1.length() << std::endl; - - // Type aliases test - gkit::math::Point3 origin{0.0f, 0.0f, 0.0f}; - std::cout << "Point3 origin: " << vec_str(origin) << std::endl; - - gkit::math::Color3 red{1.0f, 0.0f, 0.0f}; - std::cout << "Color3 red: " << vec_str(red) << std::endl; - - gkit::math::Color3 white{1.0f, 1.0f, 1.0f}; - std::cout << "Color3 white: " << vec_str(white) << std::endl; - auto temp = white.to_rgb888(); - std::cout << "white.to_rgb888: " << temp << std::endl; } \ No newline at end of file diff --git a/test/math/test_vector4.cpp b/test/math/test_vector4.cpp new file mode 100644 index 0000000..0491930 --- /dev/null +++ b/test/math/test_vector4.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +using gkit::math::Vector4; + +auto vec_str(const gkit::math::Vector4& vec) -> std::string { + auto [x, y, z, w] = vec.properties(); + return std::format("x = {}, y = {}, z = {}, w = {}", x, y, z, w); +} + +auto main() -> int { + // constructor test + Vector4 vec1{1.0f, 2.0f, 3.0f, 4.0f}; + std::cout << "vec1: " << vec_str(vec1) << std::endl; + + Vector4 vec2(vec1); + std::cout << "vec2(construct from vec1): " << vec_str(vec2) << std::endl; + + auto vec3 = vec1; + std::cout << "vec3(construct with = from vec1): " << vec_str(vec3) << std::endl; + + // Arithmetic operators test + auto vec4 = vec2 + vec3; + std::cout << "vec4(vec2 + vec3): " << vec_str(vec4) << std::endl; + + auto vec5 = vec2 - vec3; + std::cout << "vec5(vec2 - vec3): " << vec_str(vec5) << std::endl; + + auto vec6 = vec2 * 10.0f; + std::cout << "vec6(vec2 * 10): " << vec_str(vec6) << std::endl; + + auto vec7 = vec2 / 5.0f; + std::cout << "vec7(vec2 / 5): " << vec_str(vec7) << std::endl; + + // other methods + std::cout << "vec1.length(): " << vec1.length() << std::endl; + vec1 = Vector4::normalize(vec1); + std::cout << "vec1 after normalize: " << vec_str(vec1) << std::endl; + std::cout << "vec1.length() after normalize: " << vec1.length() << std::endl; +} \ No newline at end of file From e7099568957d598d2a230368fbecff384c87968e Mon Sep 17 00:00:00 2001 From: YuanSang <142496327+YuanSang0512@users.noreply.github.com> Date: Thu, 16 Apr 2026 21:51:29 +0800 Subject: [PATCH 3/5] =?UTF-8?q?Add=20color=EF=BC=88RGB=E3=80=81RGBA?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/gkit/math/color.hpp | 341 ++++++++++++++++++++++++++++++++++++ test/math/test_color.cpp | 89 ++++++++++ 2 files changed, 430 insertions(+) create mode 100644 include/gkit/math/color.hpp create mode 100644 test/math/test_color.cpp diff --git a/include/gkit/math/color.hpp b/include/gkit/math/color.hpp new file mode 100644 index 0000000..53b867d --- /dev/null +++ b/include/gkit/math/color.hpp @@ -0,0 +1,341 @@ +#pragma once + +#include +#include +#include + + +namespace gkit::math { + +// ------------------------------ +// Predefined Color Constants +// ------------------------------ + namespace colors { + // Basic 8-color palette (BGRA format: 0xAABBGGRR) + inline constexpr std::array palette_8 = { + 0xFF000000, // 0: Black + 0xFF0000FF, // 1: Red + 0xFF00FF00, // 2: Green + 0xFF00FFFF, // 3: Yellow + 0xFFFF0000, // 4: Blue + 0xFFFF00FF, // 5: Magenta + 0xFFFFFF00, // 6: Cyan + 0xFFFFFFFF // 7: White + }; + + // Basic colors + inline constexpr auto black() { return std::array{ 0.0f, 0.0f, 0.0f }; } + inline constexpr auto white() { return std::array{ 1.0f, 1.0f, 1.0f }; } + inline constexpr auto red() { return std::array{ 1.0f, 0.0f, 0.0f }; } + inline constexpr auto green() { return std::array{ 0.0f, 1.0f, 0.0f }; } + inline constexpr auto blue() { return std::array{ 0.0f, 0.0f, 1.0f }; } + inline constexpr auto yellow() { return std::array{ 1.0f, 1.0f, 0.0f }; } + inline constexpr auto cyan() { return std::array{ 0.0f, 1.0f, 1.0f }; } + inline constexpr auto magenta() { return std::array{ 1.0f, 0.0f, 1.0f }; } + + // Grayscale + inline constexpr auto gray() { return std::array{ 0.5f, 0.5f, 0.5f }; } + inline constexpr auto dark_gray() { return std::array{ 0.25f, 0.25f, 0.25f }; } + inline constexpr auto light_gray() { return std::array{ 0.75f, 0.75f, 0.75f }; } + + // HTML color names + namespace html { + inline constexpr auto coral() { return std::array{ 1.0f, 0.5f, 0.31f }; } + inline constexpr auto tomato() { return std::array{ 1.0f, 0.39f, 0.28f }; } + inline constexpr auto gold() { return std::array{ 1.0f, 0.84f, 0.0f }; } + inline constexpr auto orange() { return std::array{ 1.0f, 0.65f, 0.0f }; } + inline constexpr auto purple() { return std::array{ 0.5f, 0.0f, 0.5f }; } + inline constexpr auto violet() { return std::array{ 0.93f, 0.51f, 0.93f }; } + inline constexpr auto pink() { return std::array{ 1.0f, 0.75f, 0.8f }; } + inline constexpr auto brown() { return std::array{ 0.65f, 0.16f, 0.16f }; } + inline constexpr auto olive() { return std::array{ 0.5f, 0.5f, 0.0f }; } + inline constexpr auto navy() { return std::array{ 0.0f, 0.0f, 0.5f }; } + inline constexpr auto teal() { return std::array{ 0.0f, 0.5f, 0.5f }; } + inline constexpr auto silver() { return std::array{ 0.75f, 0.75f, 0.75f }; } + } // namespace html + } // namespace colors + + +// ------------------------------ +// Color Operation Functions +// ------------------------------ + // Format conversion + inline auto rgb_to_rgba(uint32_t rgb, uint8_t alpha = 255) noexcept -> uint32_t { + return (static_cast(alpha) << 24) | (rgb & 0x00FFFFFF); + } + + inline auto rgba_to_rgb(uint32_t rgba) noexcept -> uint32_t { + return rgba & 0x00FFFFFF; + } + + inline auto rgba_to_alpha(uint32_t rgba) noexcept -> uint8_t { + return static_cast(rgba >> 24); + } + + // Extract RGB components + inline auto get_r(uint32_t rgb) noexcept -> uint8_t { + return static_cast((rgb >> 16) & 0xFF); + } + inline auto get_g(uint32_t rgb) noexcept -> uint8_t { + return static_cast((rgb >> 8) & 0xFF); + } + inline auto get_b(uint32_t rgb) noexcept -> uint8_t { + return static_cast(rgb & 0xFF); + } + + // Color blending + inline auto alpha_blend(uint32_t src, uint32_t dst) noexcept -> uint32_t { + auto sa = static_cast(src >> 24); + auto da = static_cast(dst >> 24); + + // Fully transparent source: return destination + if (sa == 0) return dst; + // Fully opaque source: return source + if (sa == 255) return src; + + // Alpha blending: result = src * alpha + dst * (1 - alpha) + float alpha = sa / 255.0f; + float inv_alpha = 1.0f - alpha; + + auto r = static_cast(((src >> 16) & 0xFF) * alpha + ((dst >> 16) & 0xFF) * inv_alpha); + auto g = static_cast(((src >> 8) & 0xFF) * alpha + ((dst >> 8) & 0xFF) * inv_alpha); + auto b = static_cast((src & 0xFF) * alpha + (dst & 0xFF) * inv_alpha); + auto a = static_cast(sa + da * inv_alpha); + + return (static_cast(a) << 24) | + (static_cast(r) << 16) | + (static_cast(g) << 8) | + static_cast(b); + } + + // Premultiplied alpha blending + inline auto premultiplied_blend(uint32_t src, uint32_t dst) noexcept -> uint32_t { + auto sa = static_cast(src >> 24); + auto da = static_cast(dst >> 24); + + // Fully transparent source: return destination + if (sa == 0) return dst; + // Fully opaque source: return source + if (sa == 255) return src; + + // Premultiplied alpha: src already multiplied by its alpha + float alpha = sa / 255.0f; + auto ra = static_cast(std::min(255, sa + da)); + + auto r = static_cast(((src >> 16) & 0xFF) + ((dst >> 16) & 0xFF) * (1.0f - alpha)); + auto g = static_cast(((src >> 8) & 0xFF) + ((dst >> 8) & 0xFF) * (1.0f - alpha)); + auto b = static_cast((src & 0xFF) + (dst & 0xFF) * (1.0f - alpha)); + + return (static_cast(ra) << 24) | + (static_cast(r) << 16) | + (static_cast(g) << 8) | + static_cast(b); + } + + // Brightness adjustment + inline auto brightness(uint32_t rgb, float factor) noexcept -> uint32_t { + float r = ((rgb >> 16) & 0xFF) * factor; + float g = ((rgb >> 8) & 0xFF) * factor; + float b = (rgb & 0xFF) * factor; + return (static_cast(std::min(255.0f, r)) << 16) | + (static_cast(std::min(255.0f, g)) << 8) | + static_cast(std::min(255.0f, b)); + } + + // Contrast adjustment + inline auto contrast(uint32_t rgb, float factor) noexcept -> uint32_t { + float r = (((rgb >> 16) & 0xFF) - 128.0f) * factor + 128.0f; + float g = (((rgb >> 8) & 0xFF) - 128.0f) * factor + 128.0f; + float b = ((rgb & 0xFF) - 128.0f) * factor + 128.0f; + return (static_cast(std::max(0.0f, std::min(255.0f, r))) << 16) | + (static_cast(std::max(0.0f, std::min(255.0f, g))) << 8) | + static_cast(std::max(0.0f, std::min(255.0f, b))); + } + + // Grayscale conversion + inline auto grayscale(uint32_t rgb) noexcept -> uint32_t { + uint8_t gray = static_cast(0.299f * ((rgb >> 16) & 0xFF) + + 0.587f * ((rgb >> 8) & 0xFF) + + 0.114f * (rgb & 0xFF)); + return (gray << 16) | (gray << 8) | gray; + } + + // Color inversion + inline auto invert(uint32_t rgb) noexcept -> uint32_t { + return 0xFF000000 | (~rgb & 0x00FFFFFF); + } + + // Set color components + inline auto set_r(uint32_t rgb, uint8_t r) noexcept -> uint32_t { + return (rgb & 0x00FFFFFF) | (static_cast(r) << 16); + } + inline auto set_g(uint32_t rgb, uint8_t g) noexcept -> uint32_t { + return (rgb & 0xFFFF00FF) | (static_cast(g) << 8); + } + inline auto set_b(uint32_t rgb, uint8_t b) noexcept -> uint32_t { + return (rgb & 0xFFFFFF00) | static_cast(b); + } + inline auto set_a(uint32_t rgb, uint8_t a) noexcept -> uint32_t { + return (static_cast(a) << 24) | (rgb & 0x00FFFFFF); + } + + + // ------------------------------ + // HSV <-> RGB Conversion + // ------------------------------ + struct HSV { + float h; // 0-360 Hue + float s; // 0-1 Saturation + float v; // 0-1 Value + }; + + inline auto rgb_to_hsv(uint32_t rgb) noexcept -> HSV { + float r = ((rgb >> 16) & 0xFF) / 255.0f; + float g = ((rgb >> 8) & 0xFF) / 255.0f; + float b = (rgb & 0xFF) / 255.0f; + + float max_val = std::max(std::max(r, g), b); + float min_val = std::min(std::min(r, g), b); + float delta = max_val - min_val; + + // Value + float v = max_val; + + // Saturation + float s = (max_val > 0.0f) ? delta / max_val : 0.0f; + + // Hue + float h = 0.0f; + if (delta > 0.0f) { + if (max_val == r) h = 60.0f * std::fmod((g - b) / delta, 6.0f); + else if (max_val == g) h = 60.0f * ((b - r) / delta + 2.0f); + else h = 60.0f * ((r - g) / delta + 4.0f); + } + if (h < 0.0f) h += 360.0f; + + return { .h = h, .s = s, .v = v }; + } + + inline auto hsv_to_rgb(const HSV& hsv) noexcept -> uint32_t { + float h = hsv.h; + float s = hsv.s; + float v = hsv.v; + + float c = v * s; + float x = c * (1.0f - std::abs(std::fmod(h / 60.0f, 2.0f) - 1.0f)); + float m = v - c; + + float r, g, b; + if (h < 60.0f) { r = c; g = x; b = 0; } + else if (h < 120.0f) { r = x; g = c; b = 0; } + else if (h < 180.0f) { r = 0; g = c; b = x; } + else if (h < 240.0f) { r = 0; g = x; b = c; } + else if (h < 300.0f) { r = x; g = 0; b = c; } + else { r = c; g = 0; b = x; } + + auto R = static_cast((r + m) * 255.0f); + auto G = static_cast((g + m) * 255.0f); + auto B = static_cast((b + m) * 255.0f); + + return (static_cast(R) << 16) | + (static_cast(G) << 8) | + static_cast(B); + } + + + // ------------------------------ + // RGB <-> HSL Conversion + // ------------------------------ + struct HSL { + float h; // 0-360 Hue + float s; // 0-1 Saturation + float l; // 0-1 Lightness + }; + + inline auto rgb_to_hsl(uint32_t rgb) noexcept -> HSL { + float r = ((rgb >> 16) & 0xFF) / 255.0f; + float g = ((rgb >> 8) & 0xFF) / 255.0f; + float b = (rgb & 0xFF) / 255.0f; + + float max_val = std::max(std::max(r, g), b); + float min_val = std::min(std::min(r, g), b); + float delta = max_val - min_val; + + float l = (max_val + min_val) / 2.0f; + + float s = 0.0f; + if (delta > 0.0f) { + s = l > 0.5f ? delta / (2.0f - max_val - min_val) : delta / (max_val + min_val); + } + + float h = 0.0f; + if (delta > 0.0f) { + if (max_val == r) h = 60.0f * std::fmod((g - b) / delta, 6.0f); + else if (max_val == g) h = 60.0f * ((b - r) / delta + 2.0f); + else h = 60.0f * ((r - g) / delta + 4.0f); + } + if (h < 0.0f) h += 360.0f; + + return { .h=h, .s=s, .l=l }; + } + + inline auto hsl_to_rgb(const HSL& hsl) noexcept -> uint32_t { + float h = hsl.h; + float s = hsl.s; + float l = hsl.l; + + float c = (1.0f - std::abs(2.0f * l - 1.0f)) * s; + float x = c * (1.0f - std::abs(std::fmod(h / 60.0f, 2.0f) - 1.0f)); + float m = l - c / 2.0f; + + float r, g, b; + if (h < 60.0f) { r = c; g = x; b = 0; } + else if (h < 120.0f) { r = x; g = c; b = 0; } + else if (h < 180.0f) { r = 0; g = c; b = x; } + else if (h < 240.0f) { r = 0; g = x; b = c; } + else if (h < 300.0f) { r = x; g = 0; b = c; } + else { r = c; g = 0; b = x; } + + auto R = static_cast((r + m) * 255.0f); + auto G = static_cast((g + m) * 255.0f); + auto B = static_cast((b + m) * 255.0f); + + return (static_cast(R) << 16) | + (static_cast(G) << 8) | + static_cast(B); + } + + + // ------------------------------ + // Utility Functions + // ------------------------------ + // Color rounding (for antialiasing) + inline auto nearest_color(uint32_t color) noexcept -> uint32_t { + auto r = static_cast((((color >> 16) & 0xFF) + 127) / 255); + auto g = static_cast((((color >> 8) & 0xFF) + 127) / 255); + auto b = static_cast(((color & 0xFF) + 127) / 255); + return (r << 16) | (g << 8) | b; + } + + // Linear interpolation + inline auto lerp_color(uint32_t a, uint32_t b, float t) noexcept -> uint32_t { + float inv_t = 1.0f - t; + auto r = static_cast(((a >> 16) & 0xFF) * inv_t + ((b >> 16) & 0xFF) * t); + auto g = static_cast(((a >> 8) & 0xFF) * inv_t + ((b >> 8) & 0xFF) * t); + auto b_r = static_cast((a & 0xFF) * inv_t + (b & 0xFF) * t); + auto a_a = static_cast(((a >> 24) * inv_t + (b >> 24) * t)); + return (static_cast(a_a) << 24) | + (static_cast(r) << 16) | + (static_cast(g) << 8) | + static_cast(b_r); + } + + // Check if colors are similar + inline auto is_similar(uint32_t a, uint32_t b, int tolerance = 3) noexcept -> bool { + int dr = ((a >> 16) & 0xFF) - ((b >> 16) & 0xFF); + int dg = ((a >> 8) & 0xFF) - ((b >> 8) & 0xFF); + int db = (a & 0xFF) - (b & 0xFF); + return (dr * dr + dg * dg + db * db) <= tolerance * tolerance * 3; + } + +} // namespace gkit::math \ No newline at end of file diff --git a/test/math/test_color.cpp b/test/math/test_color.cpp new file mode 100644 index 0000000..85681c1 --- /dev/null +++ b/test/math/test_color.cpp @@ -0,0 +1,89 @@ +#include +#include + +using namespace gkit::math; + + +auto main() -> int { + std::cout << "=== Color Format Conversion Test ===" << std::endl; + + // Test: RGB -> RGBA + uint32_t rgb = 0xFF8040; + auto rgba = rgb_to_rgba(rgb, 128); + std::cout << "rgb_to_rgba(0xFF8040, 128) = 0x" << std::hex << rgba << std::dec << std::endl; + + // Test: get_r/g/b + std::cout << "get_r(0xFF8040) = " << static_cast(get_r(rgb)) << std::endl; + std::cout << "get_g(0xFF8040) = " << static_cast(get_g(rgb)) << std::endl; + std::cout << "get_b(0xFF8040) = " << static_cast(get_b(rgb)) << std::endl; + + std::cout << "\n=== Color Blending Test ===" << std::endl; + + // Test: alpha_blend + uint32_t src = 0x80FF0000; // Red with 50% alpha + uint32_t dst = 0xFF0000FF; // Blue + auto blended = alpha_blend(src, dst); + std::cout << "alpha_blend(0x80FF0000, 0xFF0000FF) = 0x" << std::hex << blended << std::dec << std::endl; + + // Test: premultiplied_blend + auto preblended = premultiplied_blend(src, dst); + std::cout << "premultiplied_blend(0x80FF0000, 0xFF0000FF) = 0x" << std::hex << preblended << std::dec << std::endl; + + std::cout << "\n=== Color Adjustment Test ===" << std::endl; + + // Test: brightness + auto bright = brightness(rgb, 1.5f); + std::cout << "brightness(0xFF8040, 1.5) = 0x" << std::hex << bright << std::dec << std::endl; + + // Test: contrast + auto contrast_result = contrast(rgb, 1.2f); + std::cout << "contrast(0xFF8040, 1.2) = 0x" << std::hex << contrast_result << std::dec << std::endl; + + // Test: grayscale + auto gray = grayscale(rgb); + std::cout << "grayscale(0xFF8040) = 0x" << std::hex << gray << std::dec << std::endl; + + // Test: invert + auto inverted = invert(rgb); + std::cout << "invert(0xFF8040) = 0x" << std::hex << inverted << std::dec << std::endl; + + std::cout << "\n=== HSV <-> RGB Test ===" << std::endl; + + // Test: rgb_to_hsv + auto hsv = rgb_to_hsv(0xFF8040); + std::cout << "rgb_to_hsv(0xFF8040): H=" << hsv.h << " S=" << hsv.s << " V=" << hsv.v << std::endl; + + // Test: hsv_to_rgb + HSV hsv2{ .h=180.0f, .s=1.0f, .v=0.5f }; + auto rgb2 = hsv_to_rgb(hsv2); + std::cout << "hsv_to_rgb(180, 1.0, 0.5) = 0x" << std::hex << rgb2 << std::dec << std::endl; + + std::cout << "\n=== HSL <-> RGB Test ===" << std::endl; + + // Test: rgb_to_hsl + auto hsl = rgb_to_hsl(0xFF8040); + std::cout << "rgb_to_hsl(0xFF8040): H=" << hsl.h << " S=" << hsl.s << " L=" << hsl.l << std::endl; + + // Test: hsl_to_rgb + HSL hsl2{ .h=60.0f, .s=1.0f, .l=0.5f }; + auto rgb3 = hsl_to_rgb(hsl2); + std::cout << "hsl_to_rgb(60, 1.0, 0.5) = 0x" << std::hex << rgb3 << std::dec << std::endl; + + std::cout << "\n=== Utility Test ===" << std::endl; + + // Test: lerp_color + auto lerp_result = lerp_color(0xFF0000, 0x0000FF, 0.5f); + std::cout << "lerp_color(0xFF0000, 0x0000FF, 0.5) = 0x" << std::hex << lerp_result << std::dec << std::endl; + + // Test: is_similar + bool similar = is_similar(0xFF0000, 0xFE0101, 5); + std::cout << "is_similar(0xFF0000, 0xFE0101, 5) = " << (similar ? "true" : "false") << std::endl; + + // Test: colors constants + std::cout << "\n=== Color Constants Test ===" << std::endl; + std::cout << "palette_16[0] = 0x" << std::hex << colors::palette_16[0] << std::dec << std::endl; + std::cout << "palette_16[7] = 0x" << std::hex << colors::palette_16[7] << std::dec << std::endl; + + std::cout << "\nAll tests completed!" << std::endl; + return 0; +} \ No newline at end of file From a9012658450a846897ad992e467f3b800edff678 Mon Sep 17 00:00:00 2001 From: YuanSang <142496327+YuanSang0512@users.noreply.github.com> Date: Thu, 16 Apr 2026 22:17:27 +0800 Subject: [PATCH 4/5] Move the long function to the cpp file --- include/gkit/math/color.hpp | 189 +++++------------------------------- src/math/CMakeLists.txt | 1 + src/math/color.cpp | 171 ++++++++++++++++++++++++++++++++ test/math/test_color.cpp | 4 +- 4 files changed, 196 insertions(+), 169 deletions(-) create mode 100644 src/math/color.cpp diff --git a/include/gkit/math/color.hpp b/include/gkit/math/color.hpp index 53b867d..7711a16 100644 --- a/include/gkit/math/color.hpp +++ b/include/gkit/math/color.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include @@ -39,20 +38,18 @@ namespace gkit::math { inline constexpr auto light_gray() { return std::array{ 0.75f, 0.75f, 0.75f }; } // HTML color names - namespace html { - inline constexpr auto coral() { return std::array{ 1.0f, 0.5f, 0.31f }; } - inline constexpr auto tomato() { return std::array{ 1.0f, 0.39f, 0.28f }; } - inline constexpr auto gold() { return std::array{ 1.0f, 0.84f, 0.0f }; } - inline constexpr auto orange() { return std::array{ 1.0f, 0.65f, 0.0f }; } - inline constexpr auto purple() { return std::array{ 0.5f, 0.0f, 0.5f }; } - inline constexpr auto violet() { return std::array{ 0.93f, 0.51f, 0.93f }; } - inline constexpr auto pink() { return std::array{ 1.0f, 0.75f, 0.8f }; } - inline constexpr auto brown() { return std::array{ 0.65f, 0.16f, 0.16f }; } - inline constexpr auto olive() { return std::array{ 0.5f, 0.5f, 0.0f }; } - inline constexpr auto navy() { return std::array{ 0.0f, 0.0f, 0.5f }; } - inline constexpr auto teal() { return std::array{ 0.0f, 0.5f, 0.5f }; } - inline constexpr auto silver() { return std::array{ 0.75f, 0.75f, 0.75f }; } - } // namespace html + inline constexpr auto coral() { return std::array{ 1.0f, 0.5f, 0.31f }; } + inline constexpr auto tomato() { return std::array{ 1.0f, 0.39f, 0.28f }; } + inline constexpr auto gold() { return std::array{ 1.0f, 0.84f, 0.0f }; } + inline constexpr auto orange() { return std::array{ 1.0f, 0.65f, 0.0f }; } + inline constexpr auto purple() { return std::array{ 0.5f, 0.0f, 0.5f }; } + inline constexpr auto violet() { return std::array{ 0.93f, 0.51f, 0.93f }; } + inline constexpr auto pink() { return std::array{ 1.0f, 0.75f, 0.8f }; } + inline constexpr auto brown() { return std::array{ 0.65f, 0.16f, 0.16f }; } + inline constexpr auto olive() { return std::array{ 0.5f, 0.5f, 0.0f }; } + inline constexpr auto navy() { return std::array{ 0.0f, 0.0f, 0.5f }; } + inline constexpr auto teal() { return std::array{ 0.0f, 0.5f, 0.5f }; } + inline constexpr auto silver() { return std::array{ 0.75f, 0.75f, 0.75f }; } } // namespace colors @@ -83,55 +80,6 @@ namespace gkit::math { return static_cast(rgb & 0xFF); } - // Color blending - inline auto alpha_blend(uint32_t src, uint32_t dst) noexcept -> uint32_t { - auto sa = static_cast(src >> 24); - auto da = static_cast(dst >> 24); - - // Fully transparent source: return destination - if (sa == 0) return dst; - // Fully opaque source: return source - if (sa == 255) return src; - - // Alpha blending: result = src * alpha + dst * (1 - alpha) - float alpha = sa / 255.0f; - float inv_alpha = 1.0f - alpha; - - auto r = static_cast(((src >> 16) & 0xFF) * alpha + ((dst >> 16) & 0xFF) * inv_alpha); - auto g = static_cast(((src >> 8) & 0xFF) * alpha + ((dst >> 8) & 0xFF) * inv_alpha); - auto b = static_cast((src & 0xFF) * alpha + (dst & 0xFF) * inv_alpha); - auto a = static_cast(sa + da * inv_alpha); - - return (static_cast(a) << 24) | - (static_cast(r) << 16) | - (static_cast(g) << 8) | - static_cast(b); - } - - // Premultiplied alpha blending - inline auto premultiplied_blend(uint32_t src, uint32_t dst) noexcept -> uint32_t { - auto sa = static_cast(src >> 24); - auto da = static_cast(dst >> 24); - - // Fully transparent source: return destination - if (sa == 0) return dst; - // Fully opaque source: return source - if (sa == 255) return src; - - // Premultiplied alpha: src already multiplied by its alpha - float alpha = sa / 255.0f; - auto ra = static_cast(std::min(255, sa + da)); - - auto r = static_cast(((src >> 16) & 0xFF) + ((dst >> 16) & 0xFF) * (1.0f - alpha)); - auto g = static_cast(((src >> 8) & 0xFF) + ((dst >> 8) & 0xFF) * (1.0f - alpha)); - auto b = static_cast((src & 0xFF) + (dst & 0xFF) * (1.0f - alpha)); - - return (static_cast(ra) << 24) | - (static_cast(r) << 16) | - (static_cast(g) << 8) | - static_cast(b); - } - // Brightness adjustment inline auto brightness(uint32_t rgb, float factor) noexcept -> uint32_t { float r = ((rgb >> 16) & 0xFF) * factor; @@ -154,7 +102,7 @@ namespace gkit::math { // Grayscale conversion inline auto grayscale(uint32_t rgb) noexcept -> uint32_t { - uint8_t gray = static_cast(0.299f * ((rgb >> 16) & 0xFF) + + auto gray = static_cast(0.299f * ((rgb >> 16) & 0xFF) + 0.587f * ((rgb >> 8) & 0xFF) + 0.114f * (rgb & 0xFF)); return (gray << 16) | (gray << 8) | gray; @@ -189,58 +137,8 @@ namespace gkit::math { float v; // 0-1 Value }; - inline auto rgb_to_hsv(uint32_t rgb) noexcept -> HSV { - float r = ((rgb >> 16) & 0xFF) / 255.0f; - float g = ((rgb >> 8) & 0xFF) / 255.0f; - float b = (rgb & 0xFF) / 255.0f; - - float max_val = std::max(std::max(r, g), b); - float min_val = std::min(std::min(r, g), b); - float delta = max_val - min_val; - - // Value - float v = max_val; - - // Saturation - float s = (max_val > 0.0f) ? delta / max_val : 0.0f; - - // Hue - float h = 0.0f; - if (delta > 0.0f) { - if (max_val == r) h = 60.0f * std::fmod((g - b) / delta, 6.0f); - else if (max_val == g) h = 60.0f * ((b - r) / delta + 2.0f); - else h = 60.0f * ((r - g) / delta + 4.0f); - } - if (h < 0.0f) h += 360.0f; - - return { .h = h, .s = s, .v = v }; - } - - inline auto hsv_to_rgb(const HSV& hsv) noexcept -> uint32_t { - float h = hsv.h; - float s = hsv.s; - float v = hsv.v; - - float c = v * s; - float x = c * (1.0f - std::abs(std::fmod(h / 60.0f, 2.0f) - 1.0f)); - float m = v - c; - - float r, g, b; - if (h < 60.0f) { r = c; g = x; b = 0; } - else if (h < 120.0f) { r = x; g = c; b = 0; } - else if (h < 180.0f) { r = 0; g = c; b = x; } - else if (h < 240.0f) { r = 0; g = x; b = c; } - else if (h < 300.0f) { r = x; g = 0; b = c; } - else { r = c; g = 0; b = x; } - - auto R = static_cast((r + m) * 255.0f); - auto G = static_cast((g + m) * 255.0f); - auto B = static_cast((b + m) * 255.0f); - - return (static_cast(R) << 16) | - (static_cast(G) << 8) | - static_cast(B); - } + auto rgb_to_hsv(uint32_t rgb) noexcept -> HSV; + auto hsv_to_rgb(const HSV& hsv) noexcept -> uint32_t; // ------------------------------ @@ -252,58 +150,15 @@ namespace gkit::math { float l; // 0-1 Lightness }; - inline auto rgb_to_hsl(uint32_t rgb) noexcept -> HSL { - float r = ((rgb >> 16) & 0xFF) / 255.0f; - float g = ((rgb >> 8) & 0xFF) / 255.0f; - float b = (rgb & 0xFF) / 255.0f; - - float max_val = std::max(std::max(r, g), b); - float min_val = std::min(std::min(r, g), b); - float delta = max_val - min_val; - - float l = (max_val + min_val) / 2.0f; + auto rgb_to_hsl(uint32_t rgb) noexcept -> HSL; + auto hsl_to_rgb(const HSL& hsl) noexcept -> uint32_t; - float s = 0.0f; - if (delta > 0.0f) { - s = l > 0.5f ? delta / (2.0f - max_val - min_val) : delta / (max_val + min_val); - } - float h = 0.0f; - if (delta > 0.0f) { - if (max_val == r) h = 60.0f * std::fmod((g - b) / delta, 6.0f); - else if (max_val == g) h = 60.0f * ((b - r) / delta + 2.0f); - else h = 60.0f * ((r - g) / delta + 4.0f); - } - if (h < 0.0f) h += 360.0f; - - return { .h=h, .s=s, .l=l }; - } - - inline auto hsl_to_rgb(const HSL& hsl) noexcept -> uint32_t { - float h = hsl.h; - float s = hsl.s; - float l = hsl.l; - - float c = (1.0f - std::abs(2.0f * l - 1.0f)) * s; - float x = c * (1.0f - std::abs(std::fmod(h / 60.0f, 2.0f) - 1.0f)); - float m = l - c / 2.0f; - - float r, g, b; - if (h < 60.0f) { r = c; g = x; b = 0; } - else if (h < 120.0f) { r = x; g = c; b = 0; } - else if (h < 180.0f) { r = 0; g = c; b = x; } - else if (h < 240.0f) { r = 0; g = x; b = c; } - else if (h < 300.0f) { r = x; g = 0; b = c; } - else { r = c; g = 0; b = x; } - - auto R = static_cast((r + m) * 255.0f); - auto G = static_cast((g + m) * 255.0f); - auto B = static_cast((b + m) * 255.0f); - - return (static_cast(R) << 16) | - (static_cast(G) << 8) | - static_cast(B); - } + // ------------------------------ + // Color Blending + // ------------------------------ + auto alpha_blend(uint32_t src, uint32_t dst) noexcept -> uint32_t; + auto premultiplied_blend(uint32_t src, uint32_t dst) noexcept -> uint32_t; // ------------------------------ diff --git a/src/math/CMakeLists.txt b/src/math/CMakeLists.txt index 242f81e..3899316 100644 --- a/src/math/CMakeLists.txt +++ b/src/math/CMakeLists.txt @@ -4,6 +4,7 @@ set (MATH_SRC "./vector2.cpp" "./vector3.cpp" "./vector4.cpp" + "./color.cpp" ) add_library(${GKIT_MATH} OBJECT ${MATH_SRC}) diff --git a/src/math/color.cpp b/src/math/color.cpp new file mode 100644 index 0000000..3a369f3 --- /dev/null +++ b/src/math/color.cpp @@ -0,0 +1,171 @@ +#include "gkit/math/color.hpp" + +#include + +namespace gkit::math { + +// ------------------------------ +// HSV <-> RGB +// ------------------------------ + auto rgb_to_hsv(uint32_t rgb) noexcept -> HSV { + float r = ((rgb >> 16) & 0xFF) / 255.0f; + float g = ((rgb >> 8) & 0xFF) / 255.0f; + float b = (rgb & 0xFF) / 255.0f; + + float max_val = std::max(std::max(r, g), b); + float min_val = std::min(std::min(r, g), b); + float delta = max_val - min_val; + + // Value + float v = max_val; + + // Saturation + float s = (max_val > 0.0f) ? delta / max_val : 0.0f; + + // Hue + float h = 0.0f; + if (delta > 0.0f) { + if (max_val == r) h = 60.0f * std::fmod((g - b) / delta, 6.0f); + else if (max_val == g) h = 60.0f * ((b - r) / delta + 2.0f); + else h = 60.0f * ((r - g) / delta + 4.0f); + } + if (h < 0.0f) h += 360.0f; + + return { .h = h, .s = s, .v = v }; + } + + auto hsv_to_rgb(const HSV& hsv) noexcept -> uint32_t { + float h = hsv.h; + float s = hsv.s; + float v = hsv.v; + + float c = v * s; + float x = c * (1.0f - std::abs(std::fmod(h / 60.0f, 2.0f) - 1.0f)); + float m = v - c; + + float r, g, b; + if (h < 60.0f) { r = c; g = x; b = 0; } + else if (h < 120.0f) { r = x; g = c; b = 0; } + else if (h < 180.0f) { r = 0; g = c; b = x; } + else if (h < 240.0f) { r = 0; g = x; b = c; } + else if (h < 300.0f) { r = x; g = 0; b = c; } + else { r = c; g = 0; b = x; } + + uint8_t R = static_cast((r + m) * 255.0f); + uint8_t G = static_cast((g + m) * 255.0f); + uint8_t B = static_cast((b + m) * 255.0f); + + return (static_cast(R) << 16) | + (static_cast(G) << 8) | + static_cast(B); + } + + +// ------------------------------ +// HSL <-> RGB +// ------------------------------ + auto rgb_to_hsl(uint32_t rgb) noexcept -> HSL { + float r = ((rgb >> 16) & 0xFF) / 255.0f; + float g = ((rgb >> 8) & 0xFF) / 255.0f; + float b = (rgb & 0xFF) / 255.0f; + + float max_val = std::max(std::max(r, g), b); + float min_val = std::min(std::min(r, g), b); + float delta = max_val - min_val; + + float l = (max_val + min_val) / 2.0f; + + float s = 0.0f; + if (delta > 0.0f) { + s = l > 0.5f ? delta / (2.0f - max_val - min_val) : delta / (max_val + min_val); + } + + float h = 0.0f; + if (delta > 0.0f) { + if (max_val == r) h = 60.0f * std::fmod((g - b) / delta, 6.0f); + else if (max_val == g) h = 60.0f * ((b - r) / delta + 2.0f); + else h = 60.0f * ((r - g) / delta + 4.0f); + } + if (h < 0.0f) h += 360.0f; + + return { .h = h, .s = s, .l = l }; + } + + auto hsl_to_rgb(const HSL& hsl) noexcept -> uint32_t { + float h = hsl.h; + float s = hsl.s; + float l = hsl.l; + + float c = (1.0f - std::abs(2.0f * l - 1.0f)) * s; + float x = c * (1.0f - std::abs(std::fmod(h / 60.0f, 2.0f) - 1.0f)); + float m = l - c / 2.0f; + + float r, g, b; + if (h < 60.0f) { r = c; g = x; b = 0; } + else if (h < 120.0f) { r = x; g = c; b = 0; } + else if (h < 180.0f) { r = 0; g = c; b = x; } + else if (h < 240.0f) { r = 0; g = x; b = c; } + else if (h < 300.0f) { r = x; g = 0; b = c; } + else { r = c; g = 0; b = x; } + + uint8_t R = static_cast((r + m) * 255.0f); + uint8_t G = static_cast((g + m) * 255.0f); + uint8_t B = static_cast((b + m) * 255.0f); + + return (static_cast(R) << 16) | + (static_cast(G) << 8) | + static_cast(B); + } + + +// ------------------------------ +// Alpha Blending +// ------------------------------ + auto alpha_blend(uint32_t src, uint32_t dst) noexcept -> uint32_t { + auto sa = static_cast(src >> 24); + auto da = static_cast(dst >> 24); + + // Fully transparent source: return destination + if (sa == 0) return dst; + // Fully opaque source: return source + if (sa == 255) return src; + + // Alpha blending: result = src * alpha + dst * (1 - alpha) + float alpha = sa / 255.0f; + float inv_alpha = 1.0f - alpha; + + auto r = static_cast(((src >> 16) & 0xFF) * alpha + ((dst >> 16) & 0xFF) * inv_alpha); + auto g = static_cast(((src >> 8) & 0xFF) * alpha + ((dst >> 8) & 0xFF) * inv_alpha); + auto b = static_cast((src & 0xFF) * alpha + (dst & 0xFF) * inv_alpha); + auto a = static_cast(sa + da * inv_alpha); + + return (static_cast(a) << 24) | + (static_cast(r) << 16) | + (static_cast(g) << 8) | + static_cast(b); + } + + auto premultiplied_blend(uint32_t src, uint32_t dst) noexcept -> uint32_t { + auto sa = static_cast(src >> 24); + auto da = static_cast(dst >> 24); + + // Fully transparent source: return destination + if (sa == 0) return dst; + // Fully opaque source: return source + if (sa == 255) return src; + + // Premultiplied alpha: src already multiplied by its alpha + float alpha = sa / 255.0f; + auto ra = static_cast(std::min(255, sa + da)); + + auto r = static_cast(((src >> 16) & 0xFF) + ((dst >> 16) & 0xFF) * (1.0f - alpha)); + auto g = static_cast(((src >> 8) & 0xFF) + ((dst >> 8) & 0xFF) * (1.0f - alpha)); + auto b = static_cast((src & 0xFF) + (dst & 0xFF) * (1.0f - alpha)); + + return (static_cast(ra) << 24) | + (static_cast(r) << 16) | + (static_cast(g) << 8) | + static_cast(b); + } + +} // namespace gkit::math \ No newline at end of file diff --git a/test/math/test_color.cpp b/test/math/test_color.cpp index 85681c1..2ddf4eb 100644 --- a/test/math/test_color.cpp +++ b/test/math/test_color.cpp @@ -81,8 +81,8 @@ auto main() -> int { // Test: colors constants std::cout << "\n=== Color Constants Test ===" << std::endl; - std::cout << "palette_16[0] = 0x" << std::hex << colors::palette_16[0] << std::dec << std::endl; - std::cout << "palette_16[7] = 0x" << std::hex << colors::palette_16[7] << std::dec << std::endl; + std::cout << "palette_8[0] = 0x" << std::hex << colors::palette_8[0] << std::dec << std::endl; + std::cout << "palette_8[7] = 0x" << std::hex << colors::palette_8[7] << std::dec << std::endl; std::cout << "\nAll tests completed!" << std::endl; return 0; From 92c31c2c9325586bd95e01605dac21efc9212dbf Mon Sep 17 00:00:00 2001 From: YuanSang <142496327+YuanSang0512@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:12:37 +0800 Subject: [PATCH 5/5] fix: test_vector3 --- test/math/test_vector3.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/math/test_vector3.cpp b/test/math/test_vector3.cpp index ecc6287..2fada07 100644 --- a/test/math/test_vector3.cpp +++ b/test/math/test_vector3.cpp @@ -36,7 +36,7 @@ auto main() -> int { // other methods std::cout << "vec1.length(): " << vec1.length() << std::endl; - vec1 = vec1.normalize(vec1); - std::cout << "vec1.normalization(): " << vec_str(vec1) << std::endl; - std::cout << "vec1.length() after normalization: " << vec1.length() << std::endl; + vec1 = gkit::math::Vector3::normalize(vec1); + std::cout << "normalize(vec1): " << vec_str(vec1) << std::endl; + std::cout << "vec1.length() after normalize: " << vec1.length() << std::endl; } \ No newline at end of file