Browse Source

Math: support numeric indexing in gather() / scatter() as well.

pull/326/merge
Vladimír Vondruš 7 years ago
parent
commit
17b3c8fac5
  1. 3
      doc/changelog.dox
  2. 86
      src/Magnum/Math/Swizzle.h
  3. 26
      src/Magnum/Math/Test/SwizzleTest.cpp
  4. 8
      src/Magnum/Math/Vector.h

3
doc/changelog.dox

@ -210,7 +210,8 @@ See also:
- Scalar and vector @ref Math::equal() and @ref Math::notEqual() which do a - 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 component-wise comparison in the vector case, as the equality/non-equality
operators return a single @cpp bool @ce 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 @subsubsection changelog-latest-new-meshtools MeshTools library

86
src/Magnum/Math/Swizzle.h

@ -34,27 +34,27 @@
namespace Magnum { namespace Math { namespace Magnum { namespace Math {
namespace Implementation { namespace Implementation {
template<std::size_t size, std::size_t position> struct ComponentAtPosition { template<std::size_t size, std::size_t position> struct GatherComponentAt {
static_assert(size > position, "swizzle parameter out of range of gather vector"); 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<class T> constexpr static T value(const Math::Vector<size, T>& vector) { template<class T> constexpr static T value(const Math::Vector<size, T>& vector) {
return vector._data[position]; return vector._data[position];
} }
}; };
template<std::size_t size, char component> struct Component {}; template<std::size_t size, char component> struct GatherComponent: GatherComponentAt<size, component> {};
template<std::size_t size> struct Component<size, 'x'>: public ComponentAtPosition<size, 0> {}; template<std::size_t size> struct GatherComponent<size, 'x'>: public GatherComponentAt<size, 0> {};
template<std::size_t size> struct Component<size, 'y'>: public ComponentAtPosition<size, 1> {}; template<std::size_t size> struct GatherComponent<size, 'y'>: public GatherComponentAt<size, 1> {};
template<std::size_t size> struct Component<size, 'z'>: public ComponentAtPosition<size, 2> {}; template<std::size_t size> struct GatherComponent<size, 'z'>: public GatherComponentAt<size, 2> {};
template<std::size_t size> struct Component<size, 'w'>: public ComponentAtPosition<size, 3> {}; template<std::size_t size> struct GatherComponent<size, 'w'>: public GatherComponentAt<size, 3> {};
template<std::size_t size> struct Component<size, 'r'>: public ComponentAtPosition<size, 0> {}; template<std::size_t size> struct GatherComponent<size, 'r'>: public GatherComponentAt<size, 0> {};
template<std::size_t size> struct Component<size, 'g'>: public ComponentAtPosition<size, 1> {}; template<std::size_t size> struct GatherComponent<size, 'g'>: public GatherComponentAt<size, 1> {};
template<std::size_t size> struct Component<size, 'b'>: public ComponentAtPosition<size, 2> {}; template<std::size_t size> struct GatherComponent<size, 'b'>: public GatherComponentAt<size, 2> {};
template<std::size_t size> struct Component<size, 'a'>: public ComponentAtPosition<size, 3> {}; template<std::size_t size> struct GatherComponent<size, 'a'>: public GatherComponentAt<size, 3> {};
template<std::size_t size> struct Component<size, '0'> { template<std::size_t size> struct GatherComponent<size, '0'> {
template<class T> constexpr static T value(const Math::Vector<size, T>&) { return T(0); } template<class T> constexpr static T value(const Math::Vector<size, T>&) { return T(0); }
}; };
template<std::size_t size> struct Component<size, '1'> { template<std::size_t size> struct GatherComponent<size, '1'> {
template<class T> constexpr static T value(const Math::Vector<size, T>&) { return T(1); } template<class T> constexpr static T value(const Math::Vector<size, T>&) { return T(1); }
}; };
@ -62,40 +62,42 @@ namespace Implementation {
typedef Math::Vector<size, typename T::Type> Type; typedef Math::Vector<size, typename T::Type> Type;
}; };
template<std::size_t size, char component, std::size_t i> struct ComponentOr { template<std::size_t size, std::size_t i, bool = true> struct ScatterComponentOr {
static_assert(component == 'x' || component == 'r' || template<class T> constexpr static T value(const Math::Vector<size, T>&, const T& value) {
((component == 'y' || component == 'g') && size > 1) || return value;
((component == 'z' || component == 'b') && size > 2) || }
((component == 'w' || component == 'a') && size > 3), };
"swizzle parameter out of range of scatter vector"); template<std::size_t size, std::size_t i> struct ScatterComponentOr<size, i, false> {
template<class T> constexpr static T value(const Math::Vector<size, T>& vector, const T&) { template<class T> constexpr static T value(const Math::Vector<size, T>& vector, const T&) {
return vector._data[i]; return vector._data[i];
} }
}; };
template<std::size_t size, std::size_t position> struct Value { template<std::size_t size, char component, std::size_t i> struct ScatterComponent: ScatterComponentOr<size, i, component == i> {
template<class T> constexpr static T value(const Math::Vector<size, T>&, const T& value) { static_assert(component == 'x' || component == 'r' ||
return value; ((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<std::size_t size> struct ComponentOr<size, 'x', 0>: Value<size, 0> {}; template<std::size_t size> struct ScatterComponent<size, 'x', 0>: ScatterComponentOr<size, 0> {};
template<std::size_t size> struct ComponentOr<size, 'y', 1>: Value<size, 1> {}; template<std::size_t size> struct ScatterComponent<size, 'y', 1>: ScatterComponentOr<size, 1> {};
template<std::size_t size> struct ComponentOr<size, 'z', 2>: Value<size, 2> {}; template<std::size_t size> struct ScatterComponent<size, 'z', 2>: ScatterComponentOr<size, 2> {};
template<std::size_t size> struct ComponentOr<size, 'w', 3>: Value<size, 3> {}; template<std::size_t size> struct ScatterComponent<size, 'w', 3>: ScatterComponentOr<size, 3> {};
template<std::size_t size> struct ComponentOr<size, 'r', 0>: Value<size, 0> {}; template<std::size_t size> struct ScatterComponent<size, 'r', 0>: ScatterComponentOr<size, 0> {};
template<std::size_t size> struct ComponentOr<size, 'g', 1>: Value<size, 1> {}; template<std::size_t size> struct ScatterComponent<size, 'g', 1>: ScatterComponentOr<size, 1> {};
template<std::size_t size> struct ComponentOr<size, 'b', 2>: Value<size, 2> {}; template<std::size_t size> struct ScatterComponent<size, 'b', 2>: ScatterComponentOr<size, 2> {};
template<std::size_t size> struct ComponentOr<size, 'a', 3>: Value<size, 3> {}; template<std::size_t size> struct ScatterComponent<size, 'a', 3>: ScatterComponentOr<size, 3> {};
template<class T, char component, std::size_t ...sequence> constexpr T componentOr(const T& vector, const typename T::Type& value, Sequence<sequence...>) { template<class T, char component, std::size_t ...sequence> constexpr T scatterComponentOr(const T& vector, const typename T::Type& value, Sequence<sequence...>) {
return {ComponentOr<T::Size, component, sequence>::value(vector, value)...}; return {ScatterComponent<T::Size, component, sequence>::value(vector, value)...};
} }
template<class T, std::size_t valueSize> constexpr T scatterRecursive(const T& vector, const Math::Vector<valueSize, typename T::Type>&, std::size_t) { template<class T, std::size_t valueSize> constexpr T scatterRecursive(const T& vector, const Vector<valueSize, typename T::Type>&, std::size_t) {
return vector; return vector;
} }
template<class T, std::size_t valueSize, char component, char ...next> constexpr T scatterRecursive(const T& vector, const Math::Vector<valueSize, typename T::Type>& values, std::size_t valueIndex) { template<class T, std::size_t valueSize, char component, char ...next> constexpr T scatterRecursive(const T& vector, const Vector<valueSize, typename T::Type>& values, std::size_t valueIndex) {
return scatterRecursive<T, valueSize, next...>( return scatterRecursive<T, valueSize, next...>(
componentOr<T, component>(vector, values[valueIndex], typename GenerateSequence<T::Size>::Type{}), scatterComponentOr<T, component>(vector, values[valueIndex], typename GenerateSequence<T::Size>::Type{}),
values, valueIndex + 1); 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 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 @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 components or letters @cpp '0' @ce and @cpp '1' @ce for zero and one.
elements is unlimited, but must be at least one. If the resulting vector is 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, two, three or four-component, corresponding @ref Vector2, @ref Vector3,
@ref Vector4, @ref Color3 or @ref Color4 specialization is returned. @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() @ref Vector4::rgb(), @ref Vector4::xy(), @ref Vector3::xy()
*/ */
template<char ...components, class T> constexpr typename Implementation::TypeForSize<sizeof...(components), T>::Type gather(const T& vector) { template<char ...components, class T> constexpr typename Implementation::TypeForSize<sizeof...(components), T>::Type gather(const T& vector) {
return {Implementation::Component<T::Size, components>::value(vector)...}; return {Implementation::GatherComponent<T::Size, components>::value(vector)...};
} }
/** /**

26
src/Magnum/Math/Test/SwizzleTest.cpp

@ -35,11 +35,13 @@ struct SwizzleTest: Corrade::TestSuite::Tester {
void gather(); void gather();
void gatherConstants(); void gatherConstants();
void gatherDifferentSize(); void gatherDifferentSize();
void gatherFarComponents();
void scatter(); void scatter();
void scatterOneComponent(); void scatterOneComponent();
void scatterRepeatedComponents(); void scatterRepeatedComponents();
void scatterOverwriteAllComponents(); void scatterOverwriteAllComponents();
void scatterFarComponents();
}; };
typedef Vector<2, Int> Vector2i; typedef Vector<2, Int> Vector2i;
@ -50,23 +52,27 @@ SwizzleTest::SwizzleTest() {
addTests({&SwizzleTest::gather, addTests({&SwizzleTest::gather,
&SwizzleTest::gatherConstants, &SwizzleTest::gatherConstants,
&SwizzleTest::gatherDifferentSize, &SwizzleTest::gatherDifferentSize,
&SwizzleTest::gatherFarComponents,
&SwizzleTest::scatter, &SwizzleTest::scatter,
&SwizzleTest::scatterOneComponent, &SwizzleTest::scatterOneComponent,
&SwizzleTest::scatterRepeatedComponents, &SwizzleTest::scatterRepeatedComponents,
&SwizzleTest::scatterOverwriteAllComponents}); &SwizzleTest::scatterOverwriteAllComponents,
&SwizzleTest::scatterFarComponents});
} }
void SwizzleTest::gather() { void SwizzleTest::gather() {
constexpr auto a = Math::gather<'z', 'x', 'w', 'y'>(Vector4i{2, 4, 5, 7}); 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 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(a, (Vector4i{5, 2, 7, 4}));
CORRADE_COMPARE(b, (Vector4i{5, 2, 7, 4})); CORRADE_COMPARE(b, (Vector4i{5, 2, 7, 4}));
CORRADE_COMPARE(c, (Vector4i{5, 2, 7, 4}));
} }
void SwizzleTest::gatherConstants() { void SwizzleTest::gatherConstants() {
constexpr auto a = Math::gather<'1', 'w', '0', 'y'>(Vector4i{2, 4, 5, 7}); 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(a, (Vector4i{1, 7, 0, 4}));
CORRADE_COMPARE(b, (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})); 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() { void SwizzleTest::scatter() {
constexpr auto a = Math::scatter<'w', 'y'>(Vector4i{2, 4, 5, 7}, Vector2i{1, 3}); 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 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(a, (Vector4i{2, 3, 5, 1}));
CORRADE_COMPARE(b, (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 /* It's an inverse, so doing the same gather should result back in the
original values */ original values */
@ -96,15 +109,19 @@ void SwizzleTest::scatter() {
void SwizzleTest::scatterOneComponent() { void SwizzleTest::scatterOneComponent() {
constexpr auto a = Math::scatter<'w'>(Vector<7, Int>{2, 4, 5, 7, 0, 3, 2}, 1); 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 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(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(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() { 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 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 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(a, (Vector3i{5, 4, 3}));
CORRADE_COMPARE(b, (Vector3i{5, 4, 3})); CORRADE_COMPARE(b, (Vector3i{5, 4, 3}));
CORRADE_COMPARE(c, (Vector3i{5, 4, 3}));
} }
void SwizzleTest::scatterOverwriteAllComponents() { void SwizzleTest::scatterOverwriteAllComponents() {
@ -114,6 +131,11 @@ void SwizzleTest::scatterOverwriteAllComponents() {
CORRADE_COMPARE(b, (Vector4i{9, 3, 6, 1})); 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) CORRADE_TEST_MAIN(Magnum::Math::Test::SwizzleTest)

8
src/Magnum/Math/Vector.h

@ -82,8 +82,8 @@ namespace Implementation {
/* Used to make friends to speed up debug builds */ /* Used to make friends to speed up debug builds */
template<std::size_t, class> struct MatrixDeterminant; template<std::size_t, class> struct MatrixDeterminant;
/* To make gather() / scatter() faster */ /* To make gather() / scatter() faster */
template<std::size_t, std::size_t> struct ComponentAtPosition; template<std::size_t, std::size_t> struct GatherComponentAt;
template<std::size_t, char, std::size_t> struct ComponentOr; template<std::size_t, std::size_t, bool> struct ScatterComponentOr;
} }
/** @relatesalso Vector /** @relatesalso Vector
@ -656,8 +656,8 @@ template<std::size_t size, class T> class Vector {
template<std::size_t, class> friend class Matrix; template<std::size_t, class> friend class Matrix;
template<std::size_t, class> friend struct Implementation::MatrixDeterminant; template<std::size_t, class> friend struct Implementation::MatrixDeterminant;
/* To make gather() / scatter() faster */ /* To make gather() / scatter() faster */
template<std::size_t, std::size_t> friend struct Implementation::ComponentAtPosition; template<std::size_t, std::size_t> friend struct Implementation::GatherComponentAt;
template<std::size_t, char, std::size_t> friend struct Implementation::ComponentOr; template<std::size_t, std::size_t, bool> friend struct Implementation::ScatterComponentOr;
/* So the out-of-class comparators can access data directly to avoid /* So the out-of-class comparators can access data directly to avoid
function call overhead */ function call overhead */

Loading…
Cancel
Save