From b1b663fa65e58360069fabc72c79ed55ac9cd010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 31 Aug 2018 21:23:27 +0200 Subject: [PATCH] Animation: support for spline interpolation. --- src/Magnum/Animation/Interpolation.cpp | 23 ++++- src/Magnum/Animation/Interpolation.h | 31 ++++++- .../Animation/Test/InterpolationTest.cpp | 93 ++++++++++++++++++- src/Magnum/Animation/Track.h | 7 ++ 4 files changed, 146 insertions(+), 8 deletions(-) diff --git a/src/Magnum/Animation/Interpolation.cpp b/src/Magnum/Animation/Interpolation.cpp index fcb4e9c4e..1e38c107f 100644 --- a/src/Magnum/Animation/Interpolation.cpp +++ b/src/Magnum/Animation/Interpolation.cpp @@ -25,7 +25,7 @@ #include "Interpolation.h" -#include "Magnum/Math/Complex.h" +#include "Magnum/Math/CubicHermite.h" #include "Magnum/Math/DualQuaternion.h" namespace Magnum { namespace Animation { @@ -37,6 +37,7 @@ Debug& operator<<(Debug& debug, const Interpolation value) { #define _c(value) case Interpolation::value: return debug << "Animation::Interpolation::" #value; _c(Constant) _c(Linear) + _c(Spline) _c(Custom) #undef _c /* LCOV_EXCL_STOP */ @@ -67,6 +68,7 @@ template auto TypeTraits, Math::Complex>::interpola case Interpolation::Constant: return Math::select; case Interpolation::Linear: return Math::slerp; + case Interpolation::Spline: case Interpolation::Custom: ; /* nope */ } @@ -78,6 +80,7 @@ template auto TypeTraits, Math::Quaternion>::int case Interpolation::Constant: return Math::select; case Interpolation::Linear: return Math::slerp; + case Interpolation::Spline: case Interpolation::Custom: ; /* nope */ } @@ -89,6 +92,19 @@ template auto TypeTraits, Math::DualQuaternion< case Interpolation::Constant: return Math::select; case Interpolation::Linear: return Math::sclerp; + case Interpolation::Spline: + case Interpolation::Custom: ; /* nope */ + } + + CORRADE_ASSERT(false, "Animation::interpolatorFor(): can't deduce interpolator function for" << interpolation, {}); +} + +template auto TypeTraits, T>::interpolator(Interpolation interpolation) -> Interpolator { + switch(interpolation) { + case Interpolation::Constant: return Math::select; + case Interpolation::Linear: return Math::lerp; + case Interpolation::Spline: return Math::splerp; + case Interpolation::Custom: ; /* nope */ } @@ -98,6 +114,11 @@ template auto TypeTraits, Math::DualQuaternion< template struct MAGNUM_EXPORT TypeTraits, Math::Complex>; template struct MAGNUM_EXPORT TypeTraits, Math::Quaternion>; template struct MAGNUM_EXPORT TypeTraits, Math::DualQuaternion>; +template struct MAGNUM_EXPORT TypeTraits, Float>; +template struct MAGNUM_EXPORT TypeTraits>, Math::Vector2>; +template struct MAGNUM_EXPORT TypeTraits>, Math::Vector3>; +template struct MAGNUM_EXPORT TypeTraits>, Math::Complex>; +template struct MAGNUM_EXPORT TypeTraits>, Math::Quaternion>; } diff --git a/src/Magnum/Animation/Interpolation.h b/src/Magnum/Animation/Interpolation.h index cc946f326..2e71d96f7 100644 --- a/src/Magnum/Animation/Interpolation.h +++ b/src/Magnum/Animation/Interpolation.h @@ -60,6 +60,13 @@ enum class Interpolation: UnsignedByte { */ Linear, + /** + * Spline interpolation. + * + * @see @ref Math::splerp() + */ + Spline, + /** * Custom interpolation. An user-supplied interpolation function should be * used. @@ -74,8 +81,9 @@ MAGNUM_EXPORT Debug& operator<<(Debug& debug, Interpolation value); @brief Animation result type for given value type Result of interpolating two `V` values (for example interpolating two -@ref Color3 values gives back a @ref Color3 again, but interpolating a spline -does not result in a spline). +@ref Color3 values gives back a @ref Color3 again, but interpolating a +@ref Magnum::CubicHermite2D "CubicHermite2D" spline results in +@ref Magnum::Vector2 "Vector2"). @experimental */ #ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */ @@ -96,6 +104,7 @@ faster but less precise results. Interpolation type | Value type | Result type | Interpolator ------------------- | ----------------- | ------------- | ------------ @ref Interpolation::Constant | any `V` | `V` | @ref Math::select() +@ref Interpolation::Constant | @ref Math::CubicHermite "Math::CubicHermite" | `T` | @ref Math::select(const CubicHermite&, const CubicHermite&, U) "Math::select()" @ref Interpolation::Linear | @cpp bool @ce | @cpp bool @ce | @ref Math::select() @ref Interpolation::Linear | @ref Math::BoolVector | @ref Math::BoolVector | @ref Math::select() @ref Interpolation::Linear | any scalar `V` | `V` | @ref Math::lerp() @@ -103,6 +112,12 @@ Interpolation type | Value type | Result type | Interpolator @ref Interpolation::Linear | @ref Math::Complex | @ref Math::Complex | @ref Math::slerp(const Complex&, const Complex&, T) "Math::slerp()" @ref Interpolation::Linear | @ref Math::Quaternion | @ref Math::Quaternion | @ref Math::slerp(const Quaternion&, const Quaternion&, T) "Math::slerp()" @ref Interpolation::Linear | @ref Math::DualQuaternion | @ref Math::DualQuaternion | @ref Math::sclerp(const DualQuaternion&, const DualQuaternion&, T) "Math::sclerp()" +@ref Interpolation::Linear | @ref Math::CubicHermite "Math::CubicHermite" | `T` | @ref Math::lerp(const CubicHermite&, const CubicHermite&, U) "Math::lerp()" +@ref Interpolation::Linear | @ref Math::CubicHermiteComplex | @ref Math::Complex | @ref Math::lerp(const CubicHermiteComplex&, const CubicHermiteComplex&, T) "Math::lerp()" +@ref Interpolation::Linear | @ref Math::CubicHermiteQuaternion | @ref Math::Quaternion | @ref Math::lerp(const CubicHermiteQuaternion&, const CubicHermiteQuaternion&, T) "Math::lerp()" +@ref Interpolation::Spline | @ref Math::CubicHermite "Math::CubicHermite" | `T` | @ref Math::splerp(const CubicHermite&, const CubicHermite&, U) "Math::splerp()" +@ref Interpolation::Spline | @ref Math::CubicHermiteComplex | @ref Math::Complex | @ref Math::splerp(const CubicHermiteComplex&, const CubicHermiteComplex&, T) "Math::splerp()" +@ref Interpolation::Spline | @ref Math::CubicHermiteQuaternion | @ref Math::Quaternion | @ref Math::splerp(const CubicHermiteQuaternion&, const CubicHermiteQuaternion&, T) "Math::splerp()" @see @ref interpolate(), @ref interpolateStrict() @experimental @@ -213,6 +228,9 @@ namespace Implementation { template struct ResultTraits { typedef V Type; }; +template struct ResultTraits> { + typedef T Type; +}; template struct TypeTraits { typedef V(*Interpolator)(const V&, const V&, Float); @@ -223,6 +241,7 @@ template auto TypeTraits::interpolator(Interpolation interpolatio case Interpolation::Constant: return Math::select; case Interpolation::Linear: return Math::lerp; + case Interpolation::Spline: case Interpolation::Custom: ; /* nope */ } @@ -240,6 +259,7 @@ template auto TypeTraitsBool::interpolator(Interpolation interpolati case Interpolation::Constant: case Interpolation::Linear: return Math::select; + case Interpolation::Spline: case Interpolation::Custom: ; /* nope */ } @@ -267,6 +287,13 @@ template struct MAGNUM_EXPORT TypeTraits, Math: static Interpolator interpolator(Interpolation interpolation); }; +/* Cubic Hermite spline point has a different result type */ +template struct MAGNUM_EXPORT TypeTraits, T> { + typedef T(*Interpolator)(const Math::CubicHermite&, const Math::CubicHermite&, Float); + + static Interpolator interpolator(Interpolation interpolation); +}; + } /* Needs to be defined later so it can pick up the TypeTraits definitions */ diff --git a/src/Magnum/Animation/Test/InterpolationTest.cpp b/src/Magnum/Animation/Test/InterpolationTest.cpp index 2e5a4aae4..b9406ad15 100644 --- a/src/Magnum/Animation/Test/InterpolationTest.cpp +++ b/src/Magnum/Animation/Test/InterpolationTest.cpp @@ -28,6 +28,7 @@ #include "Magnum/Animation/Interpolation.h" #include "Magnum/Math/Complex.h" +#include "Magnum/Math/CubicHermite.h" #include "Magnum/Math/DualQuaternion.h" #include "Magnum/Math/Half.h" @@ -42,6 +43,10 @@ struct InterpolationTest: TestSuite::Tester { void interpolatorForComplex(); void interpolatorForQuaternion(); void interpolatorForDualQuaternion(); + void interpolatorForCubicHermiteScalar(); + void interpolatorForCubicHermiteVector(); + void interpolatorForCubicHermiteComplex(); + void interpolatorForCubicHermiteQuaternion(); void interpolate(); void interpolateStrict(); @@ -142,7 +147,11 @@ InterpolationTest::InterpolationTest() { &InterpolationTest::interpolatorForBoolVector, &InterpolationTest::interpolatorForComplex, &InterpolationTest::interpolatorForQuaternion, - &InterpolationTest::interpolatorForDualQuaternion}); + &InterpolationTest::interpolatorForDualQuaternion, + &InterpolationTest::interpolatorForCubicHermiteScalar, + &InterpolationTest::interpolatorForCubicHermiteVector, + &InterpolationTest::interpolatorForCubicHermiteComplex, + &InterpolationTest::interpolatorForCubicHermiteQuaternion}); addInstancedTests({&InterpolationTest::interpolate, &InterpolationTest::interpolateStrict}, @@ -178,11 +187,11 @@ void InterpolationTest::interpolatorFor() { std::ostringstream out; Error redirectError{&out}; - Animation::interpolatorFor(Interpolation::Custom); + Animation::interpolatorFor(Interpolation::Spline); Animation::interpolatorFor(Interpolation(0xde)); CORRADE_COMPARE(out.str(), - "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation::Custom\n" + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation::Spline\n" "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation(0xde)\n"); } @@ -250,11 +259,11 @@ void InterpolationTest::interpolatorForQuaternion() { std::ostringstream out; Error redirectError{&out}; - Animation::interpolatorFor(Interpolation::Custom); + Animation::interpolatorFor(Interpolation::Spline); Animation::interpolatorFor(Interpolation(0xde)); CORRADE_COMPARE(out.str(), - "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation::Custom\n" + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation::Spline\n" "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation(0xde)\n"); } @@ -278,6 +287,80 @@ void InterpolationTest::interpolatorForDualQuaternion() { "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation(0xde)\n"); } +void InterpolationTest::interpolatorForCubicHermiteScalar() { + CubicHermite1D a{2.0f, 3.0f, -1.0f}; + CubicHermite1D b{5.0f, -2.0f, 1.5f}; + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Constant)(a, b, 0.8f), 3.0f); + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Linear)(a, b, 0.8f), -1.0f); + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Spline)(a, b, 0.8f), -2.152f); + + std::ostringstream out; + Error redirectError{&out}; + Animation::interpolatorFor(Interpolation::Custom); + Animation::interpolatorFor(Interpolation(0xde)); + + CORRADE_COMPARE(out.str(), + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation::Custom\n" + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation(0xde)\n"); +} + +void InterpolationTest::interpolatorForCubicHermiteVector() { + CubicHermite2D a{{2.0f, 1.5f}, {3.0f, 0.1f}, {-1.0f, 0.0f}}; + CubicHermite2D b{{5.0f, 0.3f}, {-2.0f, 1.1f}, {1.5f, 0.3f}}; + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Constant)(a, b, 0.8f), (Vector2{3.0f, 0.1f})); + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Linear)(a, b, 0.8f), (Vector2{-1.0f, 0.9f})); + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Spline)(a, b, 0.8f), (Vector2{-2.152f, 0.9576f})); + + std::ostringstream out; + Error redirectError{&out}; + Animation::interpolatorFor(Interpolation::Custom); + Animation::interpolatorFor(Interpolation(0xde)); + + CORRADE_COMPARE(out.str(), + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation::Custom\n" + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation(0xde)\n"); +} + +void InterpolationTest::interpolatorForCubicHermiteComplex() { + CubicHermiteComplex a{{2.0f, 1.5f}, {0.999445f, 0.0333148f}, {-1.0f, 0.0f}}; + CubicHermiteComplex b{{5.0f, 0.3f}, {-0.876216f, 0.481919f}, {1.5f, 0.3f}}; + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Constant)(a, b, 0.8f), (Complex{0.999445f, 0.0333148f})); + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Linear)(a, b, 0.8f), (Complex{-0.78747f, 0.616353f})); + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Spline)(a, b, 0.8f), (Complex{-0.95958f, 0.281435f})); + + std::ostringstream out; + Error redirectError{&out}; + Animation::interpolatorFor(Interpolation::Custom); + Animation::interpolatorFor(Interpolation(0xde)); + + CORRADE_COMPARE(out.str(), + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation::Custom\n" + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation(0xde)\n"); +} + +void InterpolationTest::interpolatorForCubicHermiteQuaternion() { + CubicHermiteQuaternion a{ + {{2.0f, 1.5f, 0.3f}, 1.1f}, + {{0.780076f, 0.0260025f, 0.598059f}, 0.182018f}, + {{-1.0f, 0.0f, 0.3f}, 0.4f}}; + CubicHermiteQuaternion b{ + {{5.0f, 0.3f, 1.1f}, 0.5f}, + {{-0.711568f, 0.391362f, 0.355784f}, 0.462519f}, + {{1.5f, 0.3f, 17.0f}, -7.0f}}; + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Constant)(a, b, 0.8f), (Quaternion{{0.780076f, 0.0260025f, 0.598059f}, 0.182018f})); + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Linear)(a, b, 0.8f), (Quaternion{{-0.533196f, 0.410685f, 0.521583f}, 0.524396f})); + CORRADE_COMPARE(Animation::interpolatorFor(Interpolation::Spline)(a, b, 0.8f), (Quaternion{{-0.911408f, 0.23368f, 0.185318f}, 0.283524f})); + + std::ostringstream out; + Error redirectError{&out}; + Animation::interpolatorFor(Interpolation::Custom); + Animation::interpolatorFor(Interpolation(0xde)); + + CORRADE_COMPARE(out.str(), + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation::Custom\n" + "Animation::interpolatorFor(): can't deduce interpolator function for Animation::Interpolation(0xde)\n"); +} + namespace { constexpr Float Keys[]{0.0f, 2.0f, 4.0f, 5.0f}; constexpr Float Values[]{3.0f, 1.0f, 2.5f, 0.5f}; diff --git a/src/Magnum/Animation/Track.h b/src/Magnum/Animation/Track.h index a7fa77b20..e271b71cd 100644 --- a/src/Magnum/Animation/Track.h +++ b/src/Magnum/Animation/Track.h @@ -63,15 +63,22 @@ are common combinations: Interpolation type | Value type | Result type | Interpolator ------------------- | ----------------- | ------------- | ------------ Constant | any `V` | `V` | @ref Math::select() +Constant | @ref Math::CubicHermite "Math::CubicHermite" | `T` | @ref Math::select(const CubicHermite&, const CubicHermite&, U) "Math::select()" Linear | @cpp bool @ce | @cpp bool @ce | @ref Math::select() Linear | @ref Math::BoolVector | @ref Math::BoolVector | @ref Math::select() Linear | any scalar `V` | `V` | @ref Math::lerp() Linear | any vector `V` | `V` | @ref Math::lerp() Linear | @ref Math::Complex | @ref Math::Complex | @ref Math::lerp(const Complex&, const Complex&, T) "Math::lerp()" Linear | @ref Math::Quaternion | @ref Math::Quaternion | @ref Math::lerp(const Quaternion&, const Quaternion&, T) "Math::lerp()" +Linear | @ref Math::CubicHermite "Math::CubicHermite" | `T` | @ref Math::lerp(const CubicHermite&, const CubicHermite&, U) "Math::lerp()" +Linear | @ref Math::CubicHermiteComplex | @ref Math::Complex | @ref Math::lerp(const CubicHermiteComplex&, const CubicHermiteComplex&, T) "Math::lerp()" +Linear | @ref Math::CubicHermiteQuaternion | @ref Math::Quaternion | @ref Math::lerp(const CubicHermiteQuaternion&, const CubicHermiteQuaternion&, T) "Math::lerp()" Spherical linear | @ref Math::Complex | @ref Math::Complex | @ref Math::slerp(const Complex&, const Complex&, T) "Math::slerp()" Spherical linear | @ref Math::Quaternion | @ref Math::Quaternion | @ref Math::slerp(const Quaternion&, const Quaternion&, T) "Math::slerp()" Screw linear | @ref Math::DualQuaternion | @ref Math::DualQuaternion | @ref Math::sclerp(const DualQuaternion&, const DualQuaternion&, T) "Math::sclerp()" +Spline | @ref Math::CubicHermite "Math::CubicHermite" | `T` | @ref Math::splerp(const CubicHermite&, const CubicHermite&, U) "Math::splerp()" +Spline | @ref Math::CubicHermiteComplex | @ref Math::Complex | @ref Math::splerp(const CubicHermiteComplex&, const CubicHermiteComplex&, T) "Math::splerp()" +Spline | @ref Math::CubicHermiteQuaternion | @ref Math::Quaternion | @ref Math::splerp(const CubicHermiteQuaternion&, const CubicHermiteQuaternion&, T) "Math::splerp()" It's also possible to supply a generic interpolation behavior by passing the @ref Interpolation enum to the constructor. In case the interpolator function