diff --git a/src/Math/Matrix.h b/src/Math/Matrix.h index 47d6c41ee..8dfd40d93 100644 --- a/src/Math/Matrix.h +++ b/src/Math/Matrix.h @@ -26,16 +26,6 @@ namespace Magnum { namespace Math { #ifndef DOXYGEN_GENERATING_OUTPUT namespace Implementation { template class MatrixDeterminant; - - template struct Sequence {}; - - /* E.g. GenerateSequence<3>::Type is Sequence<0, 1, 2> */ - template struct GenerateSequence: - GenerateSequence {}; - - template struct GenerateSequence<0, sequence...> { - typedef Sequence Type; - }; } #endif diff --git a/src/Math/Swizzle.h b/src/Math/Swizzle.h index cfbec9290..e964948a6 100644 --- a/src/Math/Swizzle.h +++ b/src/Math/Swizzle.h @@ -48,6 +48,26 @@ namespace Implementation { template struct TypeForSize<2, T> { typedef Vector2 Type; }; template struct TypeForSize<3, T> { typedef Vector3 Type; }; template struct TypeForSize<4, T> { typedef Vector4 Type; }; + + inline constexpr size_t getPosition(size_t size, size_t position) { + return size > position ? position : throw; + } + + template inline constexpr size_t getComponent(char component) { + return component == 'x' ? getPosition(size, 0) : + component == 'y' ? getPosition(size, 1) : + component == 'z' ? getPosition(size, 2) : + component == 'w' ? getPosition(size, 3) : + component == 'r' ? getPosition(size, 0) : + component == 'g' ? getPosition(size, 1) : + component == 'b' ? getPosition(size, 2) : + component == 'a' ? getPosition(size, 3) : + throw; + } + + template inline constexpr Vector swizzleFrom(Sequence, const Vector& vector, const char(&components)[sizeof...(sequence)+1]) { + return {vector[getComponent(components[sequence])]...}; + } } #endif @@ -66,12 +86,43 @@ elements is unlimited, but must be at least one. If the resulting vector is two, three or four-component, corresponding Vector2, Vector3 or Vector4 specialization is returned. +@attention This function is less convenient to write than +swizzle(const Vector&, const char(&)[newSize]), but the evaluation of +the swizzling operation is guaranteed to be always done at compile time +instead of at runtime. + @see Vector4::xyz(), Vector4::rgb(), Vector4::xy(), Vector3::xy() */ template inline constexpr typename Implementation::TypeForSize::Type swizzle(const Vector& vector) { return {vector[Implementation::GetComponent::value()]...}; } +/** +@brief Swizzle Vector components + +Creates new vector from given components. Example: +@code +Vector4 original(1, 2, 3, 4); + +auto vec = swizzle(original, "abbgrr"); +// vec == { 4, 3, 3, 2, 1, 1 } +@endcode +You can use letters `x`, `y`, `z`, `w` and `r`, `g`, `b`, `a`. Count of +elements is unlimited, but must be at least one. If the resulting vector is +two, three or four-component, corresponding Vector2, Vector3 or Vector4 +specialization is returned. + +@attention This function is more convenient to write than +swizzle(const Vector&), but unless the result is marked with +`constexpr`, the evaluation of the swizzling operation probably won't be +evaluated at compile time, but at runtime. + +@see Vector4::xyz(), Vector4::rgb(), Vector4::xy(), Vector3::xy() +*/ +template inline constexpr typename Implementation::TypeForSize::Type swizzle(const Vector& vector, const char(&components)[newSize]) { + return Implementation::swizzleFrom(typename Implementation::GenerateSequence::Type(), vector, components); +} + }} #endif diff --git a/src/Math/Test/SwizzleTest.cpp b/src/Math/Test/SwizzleTest.cpp index f71cc8d6d..52f590ae8 100644 --- a/src/Math/Test/SwizzleTest.cpp +++ b/src/Math/Test/SwizzleTest.cpp @@ -32,31 +32,48 @@ template using Vector = Math::Vector; SwizzleTest::SwizzleTest() { addTests(&SwizzleTest::xyzw, &SwizzleTest::rgba, + &SwizzleTest::fromSmall, &SwizzleTest::type, &SwizzleTest::defaultType); } void SwizzleTest::xyzw() { Vector4 orig(2, 4, 5, 7); - CORRADE_COMPARE((swizzle<'z', 'x', 'w', 'y'>(orig)), Vector4(5, 2, 7, 4)); + Vector4 swizzled(5, 2, 7, 4); + CORRADE_COMPARE(swizzle(orig, "zxwy"), swizzled); + CORRADE_COMPARE((swizzle<'z', 'x', 'w', 'y'>(orig)), swizzled); } void SwizzleTest::rgba() { Vector4 orig(2, 4, 5, 7); - CORRADE_COMPARE((swizzle<'b', 'r', 'a', 'g'>(orig)), Vector4(5, 2, 7, 4)); + Vector4 swizzled(5, 2, 7, 4); + CORRADE_COMPARE(swizzle(orig, "brag"), swizzled); + CORRADE_COMPARE((swizzle<'b', 'r', 'a', 'g'>(orig)), swizzled); +} + +void SwizzleTest::fromSmall() { + /* Force compile-time evaluation for both */ + constexpr Vector2 orig(1, 2); + CORRADE_VERIFY((integral_constant::value)); + CORRADE_COMPARE((swizzle<'g', 'x', 'r'>(orig)), Vector3(2, 1, 1)); } void SwizzleTest::type() { Vector4 orig; CORRADE_VERIFY((is_same(orig)), Vector2>::value)); + CORRADE_VERIFY((is_same::value)); CORRADE_VERIFY((is_same(orig)), Vector3>::value)); + CORRADE_VERIFY((is_same::value)); CORRADE_VERIFY((is_same(orig)), Vector4>::value)); + CORRADE_VERIFY((is_same::value)); } void SwizzleTest::defaultType() { Vector4 orig(1, 2, 3, 4); CORRADE_COMPARE(swizzle<'b'>(orig), Vector<1>(3)); + CORRADE_COMPARE(swizzle(orig, "b"), Vector<1>(3)); CORRADE_COMPARE((swizzle<'b', 'r', 'a', 'g', 'z', 'y', 'x'>(orig)), Vector<7>(3, 1, 4, 2, 3, 2, 1)); + CORRADE_COMPARE(swizzle(orig, "bragzyx"), Vector<7>(3, 1, 4, 2, 3, 2, 1)); } }}} diff --git a/src/Math/Test/SwizzleTest.h b/src/Math/Test/SwizzleTest.h index f68a05e45..a08dfdd17 100644 --- a/src/Math/Test/SwizzleTest.h +++ b/src/Math/Test/SwizzleTest.h @@ -25,6 +25,7 @@ class SwizzleTest: public Corrade::TestSuite::Tester { void xyzw(); void rgba(); + void fromSmall(); void type(); void defaultType(); }; diff --git a/src/Math/Vector.h b/src/Math/Vector.h index 0a56fa479..7bd884fbc 100644 --- a/src/Math/Vector.h +++ b/src/Math/Vector.h @@ -26,11 +26,21 @@ namespace Magnum { namespace Math { -/** -@brief %Vector +#ifndef DOXYGEN_GENERATING_OUTPUT +namespace Implementation { + template struct Sequence {}; -@todo Swizzling -*/ + /* E.g. GenerateSequence<3>::Type is Sequence<0, 1, 2> */ + template struct GenerateSequence: + GenerateSequence {}; + + template struct GenerateSequence<0, sequence...> { + typedef Sequence Type; + }; +} +#endif + +/** @brief %Vector */ template class Vector { public: const static size_t Size = size; /**< @brief %Vector size */