From 17b3c8fac5264252fc0e209015aaf719f0a287ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 5 Sep 2019 14:15:06 +0200 Subject: [PATCH] Math: support numeric indexing in gather() / scatter() as well. --- doc/changelog.dox | 3 +- src/Magnum/Math/Swizzle.h | 86 +++++++++++++++------------- src/Magnum/Math/Test/SwizzleTest.cpp | 26 ++++++++- src/Magnum/Math/Vector.h | 8 +-- 4 files changed, 75 insertions(+), 48 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 207a9db66..ab267acb2 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -210,7 +210,8 @@ See also: - Scalar and vector @ref Math::equal() and @ref Math::notEqual() which do a component-wise comparison in the vector case, as the equality/non-equality operators return a single @cpp bool @ce -- @ref Math::scatter() as an inverse operation to @ref math::gather() +- @ref Math::scatter() as an inverse operation to @ref math::gather(), both + functions now support addressing via component indices as well @subsubsection changelog-latest-new-meshtools MeshTools library diff --git a/src/Magnum/Math/Swizzle.h b/src/Magnum/Math/Swizzle.h index 976a1913f..db31a02ae 100644 --- a/src/Magnum/Math/Swizzle.h +++ b/src/Magnum/Math/Swizzle.h @@ -34,27 +34,27 @@ namespace Magnum { namespace Math { namespace Implementation { - template struct ComponentAtPosition { - static_assert(size > position, "swizzle parameter out of range of gather vector"); + template struct GatherComponentAt { + static_assert(size > position, "numeric swizzle parameter out of range of gather vector, use either xyzw/rgba/0/1 letters or small enough numbers"); template constexpr static T value(const Math::Vector& vector) { return vector._data[position]; } }; - template struct Component {}; - template struct Component: public ComponentAtPosition {}; - template struct Component: public ComponentAtPosition {}; - template struct Component: public ComponentAtPosition {}; - template struct Component: public ComponentAtPosition {}; - template struct Component: public ComponentAtPosition {}; - template struct Component: public ComponentAtPosition {}; - template struct Component: public ComponentAtPosition {}; - template struct Component: public ComponentAtPosition {}; - template struct Component { + template struct GatherComponent: GatherComponentAt {}; + template struct GatherComponent: public GatherComponentAt {}; + template struct GatherComponent: public GatherComponentAt {}; + template struct GatherComponent: public GatherComponentAt {}; + template struct GatherComponent: public GatherComponentAt {}; + template struct GatherComponent: public GatherComponentAt {}; + template struct GatherComponent: public GatherComponentAt {}; + template struct GatherComponent: public GatherComponentAt {}; + template struct GatherComponent: public GatherComponentAt {}; + template struct GatherComponent { template constexpr static T value(const Math::Vector&) { return T(0); } }; - template struct Component { + template struct GatherComponent { template constexpr static T value(const Math::Vector&) { return T(1); } }; @@ -62,40 +62,42 @@ namespace Implementation { typedef Math::Vector Type; }; - template struct ComponentOr { - static_assert(component == 'x' || component == 'r' || - ((component == 'y' || component == 'g') && size > 1) || - ((component == 'z' || component == 'b') && size > 2) || - ((component == 'w' || component == 'a') && size > 3), - "swizzle parameter out of range of scatter vector"); - + template struct ScatterComponentOr { + template constexpr static T value(const Math::Vector&, const T& value) { + return value; + } + }; + template struct ScatterComponentOr { template constexpr static T value(const Math::Vector& vector, const T&) { return vector._data[i]; } }; - template struct Value { - template constexpr static T value(const Math::Vector&, const T& value) { - return value; - } + template struct ScatterComponent: ScatterComponentOr { + static_assert(component == 'x' || component == 'r' || + ((component == 'y' || component == 'g') && size > 1) || + ((component == 'z' || component == 'b') && size > 2) || + ((component == 'w' || component == 'a') && size > 3) || + std::size_t(component) < size, + "swizzle parameter out of range of scatter vector, use either xyzw/rgba letters or small enough numbers"); }; - template struct ComponentOr: Value {}; - template struct ComponentOr: Value {}; - template struct ComponentOr: Value {}; - template struct ComponentOr: Value {}; - template struct ComponentOr: Value {}; - template struct ComponentOr: Value {}; - template struct ComponentOr: Value {}; - template struct ComponentOr: Value {}; - - template constexpr T componentOr(const T& vector, const typename T::Type& value, Sequence) { - return {ComponentOr::value(vector, value)...}; + template struct ScatterComponent: ScatterComponentOr {}; + template struct ScatterComponent: ScatterComponentOr {}; + template struct ScatterComponent: ScatterComponentOr {}; + template struct ScatterComponent: ScatterComponentOr {}; + template struct ScatterComponent: ScatterComponentOr {}; + template struct ScatterComponent: ScatterComponentOr {}; + template struct ScatterComponent: ScatterComponentOr {}; + template struct ScatterComponent: ScatterComponentOr {}; + + template constexpr T scatterComponentOr(const T& vector, const typename T::Type& value, Sequence) { + return {ScatterComponent::value(vector, value)...}; } - template constexpr T scatterRecursive(const T& vector, const Math::Vector&, std::size_t) { + template constexpr T scatterRecursive(const T& vector, const Vector&, std::size_t) { return vector; } - template constexpr T scatterRecursive(const T& vector, const Math::Vector& values, std::size_t valueIndex) { + template constexpr T scatterRecursive(const T& vector, const Vector& values, std::size_t valueIndex) { return scatterRecursive( - componentOr(vector, values[valueIndex], typename GenerateSequence::Type{}), + scatterComponentOr(vector, values[valueIndex], typename GenerateSequence::Type{}), values, valueIndex + 1); } } @@ -109,8 +111,10 @@ Creates a new vector from given components. Example: You can use letters @cpp 'x' @ce, @cpp 'y' @ce, @cpp 'z' @ce, @cpp 'w' @ce and @cpp 'r' @ce, @cpp 'g' @ce, @cpp 'b' @ce, @cpp 'a' @ce for addressing -components or letters @cpp '0' @ce and @cpp '1' @ce for zero and one. Count of -elements is unlimited, but must be at least one. If the resulting vector is +components or letters @cpp '0' @ce and @cpp '1' @ce for zero and one. +Alternatively you can also address components using their numeric index --- +which is especially useful when your input has more than four components. Count +of elements is unlimited, but must be at least one. If the resulting vector is two, three or four-component, corresponding @ref Vector2, @ref Vector3, @ref Vector4, @ref Color3 or @ref Color4 specialization is returned. @@ -118,7 +122,7 @@ two, three or four-component, corresponding @ref Vector2, @ref Vector3, @ref Vector4::rgb(), @ref Vector4::xy(), @ref Vector3::xy() */ template constexpr typename Implementation::TypeForSize::Type gather(const T& vector) { - return {Implementation::Component::value(vector)...}; + return {Implementation::GatherComponent::value(vector)...}; } /** diff --git a/src/Magnum/Math/Test/SwizzleTest.cpp b/src/Magnum/Math/Test/SwizzleTest.cpp index 2f369b092..186e9a00f 100644 --- a/src/Magnum/Math/Test/SwizzleTest.cpp +++ b/src/Magnum/Math/Test/SwizzleTest.cpp @@ -35,11 +35,13 @@ struct SwizzleTest: Corrade::TestSuite::Tester { void gather(); void gatherConstants(); void gatherDifferentSize(); + void gatherFarComponents(); void scatter(); void scatterOneComponent(); void scatterRepeatedComponents(); void scatterOverwriteAllComponents(); + void scatterFarComponents(); }; typedef Vector<2, Int> Vector2i; @@ -50,23 +52,27 @@ SwizzleTest::SwizzleTest() { addTests({&SwizzleTest::gather, &SwizzleTest::gatherConstants, &SwizzleTest::gatherDifferentSize, + &SwizzleTest::gatherFarComponents, &SwizzleTest::scatter, &SwizzleTest::scatterOneComponent, &SwizzleTest::scatterRepeatedComponents, - &SwizzleTest::scatterOverwriteAllComponents}); + &SwizzleTest::scatterOverwriteAllComponents, + &SwizzleTest::scatterFarComponents}); } void SwizzleTest::gather() { constexpr auto a = Math::gather<'z', 'x', 'w', 'y'>(Vector4i{2, 4, 5, 7}); constexpr auto b = Math::gather<'b', 'r', 'a', 'g'>(Vector4i{2, 4, 5, 7}); + constexpr auto c = Math::gather<2, 0, 3, 1>(Vector4i{2, 4, 5, 7}); CORRADE_COMPARE(a, (Vector4i{5, 2, 7, 4})); CORRADE_COMPARE(b, (Vector4i{5, 2, 7, 4})); + CORRADE_COMPARE(c, (Vector4i{5, 2, 7, 4})); } void SwizzleTest::gatherConstants() { constexpr auto a = Math::gather<'1', 'w', '0', 'y'>(Vector4i{2, 4, 5, 7}); - constexpr auto b = Math::gather<'1', 3, '0', 2>(Vector4i{2, 4, 5, 7}); + constexpr auto b = Math::gather<'1', 3, '0', 1>(Vector4i{2, 4, 5, 7}); CORRADE_COMPARE(a, (Vector4i{1, 7, 0, 4})); CORRADE_COMPARE(b, (Vector4i{1, 7, 0, 4})); } @@ -82,11 +88,18 @@ void SwizzleTest::gatherDifferentSize() { CORRADE_COMPARE(c, (Vector<7, Int>{3, 1, 4, 2, 3, 2, 1})); } +void SwizzleTest::gatherFarComponents() { + constexpr auto a = Math::gather<5, 4, 6>(Vector<7, Int>{2, 4, 5, 7, 0, 3, 2}); + CORRADE_COMPARE(a, (Vector3i{3, 0, 2})); +} + void SwizzleTest::scatter() { constexpr auto a = Math::scatter<'w', 'y'>(Vector4i{2, 4, 5, 7}, Vector2i{1, 3}); constexpr auto b = Math::scatter<'a', 'g'>(Vector4i{2, 4, 5, 7}, Vector2i{1, 3}); + constexpr auto c = Math::scatter<3, 1>(Vector4i{2, 4, 5, 7}, Vector2i{1, 3}); CORRADE_COMPARE(a, (Vector4i{2, 3, 5, 1})); CORRADE_COMPARE(b, (Vector4i{2, 3, 5, 1})); + CORRADE_COMPARE(c, (Vector4i{2, 3, 5, 1})); /* It's an inverse, so doing the same gather should result back in the original values */ @@ -96,15 +109,19 @@ void SwizzleTest::scatter() { void SwizzleTest::scatterOneComponent() { constexpr auto a = Math::scatter<'w'>(Vector<7, Int>{2, 4, 5, 7, 0, 3, 2}, 1); constexpr auto b = Math::scatter<'a'>(Vector<7, Int>{2, 4, 5, 7, 0, 3, 2}, 1); + constexpr auto c = Math::scatter<3>(Vector<7, Int>{2, 4, 5, 7, 0, 3, 2}, 1); CORRADE_COMPARE(a, (Vector<7, Int>{2, 4, 5, 1, 0, 3, 2})); CORRADE_COMPARE(b, (Vector<7, Int>{2, 4, 5, 1, 0, 3, 2})); + CORRADE_COMPARE(c, (Vector<7, Int>{2, 4, 5, 1, 0, 3, 2})); } void SwizzleTest::scatterRepeatedComponents() { constexpr auto a = Math::scatter<'x', 'y', 'z', 'y', 'x'>(Vector3i{6, 12, 19}, Vector<5, Int>{1, 2, 3, 4, 5}); constexpr auto b = Math::scatter<'r', 'g', 'b', 'g', 'r'>(Vector3i{6, 12, 19}, Vector<5, Int>{1, 2, 3, 4, 5}); + constexpr auto c = Math::scatter<0, 1, 2, 1, 0>(Vector3i{6, 12, 19}, Vector<5, Int>{1, 2, 3, 4, 5}); CORRADE_COMPARE(a, (Vector3i{5, 4, 3})); CORRADE_COMPARE(b, (Vector3i{5, 4, 3})); + CORRADE_COMPARE(c, (Vector3i{5, 4, 3})); } void SwizzleTest::scatterOverwriteAllComponents() { @@ -114,6 +131,11 @@ void SwizzleTest::scatterOverwriteAllComponents() { CORRADE_COMPARE(b, (Vector4i{9, 3, 6, 1})); } +void SwizzleTest::scatterFarComponents() { + constexpr auto a = Math::scatter<5, 4, 6>(Vector<7, Int>{2, 4, 5, 7, 0, 3, 2}, Vector3i{1, 6, 9}); + CORRADE_COMPARE(a, (Vector<7, Int>{2, 4, 5, 7, 6, 1, 9})); +} + }}}} CORRADE_TEST_MAIN(Magnum::Math::Test::SwizzleTest) diff --git a/src/Magnum/Math/Vector.h b/src/Magnum/Math/Vector.h index 2a1a3c39e..b948ad8f7 100644 --- a/src/Magnum/Math/Vector.h +++ b/src/Magnum/Math/Vector.h @@ -82,8 +82,8 @@ namespace Implementation { /* Used to make friends to speed up debug builds */ template struct MatrixDeterminant; /* To make gather() / scatter() faster */ - template struct ComponentAtPosition; - template struct ComponentOr; + template struct GatherComponentAt; + template struct ScatterComponentOr; } /** @relatesalso Vector @@ -656,8 +656,8 @@ template class Vector { template friend class Matrix; template friend struct Implementation::MatrixDeterminant; /* To make gather() / scatter() faster */ - template friend struct Implementation::ComponentAtPosition; - template friend struct Implementation::ComponentOr; + template friend struct Implementation::GatherComponentAt; + template friend struct Implementation::ScatterComponentOr; /* So the out-of-class comparators can access data directly to avoid function call overhead */