From 552e16222cf22703d4ad8c31ca87630c92dfdb50 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 24 Oct 2018 18:38:36 +0700 Subject: [PATCH] math rect class --- headers/enduro2d/math/_all.hpp | 1 + headers/enduro2d/math/_math.hpp | 10 ++ headers/enduro2d/math/rect.hpp | 212 ++++++++++++++++++++++++++ untests/sources/untests_math/rect.cpp | 109 +++++++++++++ 4 files changed, 332 insertions(+) create mode 100644 headers/enduro2d/math/rect.hpp create mode 100644 untests/sources/untests_math/rect.cpp diff --git a/headers/enduro2d/math/_all.hpp b/headers/enduro2d/math/_all.hpp index 999349c0..46b3da52 100644 --- a/headers/enduro2d/math/_all.hpp +++ b/headers/enduro2d/math/_all.hpp @@ -11,6 +11,7 @@ #include "mat2.hpp" #include "mat3.hpp" #include "mat4.hpp" +#include "rect.hpp" #include "trig.hpp" #include "unit.hpp" #include "vec2.hpp" diff --git a/headers/enduro2d/math/_math.hpp b/headers/enduro2d/math/_math.hpp index 4759ecce..8ba7987c 100644 --- a/headers/enduro2d/math/_math.hpp +++ b/headers/enduro2d/math/_math.hpp @@ -28,6 +28,9 @@ namespace e2d template < typename T > class mat4; + template < typename T > + class rect; + template < typename T, typename Tag > class unit; @@ -79,6 +82,13 @@ namespace e2d using m4hi = mat4; using m4hu = mat4; + using r4d = rect; + using r4f = rect; + using r4i = rect; + using r4u = rect; + using r4hi = rect; + using r4hu = rect; + struct deg_tag {}; struct rad_tag {}; diff --git a/headers/enduro2d/math/rect.hpp b/headers/enduro2d/math/rect.hpp new file mode 100644 index 00000000..13cf3c68 --- /dev/null +++ b/headers/enduro2d/math/rect.hpp @@ -0,0 +1,212 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018 Matvey Cherevko + ******************************************************************************/ + +#pragma once + +#include "_math.hpp" +#include "vec2.hpp" + +namespace e2d +{ + template < typename T > + class rect final { + static_assert( + std::is_arithmetic::value, + "type of 'rect' must be arithmetic"); + public: + using self_type = rect; + using value_type = T; + public: + vec2 position; + vec2 size; + public: + rect() noexcept = default; + rect(const rect& other) noexcept = default; + rect& operator=(const rect& other) noexcept = default; + + rect(T w, T h) noexcept; + rect(T x, T y, T w, T h) noexcept; + + rect(const vec2& nsize) noexcept; + rect(const vec2& nposition, const vec2& nsize) noexcept; + + template < typename To > + rect cast_to() const noexcept; + }; +} + +namespace e2d +{ + template < typename T > + rect::rect(T w, T h) noexcept + : size(w, h) {} + + template < typename T > + rect::rect(T x, T y, T w, T h) noexcept + : position(x, y) + , size(w, h) {} + + template < typename T > + rect::rect(const vec2& nsize) noexcept + : size(nsize) {} + + template < typename T > + rect::rect(const vec2& nposition, const vec2& nsize) noexcept + : position(nposition) + , size(nsize) {} + + template < typename T > + template < typename To > + rect rect::cast_to() const noexcept { + return { + position.template cast_to(), + size.template cast_to()}; + } +} + +namespace e2d +{ + // + // make_rect + // + + template < typename T > + rect make_rect(T w, T h) noexcept { + return {w, h}; + } + + template < typename T > + rect make_rect(T x, T y, T w, T h) noexcept { + return {x, y, w, h}; + } + + template < typename T > + rect make_rect(const vec2& size) noexcept { + return {size}; + } + + template < typename T > + rect make_rect(const vec2& position, const vec2& size) noexcept { + return {position, size}; + } + + // + // rect (==,!=) rect + // + + template < typename T > + bool operator==(const rect& l, const rect& r) noexcept { + return l.position == r.position + && l.size == r.size; + } + + template < typename T > + bool operator!=(const rect& l, const rect& r) noexcept { + return !(l == r); + } + + // + // rect (<,>,<=,>=) rect + // + + template < typename T > + bool operator<(const rect& l, const rect& r) noexcept { + return l.size.x * l.size.y < r.size.x * r.size.y; + } + + template < typename T > + bool operator>(const rect& l, const rect& r) noexcept { + return r < l; + } + + template < typename T > + bool operator<=(const rect& l, const rect& r) noexcept { + return !(r < l); + } + + template < typename T > + bool operator>=(const rect& l, const rect& r) noexcept { + return !(l < r); + } +} + +namespace e2d { namespace math +{ + // + // approximately + // + + template < typename T > + bool approximately( + const rect& l, + const rect& r, + T precision = math::default_precision()) noexcept + { + return math::approximately(l.position, r.position, precision) + && math::approximately(l.size, r.size, precision); + } + + // + // minimum/maximum + // + + template < typename T > + vec2 minimum(const rect& r) noexcept { + return math::minimized(r.position, r.position + r.size); + } + + template < typename T > + vec2 maximum(const rect& r) noexcept { + return math::maximized(r.position, r.position + r.size); + } + + // + // area + // + + template < typename T > + T area(const rect& r) noexcept { + return r.size.x * r.size.y; + } + + template < typename T > + T abs_area(const rect& r) noexcept { + return math::abs(r.size.x * r.size.y); + } + + // + // merged + // + + template < typename T > + rect merged(const rect& l, const rect& r) noexcept { + const vec2 min = math::minimized(minimum(l), minimum(r)); + const vec2 max = math::maximized(maximum(l), maximum(r)); + return { min, max - min }; + } + + // + // inside + // + + template < typename T > + bool inside(const rect& r, const vec2& p) noexcept { + const vec2 min = minimum(r); + const vec2 max = maximum(r); + return p.x >= min.x && p.x <= max.x + && p.y >= min.y && p.y <= max.y; + } + + // + // contains_nan + // + + template < typename T > + bool contains_nan(const rect& r) noexcept { + return math::contains_nan(r.position) + || math::contains_nan(r.size); + } +}} diff --git a/untests/sources/untests_math/rect.cpp b/untests/sources/untests_math/rect.cpp new file mode 100644 index 00000000..efa656cc --- /dev/null +++ b/untests/sources/untests_math/rect.cpp @@ -0,0 +1,109 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018 Matvey Cherevko + ******************************************************************************/ + +#include "_math.hpp" +using namespace e2d; + +TEST_CASE("rect") { + { + REQUIRE(r4i().position == v2i(0,0)); + REQUIRE(r4i().size == v2i(0,0)); + + REQUIRE(r4i(1,2).position == v2i(0,0)); + REQUIRE(r4i(1,2).size == v2i(1,2)); + + REQUIRE(r4i(1,2,3,4).position == v2i(1,2)); + REQUIRE(r4i(1,2,3,4).size == v2i(3,4)); + + REQUIRE(r4i(v2i{1,2}).position == v2i(0,0)); + REQUIRE(r4i(v2i{1,2}).size == v2i(1,2)); + + REQUIRE(r4i(v2i{1,2},v2i{3,4}).position == v2i(1,2)); + REQUIRE(r4i(v2i{1,2},v2i{3,4}).size == v2i(3,4)); + } + { + REQUIRE(r4f(1,2,3,4).cast_to() == r4i(1,2,3,4)); + } + { + REQUIRE(make_rect(2,1) == r4i(0,0,2,1)); + REQUIRE(make_rect(4,3,2,1) == r4i(4,3,2,1)); + REQUIRE(make_rect(v2i{2,1}) == r4i(0,0,2,1)); + REQUIRE(make_rect(v2i{4,3},v2i{2,1}) == r4i(4,3,2,1)); + } + { + auto r0 = r4i(1,2,3,4); + auto r1 = r4i(r0); + REQUIRE(r1 == r4i(1,2,3,4)); + r1 = r4i(4,3,2,1); + REQUIRE(r1 == r4i(4,3,2,1)); + } + { + REQUIRE(r4i(1,2,3,4) == r4i(1,2,3,4)); + REQUIRE_FALSE(r4i(1,2,3,4) == r4i(1,2,4,3)); + REQUIRE_FALSE(r4i(1,2,3,4) == r4i(2,1,3,4)); + REQUIRE_FALSE(r4i(1,2,3,4) == r4i(2,1,4,3)); + + REQUIRE_FALSE(r4i(1,2,3,4) != r4i(1,2,3,4)); + REQUIRE(r4i(1,2,3,4) != r4i(1,2,4,3)); + REQUIRE(r4i(1,2,3,4) != r4i(2,1,3,4)); + REQUIRE(r4i(1,2,3,4) != r4i(2,1,4,3)); + } + { + REQUIRE(r4i(4,4) < r4i(4,5)); + REQUIRE_FALSE(r4i(4,4) < r4i(3,4)); + REQUIRE_FALSE(r4i(4,4) < r4i(4,3)); + } + { + REQUIRE(math::approximately(r4i(1,2,3,4), r4i(1,2,4,5), 1)); + REQUIRE_FALSE(math::approximately(r4i(1,2,3,4), r4i(1,2,4,5))); + } + { + REQUIRE(math::minimum(r4i(1,2,3,4)) == v2i(1,2)); + REQUIRE(math::minimum(r4i(1,2,-3,5)) == v2i(-2,2)); + REQUIRE(math::minimum(r4i(1,2,3,-5)) == v2i(1,-3)); + REQUIRE(math::minimum(r4i(1,2,-3,-5)) == v2i(-2,-3)); + + REQUIRE(math::maximum(r4i(1,2,3,4)) == v2i(4,6)); + REQUIRE(math::maximum(r4i(1,2,-3,5)) == v2i(1,7)); + REQUIRE(math::maximum(r4i(1,2,3,-5)) == v2i(4,2)); + REQUIRE(math::maximum(r4i(1,2,-3,-5)) == v2i(1,2)); + + REQUIRE(math::area(r4i(1,2,3,4)) == 12); + REQUIRE(math::area(r4i(1,2,3,-4)) == -12); + REQUIRE(math::area(r4i(1,2,-3,4)) == -12); + REQUIRE(math::area(r4i(1,2,-3,-4)) == 12); + + REQUIRE(math::abs_area(r4i(1,2,3,4)) == 12); + REQUIRE(math::abs_area(r4i(1,2,3,-4)) == 12); + REQUIRE(math::abs_area(r4i(1,2,-3,4)) == 12); + REQUIRE(math::abs_area(r4i(1,2,-3,-4)) == 12); + + REQUIRE(math::merged(r4i(1,2,3,4), r4i(1,2,3,4)) == r4i(1,2,3,4)); + REQUIRE(math::merged(r4i(1,2,3,4), r4i(0,1,3,4)) == r4i(0,1,4,5)); + REQUIRE(math::merged(r4i(1,2,3,4), r4i(1,2,4,5)) == r4i(1,2,4,5)); + REQUIRE(math::merged(r4i(1,2,3,4), r4i(0,1,4,5)) == r4i(0,1,4,5)); + + REQUIRE(math::merged(r4i(1,2,3,4), r4i(1,2,-3,-5)) == r4i(-2,-3,6,9)); + REQUIRE(math::merged(r4i(-1,-2,3,4), r4i(1,2,-3,-5)) == r4i(-2,-3,4,5)); + + REQUIRE(math::inside(r4i(1,2,3,4), v2i(1,2))); + REQUIRE(math::inside(r4i(1,2,3,4), v2i(4,5))); + REQUIRE_FALSE(math::inside(r4i(1,2,3,4), v2i(4,7))); + REQUIRE_FALSE(math::inside(r4i(1,2,3,4), v2i(0,5))); + + REQUIRE(math::inside(r4i(1,2,-3,-4), v2i(1,2))); + REQUIRE(math::inside(r4i(1,2,-3,-4), v2i(-1,-2))); + REQUIRE_FALSE(math::inside(r4i(1,2,-3,-4), v2i(2,2))); + REQUIRE_FALSE(math::inside(r4i(1,2,-3,-4), v2i(1,3))); + REQUIRE_FALSE(math::inside(r4i(1,2,-3,-4), v2i(-3,2))); + REQUIRE_FALSE(math::inside(r4i(1,2,-3,-4), v2i(1,-3))); + + REQUIRE_FALSE(math::contains_nan(r4i(1,2,3,4))); + REQUIRE_FALSE(math::contains_nan(r4f(1.f,2.f,3.f,4.f))); + REQUIRE(math::contains_nan(r4f(1.f,std::numeric_limits::quiet_NaN()))); + REQUIRE(math::contains_nan(r4f(std::numeric_limits::infinity(), 1.f))); + } +}