From acc8d8994b969e10297a9ae01acb1e71f95145be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 5 Sep 2019 13:05:48 +0200 Subject: [PATCH] Math: added scatter(), renamed swizzle() to gather(). The swizzle() function is still present, only marked as deprecated and scheduled to be removed in some future release. --- doc/changelog.dox | 3 + doc/matrix-vector.dox | 4 +- doc/snippets/MagnumMath.cpp | 27 ++++-- src/Magnum/Math/Swizzle.h | 83 +++++++++++++++-- src/Magnum/Math/Test/ColorTest.cpp | 8 +- src/Magnum/Math/Test/SwizzleTest.cpp | 89 ++++++++++++++----- src/Magnum/Math/Test/Vector2Test.cpp | 2 +- src/Magnum/Math/Test/Vector3Test.cpp | 2 +- src/Magnum/Math/Test/Vector4Test.cpp | 2 +- src/Magnum/Math/Vector3.h | 4 +- .../TgaImageConverter/TgaImageConverter.cpp | 4 +- src/MagnumPlugins/TgaImporter/TgaImporter.cpp | 4 +- 12 files changed, 180 insertions(+), 52 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 079ad2bcb..207a9db66 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -210,6 +210,7 @@ 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() @subsubsection changelog-latest-new-meshtools MeshTools library @@ -678,6 +679,8 @@ See also: the wrong thing. For backwards compatibility @ref Platform::BasicScreenedApplication is convertible to a pointer and implements @cpp operator-> @ce, but this conversion is deprecated. +- @cpp Math::swizzle() @ce is deprecated, use @ref Math::gather() instead as + that fits better with the new @ref Math::scatter() @subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs diff --git a/doc/matrix-vector.dox b/doc/matrix-vector.dox index cbf42fa6e..c677c433d 100644 --- a/doc/matrix-vector.dox +++ b/doc/matrix-vector.dox @@ -147,8 +147,8 @@ and subparts: @ref Color3 and @ref Color4 name their components `rgba` instead of `xyzw`. -For more involved operations with components there is the @ref Math::swizzle() -function: +For more involved operations with components there is the @ref Math::gather() +and @ref Math::scatter() functions: @snippet MagnumMath.cpp matrix-vector-access-swizzle diff --git a/doc/snippets/MagnumMath.cpp b/doc/snippets/MagnumMath.cpp index 4c31392fc..c9001f560 100644 --- a/doc/snippets/MagnumMath.cpp +++ b/doc/snippets/MagnumMath.cpp @@ -162,9 +162,14 @@ static_cast(x); { /* [matrix-vector-access-swizzle] */ Vector4i orig{-1, 2, 3, 4}; -Vector4i bgra = Math::swizzle<'b', 'g', 'r', 'a'>(orig); // { 3, 2, -1, 4 } -Math::Vector<6, Int> w10xyz = Math::swizzle<'w', '1', '0', 'x', 'y', 'z'>(orig); +Vector4i bgra = Math::gather<'b', 'g', 'r', 'a'>(orig); // { 3, 2, -1, 4 } +Math::Vector<6, Int> w10xyz = Math::gather<'w', '1', '0', 'x', 'y', 'z'>(orig); // { 4, 1, 0, -1, 2, 3 } + +Vector4 vec{1.5f, 3.0f, 0.1f, 1.1f}; +Vector2 coords{5.0f, -2.0f}; +Math::scatter<'z', 'w'>(vec, coords); // { 1.5, 3.0, 5.0, -2.0 } + /* [matrix-vector-access-swizzle] */ static_cast(bgra); static_cast(w10xyz); @@ -1077,15 +1082,27 @@ static_cast(mySet); } { -/* [swizzle] */ +/* [gather] */ Vector4i original(-1, 2, 3, 4); -auto vec = Math::swizzle<'w', '1', '0', 'x', 'y', 'z'>(original); +auto vec = Math::gather<'w', '1', '0', 'x', 'y', 'z'>(original); // vec == { 4, 1, 0, -1, 2, 3 } -/* [swizzle] */ +/* [gather] */ static_cast(vec); } +{ +/* [scatter] */ +Vector4 vec{1.5f, 3.0f, 0.1f, 1.1f}; +Vector2 coords{5.0f, -2.0f}; +vec = Math::scatter<'z', 'w'>(vec, coords); // { 1.5, 3.0, 5.0, -2.0 } + +/* Combine the two for more advanced swizzles */ +Vector4 vec2; +vec2 = Math::scatter<'w', 'x', 'y'>(vec2, Math::gather<'x', 'w', 'y'>(vec)); +/* [scatter] */ +} + { Float a{}, b{}; /* [TypeTraits-equalsZero] */ diff --git a/src/Magnum/Math/Swizzle.h b/src/Magnum/Math/Swizzle.h index 1c71f6bca..caa3feded 100644 --- a/src/Magnum/Math/Swizzle.h +++ b/src/Magnum/Math/Swizzle.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Function @ref Magnum::Math::swizzle() + * @brief Function @ref Magnum::Math::gather(), @ref Magnum::Math::scatter() */ #include "Magnum/Math/Vector.h" @@ -35,9 +35,11 @@ namespace Magnum { namespace Math { namespace Implementation { template struct ComponentAtPosition { - static_assert(size > position, "Swizzle parameter out of range of base vector"); + static_assert(size > position, "swizzle parameter out of range of gather vector"); - template constexpr static T value(const Math::Vector& vector) { return vector[position]; } + template constexpr static T value(const Math::Vector& vector) { + return vector[position]; + } }; template struct Component {}; @@ -59,14 +61,51 @@ namespace Implementation { template struct TypeForSize { 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 constexpr static T value(const Math::Vector& vector, const T&) { + return vector[i]; + } + }; + template struct Value { + template constexpr static T value(const Math::Vector&, const T& value) { + return 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 struct ComponentOr: Value {}; + + template constexpr T componentOr(const T& vector, const typename T::Type& value, Sequence) { + return {ComponentOr::value(vector, value)...}; + } + template constexpr T scatterRecursive(const T& vector, const Math::Vector&, std::size_t) { + return vector; + } + template constexpr T scatterRecursive(const T& vector, const Math::Vector& values, std::size_t valueIndex) { + return scatterRecursive( + componentOr(vector, values[valueIndex], typename GenerateSequence::Type{}), + values, valueIndex + 1); + } } /** -@brief Swizzle @ref Vector components +@brief Gather @ref Vector components -Creates new vector from given components. Example: +Creates a new vector from given components. Example: -@snippet MagnumMath.cpp swizzle +@snippet MagnumMath.cpp gather 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 @@ -75,13 +114,41 @@ 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. -@see @ref matrix-vector-component-access, @ref Vector4::xyz(), +@see @ref scatter(), @ref matrix-vector-component-access, @ref Vector4::xyz(), @ref Vector4::rgb(), @ref Vector4::xy(), @ref Vector3::xy() */ -template constexpr typename Implementation::TypeForSize::Type swizzle(const T& vector) { +template constexpr typename Implementation::TypeForSize::Type gather(const T& vector) { return {Implementation::Component::value(vector)...}; } +/** +@brief Scatter @ref Vector components +@param vector Vector to update +@param values Values to update it with +@return Updated vector + +Returns a copy of @p vector with particular components updated from @p values. +Inverse to @ref gather(), supporting the same component addressing except for +@cpp '0' @ce and @cpp '1' @ce. Example: + +@snippet MagnumMath.cpp scatter + +@see @ref matrix-vector-component-access, @ref Vector4::xyz(), + @ref Vector4::rgb(), @ref Vector4::xy(), @ref Vector3::xy() +*/ +template constexpr T scatter(const T& vector, const Vector& values) { + return Implementation::scatterRecursive(vector, values, 0); +} + +#ifdef MAGNUM_BUILD_DEPRECATED +/** @brief @copybrief gather() + * @deprecated Use @ref gather instead. + */ +template CORRADE_DEPRECATED("use gather() instead") constexpr typename Implementation::TypeForSize::Type swizzle(const T& vector) { + return gather(vector); +} +#endif + }} #endif diff --git a/src/Magnum/Math/Test/ColorTest.cpp b/src/Magnum/Math/Test/ColorTest.cpp index b02ec4e81..ef8cc62a3 100644 --- a/src/Magnum/Math/Test/ColorTest.cpp +++ b/src/Magnum/Math/Test/ColorTest.cpp @@ -990,16 +990,16 @@ void ColorTest::swizzleType() { constexpr Color3 origColor3; constexpr Color4ub origColor4; - constexpr auto a = Math::swizzle<'y', 'z', 'r'>(origColor3); + constexpr auto a = Math::gather<'y', 'z', 'r'>(origColor3); CORRADE_VERIFY((std::is_same::value)); - constexpr auto b = Math::swizzle<'y', 'z', 'a'>(origColor4); + constexpr auto b = Math::gather<'y', 'z', 'a'>(origColor4); CORRADE_VERIFY((std::is_same::value)); - constexpr auto c = Math::swizzle<'y', 'z', 'y', 'x'>(origColor3); + constexpr auto c = Math::gather<'y', 'z', 'y', 'x'>(origColor3); CORRADE_VERIFY((std::is_same::value)); - constexpr auto d = Math::swizzle<'y', 'a', 'y', 'x'>(origColor4); + constexpr auto d = Math::gather<'y', 'a', 'y', 'x'>(origColor4); CORRADE_VERIFY((std::is_same::value)); } diff --git a/src/Magnum/Math/Test/SwizzleTest.cpp b/src/Magnum/Math/Test/SwizzleTest.cpp index 12ba8d4b4..2f369b092 100644 --- a/src/Magnum/Math/Test/SwizzleTest.cpp +++ b/src/Magnum/Math/Test/SwizzleTest.cpp @@ -32,45 +32,86 @@ namespace Magnum { namespace Math { namespace Test { namespace { struct SwizzleTest: Corrade::TestSuite::Tester { explicit SwizzleTest(); - void components(); - void constants(); - void rgba(); - void sizes(); + void gather(); + void gatherConstants(); + void gatherDifferentSize(); + + void scatter(); + void scatterOneComponent(); + void scatterRepeatedComponents(); + void scatterOverwriteAllComponents(); }; +typedef Vector<2, Int> Vector2i; +typedef Vector<3, Int> Vector3i; typedef Vector<4, Int> Vector4i; SwizzleTest::SwizzleTest() { - addTests({&SwizzleTest::components, - &SwizzleTest::constants, - &SwizzleTest::rgba, - &SwizzleTest::sizes}); + addTests({&SwizzleTest::gather, + &SwizzleTest::gatherConstants, + &SwizzleTest::gatherDifferentSize, + + &SwizzleTest::scatter, + &SwizzleTest::scatterOneComponent, + &SwizzleTest::scatterRepeatedComponents, + &SwizzleTest::scatterOverwriteAllComponents}); } -void SwizzleTest::components() { - constexpr auto a = swizzle<'z', 'x', 'w', 'y'>(Vector4i(2, 4, 5, 7)); - CORRADE_COMPARE(a, Vector4i(5, 2, 7, 4)); +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}); + CORRADE_COMPARE(a, (Vector4i{5, 2, 7, 4})); + CORRADE_COMPARE(b, (Vector4i{5, 2, 7, 4})); } -void SwizzleTest::constants() { - constexpr auto a = swizzle<'1', 'w', '0', 'y'>(Vector4i(2, 4, 5, 7)); - CORRADE_COMPARE(a, Vector4i(1, 7, 0, 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}); + CORRADE_COMPARE(a, (Vector4i{1, 7, 0, 4})); + CORRADE_COMPARE(b, (Vector4i{1, 7, 0, 4})); } -void SwizzleTest::rgba() { - constexpr auto a = swizzle<'b', 'r', 'a', 'g'>(Vector4i(2, 4, 5, 7)); - CORRADE_COMPARE(a, Vector4i(5, 2, 7, 4)); +void SwizzleTest::gatherDifferentSize() { + constexpr auto a = Math::gather<'y', 'x', 'x'>(Vector<2, Int>{1, 2}); + CORRADE_COMPARE(a, (Vector3i{2, 1, 1})); + + constexpr auto b = Math::gather<'z'>(Vector4i{1, 2, 3, 4}); + CORRADE_COMPARE(b, (Vector<1, Int>{3})); + + constexpr auto c = Math::gather<'z', 'x', 'w', 'y', 'z', 'y', 'x'>(Vector4i{1, 2, 3, 4}); + CORRADE_COMPARE(c, (Vector<7, Int>{3, 1, 4, 2, 3, 2, 1})); } -void SwizzleTest::sizes() { - constexpr auto a = swizzle<'y', 'x', 'x'>(Vector<2, Int>(1, 2)); - CORRADE_COMPARE(a, (Vector<3, Int>(2, 1, 1))); +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}); + CORRADE_COMPARE(a, (Vector4i{2, 3, 5, 1})); + CORRADE_COMPARE(b, (Vector4i{2, 3, 5, 1})); + + /* It's an inverse, so doing the same gather should result back in the + original values */ + CORRADE_COMPARE((Math::gather<'w', 'y'>(a)), (Vector2i{1, 3})); +} - constexpr auto b = swizzle<'z'>(Vector4i(1, 2, 3, 4)); - CORRADE_COMPARE(b, (Vector<1, Int>(3))); +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); + 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})); +} + +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}); + CORRADE_COMPARE(a, (Vector3i{5, 4, 3})); + CORRADE_COMPARE(b, (Vector3i{5, 4, 3})); +} - constexpr auto c = swizzle<'z', 'x', 'w', 'y', 'z', 'y', 'x'>(Vector4i(1, 2, 3, 4)); - CORRADE_COMPARE(c, (Vector<7, Int>(3, 1, 4, 2, 3, 2, 1))); +void SwizzleTest::scatterOverwriteAllComponents() { + constexpr auto a = Math::scatter<'w', 'y', 'z', 'x'>(Vector4i{2, 4, 5, 7}, Vector4i{1, 3, 6, 9}); + constexpr auto b = Math::gather<'w', 'y', 'z', 'x'>(Vector4i{1, 3, 6, 9}); + CORRADE_COMPARE(a, (Vector4i{9, 3, 6, 1})); + CORRADE_COMPARE(b, (Vector4i{9, 3, 6, 1})); } }}}} diff --git a/src/Magnum/Math/Test/Vector2Test.cpp b/src/Magnum/Math/Test/Vector2Test.cpp index e54146b32..991956768 100644 --- a/src/Magnum/Math/Test/Vector2Test.cpp +++ b/src/Magnum/Math/Test/Vector2Test.cpp @@ -250,7 +250,7 @@ void Vector2Test::strictWeakOrdering() { void Vector2Test::swizzleType() { constexpr Vector<4, Int> orig; - constexpr auto a = swizzle<'y', 'a'>(orig); + constexpr auto a = gather<'y', 'a'>(orig); CORRADE_VERIFY((std::is_same::value)); } diff --git a/src/Magnum/Math/Test/Vector3Test.cpp b/src/Magnum/Math/Test/Vector3Test.cpp index 40a5411d1..bfeb9030e 100644 --- a/src/Magnum/Math/Test/Vector3Test.cpp +++ b/src/Magnum/Math/Test/Vector3Test.cpp @@ -274,7 +274,7 @@ void Vector3Test::strictWeakOrdering() { void Vector3Test::swizzleType() { constexpr Vector<4, Int> orig; - constexpr auto b = swizzle<'y', 'z', 'a'>(orig); + constexpr auto b = gather<'y', 'z', 'a'>(orig); CORRADE_VERIFY((std::is_same::value)); } diff --git a/src/Magnum/Math/Test/Vector4Test.cpp b/src/Magnum/Math/Test/Vector4Test.cpp index 229c5bea7..db89add8a 100644 --- a/src/Magnum/Math/Test/Vector4Test.cpp +++ b/src/Magnum/Math/Test/Vector4Test.cpp @@ -317,7 +317,7 @@ void Vector4Test::strictWeakOrdering() { void Vector4Test::swizzleType() { constexpr Vector4i orig; - constexpr auto c = swizzle<'y', 'a', 'y', 'x'>(orig); + constexpr auto c = gather<'y', 'a', 'y', 'x'>(orig); CORRADE_VERIFY((std::is_same::value)); } diff --git a/src/Magnum/Math/Vector3.h b/src/Magnum/Math/Vector3.h index c6fe63338..e6e56b077 100644 --- a/src/Magnum/Math/Vector3.h +++ b/src/Magnum/Math/Vector3.h @@ -51,8 +51,8 @@ https://twitter.com/sjb3d/status/563640846671953920): @f[ @see @ref cross(const Vector2&, const Vector2&), @ref planeEquation() */ template inline Vector3 cross(const Vector3& a, const Vector3& b) { - return swizzle<'y', 'z', 'x'>(a*swizzle<'y', 'z', 'x'>(b) - - b*swizzle<'y', 'z', 'x'>(a)); + return gather<'y', 'z', 'x'>(a*gather<'y', 'z', 'x'>(b) - + b*gather<'y', 'z', 'x'>(a)); } /** diff --git a/src/MagnumPlugins/TgaImageConverter/TgaImageConverter.cpp b/src/MagnumPlugins/TgaImageConverter/TgaImageConverter.cpp index eef60bd3c..44d10b435 100644 --- a/src/MagnumPlugins/TgaImageConverter/TgaImageConverter.cpp +++ b/src/MagnumPlugins/TgaImageConverter/TgaImageConverter.cpp @@ -82,11 +82,11 @@ Containers::Array TgaImageConverter::doExportToData(const ImageView2D& ima if(image.format() == PixelFormat::RGB8Unorm) { auto pixels = reinterpret_cast*>(data.begin()+sizeof(Implementation::TgaHeader)); std::transform(pixels, pixels + image.size().product(), pixels, - [](Math::Vector3 pixel) { return Math::swizzle<'b', 'g', 'r'>(pixel); }); + [](Math::Vector3 pixel) { return Math::gather<'b', 'g', 'r'>(pixel); }); } else if(image.format() == PixelFormat::RGBA8Unorm) { auto pixels = reinterpret_cast*>(data.begin()+sizeof(Implementation::TgaHeader)); std::transform(pixels, pixels + image.size().product(), pixels, - [](Math::Vector4 pixel) { return Math::swizzle<'b', 'g', 'r', 'a'>(pixel); }); + [](Math::Vector4 pixel) { return Math::gather<'b', 'g', 'r', 'a'>(pixel); }); } return data; diff --git a/src/MagnumPlugins/TgaImporter/TgaImporter.cpp b/src/MagnumPlugins/TgaImporter/TgaImporter.cpp index 2e5ed082b..0bd55bf94 100644 --- a/src/MagnumPlugins/TgaImporter/TgaImporter.cpp +++ b/src/MagnumPlugins/TgaImporter/TgaImporter.cpp @@ -136,11 +136,11 @@ Containers::Optional TgaImporter::doImage2D(UnsignedInt) { if(format == PixelFormat::RGB8Unorm) { auto pixels = reinterpret_cast*>(data.data()); std::transform(pixels, pixels + size.product(), pixels, - [](Math::Vector3 pixel) { return Math::swizzle<'b', 'g', 'r'>(pixel); }); + [](Math::Vector3 pixel) { return Math::gather<'b', 'g', 'r'>(pixel); }); } else if(format == PixelFormat::RGBA8Unorm) { auto pixels = reinterpret_cast*>(data.data()); std::transform(pixels, pixels + size.product(), pixels, - [](Math::Vector4 pixel) { return Math::swizzle<'b', 'g', 'r', 'a'>(pixel); }); + [](Math::Vector4 pixel) { return Math::gather<'b', 'g', 'r', 'a'>(pixel); }); } return ImageData2D{storage, format, size, std::move(data)};