From 562d71dbe677c3dbd6427982895a62ce67dc9758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 7 Jun 2015 18:27:40 +0200 Subject: [PATCH] Math: ability to convert ranges from/to external representation. --- src/Magnum/Math/Range.h | 16 ++++ src/Magnum/Math/Test/RangeTest.cpp | 138 ++++++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Math/Range.h b/src/Magnum/Math/Range.h index 36804ebbbb..d754e88079 100644 --- a/src/Magnum/Math/Range.h +++ b/src/Magnum/Math/Range.h @@ -39,6 +39,8 @@ namespace Implementation { template struct RangeTraits<1, T> { typedef Vector<1, T> Type; }; template struct RangeTraits<2, T> { typedef Vector2 Type; }; template struct RangeTraits<3, T> { typedef Vector3 Type; }; + + template struct RangeConverter; } /** @@ -93,6 +95,14 @@ template class Range { */ template constexpr explicit Range(const Range& other): _min(other._min), _max(other._max) {} + /** @brief Construct range from external representation */ + template::from(std::declval()))> constexpr explicit Range(const U& other): Range{Implementation::RangeConverter::from(other)} {} + + /** @brief Convert range to external representation */ + template::to(std::declval>()))> constexpr explicit operator U() const { + return Implementation::RangeConverter::to(*this); + } + /** @brief Equality comparison */ constexpr bool operator==(const Range& other) const { return _min == other._min && _max == other._max; @@ -220,6 +230,9 @@ template class Range2D: public Range<2, T> { /** @copydoc Range(const Range&) */ template constexpr explicit Range2D(const Range2D& other): Range<2, T>(other) {} + /** @brief Construct range from external representation */ + template::from(std::declval()))> constexpr explicit Range2D(const U& other): Range<2, T>{Implementation::RangeConverter<2, T, U>::from(other)} {} + /** * @brief Bottom left corner * @@ -321,6 +334,9 @@ template class Range3D: public Range<3, T> { /** @copydoc Range(const Range&) */ template constexpr explicit Range3D(const Range3D& other): Range<3, T>(other) {} + /** @brief Construct range from external representation */ + template::from(std::declval()))> constexpr explicit Range3D(const U& other): Range<3, T>{Implementation::RangeConverter<3, T, U>::from(other)} {} + /** * @brief Back bottom left corner * diff --git a/src/Magnum/Math/Test/RangeTest.cpp b/src/Magnum/Math/Test/RangeTest.cpp index dd687ed9a5..5372477997 100644 --- a/src/Magnum/Math/Test/RangeTest.cpp +++ b/src/Magnum/Math/Test/RangeTest.cpp @@ -29,7 +29,79 @@ #include "Magnum/Math/Range.h" -namespace Magnum { namespace Math { namespace Test { +struct Dim { + float offset, size; +}; + +struct Rect { + float x, y, w, h; +}; + +struct Box { + float x, y, z, w, h, d; +}; + +namespace Magnum { namespace Math { + +namespace Implementation { + +template<> struct RangeConverter<1, Float, Dim> { + #if !defined(__GNUC__) || defined(__clang__) + constexpr /* See the convert() test case */ + #endif + static Range<1, Float> from(const Dim& other) { + /* Doing it this way to preserve constexpr */ + return {other.offset, other.offset + other.size}; + } + + constexpr static Dim to(const Range<1, Float>& other) { + return {other.min()[0], + /* Doing it this way to preserve constexpr */ + other.max()[0] - other.min()[0]}; + } +}; + +template<> struct RangeConverter<2, Float, Rect> { + #if !defined(__GNUC__) || defined(__clang__) + constexpr /* See the convert() test case */ + #endif + static Range<2, Float> from(const Rect& other) { + /* Doing it this way to preserve constexpr */ + return {{other.x, other.y}, {other.x + other.w, other.y + other.h}}; + } + + constexpr static Rect to(const Range<2, Float>& other) { + return {other.min().x(), other.min().y(), + /* Doing it this way to preserve constexpr */ + other.max().x() - other.min().x(), + other.max().y() - other.min().y()}; + } +}; + +template<> struct RangeConverter<3, Float, Box> { + #if !defined(__GNUC__) || defined(__clang__) + constexpr /* See the convert() test case */ + #endif + static Range<3, Float> from(const Box& other) { + return {{other.x, other.y, other.z}, + /* Doing it this way to preserve constexpr */ + {other.x + other.w, + other.y + other.h, + other.z + other.d}}; + } + + constexpr static Box to(const Range<3, Float>& other) { + return {other.min().x(), other.min().y(), other.min().z(), + /* Doing it this way to preserve constexpr */ + other.max().x() - other.min().x(), + other.max().y() - other.min().y(), + other.max().z() - other.min().z()}; + } +}; + +} + +namespace Test { struct RangeTest: Corrade::TestSuite::Tester { explicit RangeTest(); @@ -39,6 +111,7 @@ struct RangeTest: Corrade::TestSuite::Tester { void constructFromSize(); void constructConversion(); void constructCopy(); + void convert(); void access(); void compare(); @@ -71,6 +144,7 @@ RangeTest::RangeTest() { &RangeTest::constructFromSize, &RangeTest::constructConversion, &RangeTest::constructCopy, + &RangeTest::convert, &RangeTest::access, &RangeTest::compare, @@ -149,6 +223,68 @@ void RangeTest::constructCopy() { CORRADE_COMPARE(f, Range3Di({3, 5, -7}, {23, 78, 2})); } +void RangeTest::convert() { + /* It's position/size, not min/max */ + constexpr Dim a{1.5f, 3.5f}; + constexpr Rect b{1.5f, -2.0f, 3.5f, 0.5f}; + constexpr Box c{1.5f, -2.0f, -0.5f, 3.5f, 0.5f, 9.5f}; + constexpr Range1D d{1.5f, 5.0f}; + constexpr Range2D e{{1.5f, -2.0f}, {5.0f, -1.5f}}; + constexpr Range3D f{{1.5f, -2.0f, -0.5f}, {5.0f, -1.5f, 9.0f}}; + + /* GCC 5.1 fills the result with zeros instead of properly calling + delegated copy constructor if using constexpr. Reported here: + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66450 */ + #if !defined(__GNUC__) || defined(__clang__) + constexpr + #endif + Range<2, Float> g{b}; + #if !defined(__GNUC__) || defined(__clang__) + constexpr + #endif + Range1D h{a}; + #if !defined(__GNUC__) || defined(__clang__) + constexpr + #endif + Range2D i{b}; + #if !defined(__GNUC__) || defined(__clang__) + constexpr + #endif + Range3D j{c}; + CORRADE_COMPARE(g, e); + CORRADE_COMPARE(h, d); + CORRADE_COMPARE(i, e); + CORRADE_COMPARE(j, f); + + constexpr Dim k(d); + CORRADE_COMPARE(k.offset, a.offset); + CORRADE_COMPARE(k.size, a.size); + + constexpr Rect l(e); + CORRADE_COMPARE(l.x, b.x); + CORRADE_COMPARE(l.y, b.y); + CORRADE_COMPARE(l.w, b.w); + CORRADE_COMPARE(l.h, b.h); + + constexpr Box m(f); + CORRADE_COMPARE(m.x, c.x); + CORRADE_COMPARE(m.y, c.y); + CORRADE_COMPARE(m.z, c.z); + CORRADE_COMPARE(m.w, c.w); + CORRADE_COMPARE(m.h, c.h); + CORRADE_COMPARE(m.d, c.d); + + /* Implicit conversion is not allowed */ + CORRADE_VERIFY(!(std::is_convertible>::value)); + CORRADE_VERIFY(!(std::is_convertible::value)); + CORRADE_VERIFY(!(std::is_convertible::value)); + CORRADE_VERIFY(!(std::is_convertible::value)); + + CORRADE_VERIFY(!(std::is_convertible::value)); + CORRADE_VERIFY(!(std::is_convertible::value)); + CORRADE_VERIFY(!(std::is_convertible::value)); +} + void RangeTest::access() { Range1Di line(34, 47); Range2Di rect({34, 23}, {47, 30});