Browse Source

Animation: make interpolation result type non-implicit.

TrackView can, for example, have quaternions encoded as a 10-10-10-2
integer, but the result should still be a Quaternion.
pull/191/head
Vladimír Vondruš 8 years ago
parent
commit
4d4b299740
  1. 4
      src/Magnum/Animation/Animation.h
  2. 9
      src/Magnum/Animation/Interpolation.h
  3. 33
      src/Magnum/Animation/Test/InterpolationTest.cpp
  4. 41
      src/Magnum/Animation/Test/TrackTest.cpp
  5. 37
      src/Magnum/Animation/Test/TrackViewTest.cpp
  6. 52
      src/Magnum/Animation/Track.h

4
src/Magnum/Animation/Animation.h

@ -41,8 +41,8 @@ template<class V> using ResultOf = typename Implementation::TypeTraits<V>::Resul
enum class Extrapolation: UnsignedByte; enum class Extrapolation: UnsignedByte;
template<class, class> class Track; template<class K, class V, class R = ResultOf<V>> class Track;
template<class, class> class TrackView; template<class K, class V, class R = ResultOf<V>> class TrackView;
}} }}

9
src/Magnum/Animation/Interpolation.h

@ -98,6 +98,7 @@ MAGNUM_EXPORT Debug& operator<<(Debug& debug, Extrapolation value);
@brief Interpolate animation value @brief Interpolate animation value
@tparam K Key type @tparam K Key type
@tparam V Value type @tparam V Value type
@tparam R Result type
@param keys Keys @param keys Keys
@param values Values @param values Values
@param before Extrapolation mode before first keyframe @param before Extrapolation mode before first keyframe
@ -129,7 +130,7 @@ documentation for more information.
@ref Math::slerp(), @ref Math::sclerp() @ref Math::slerp(), @ref Math::sclerp()
@experimental @experimental
*/ */
template<class K, class V> ResultOf<V> interpolate(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, Extrapolation before, Extrapolation after, ResultOf<V>(*interpolator)(const V&, const V&, Float), K frame, std::size_t& hint); template<class K, class V, class R = ResultOf<V>> R interpolate(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, Extrapolation before, Extrapolation after, R(*interpolator)(const V&, const V&, Float), K frame, std::size_t& hint);
/** /**
@brief Interpolate animation value with strict constraints @brief Interpolate animation value with strict constraints
@ -152,9 +153,9 @@ Used internally from @ref Track::atStrict() / @ref TrackView::atStrict(), see
@ref Math::sclerp() @ref Math::sclerp()
@experimental @experimental
*/ */
template<class K, class V> ResultOf<V> interpolateStrict(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, ResultOf<V>(*interpolator)(const V&, const V&, Float), K frame, std::size_t& hint); template<class K, class V, class R = ResultOf<V>> R interpolateStrict(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, R(*interpolator)(const V&, const V&, Float), K frame, std::size_t& hint);
template<class K, class V> ResultOf<V> interpolate(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, const Extrapolation before, const Extrapolation after, ResultOf<V>(*const interpolator)(const V&, const V&, Float), K frame, std::size_t& hint) { template<class K, class V, class R> R interpolate(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, const Extrapolation before, const Extrapolation after, R(*const interpolator)(const V&, const V&, Float), K frame, std::size_t& hint) {
CORRADE_ASSERT(keys.size() == values.size(), "Animation::interpolate(): keys and values don't have the same size", {}); CORRADE_ASSERT(keys.size() == values.size(), "Animation::interpolate(): keys and values don't have the same size", {});
/* No data, return default-constructed value */ /* No data, return default-constructed value */
@ -190,7 +191,7 @@ template<class K, class V> ResultOf<V> interpolate(const Containers::StridedArra
Math::lerpInverted(keys[hint], keys[hint + 1], frame)); Math::lerpInverted(keys[hint], keys[hint + 1], frame));
} }
template<class K, class V> ResultOf<V> interpolateStrict(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, ResultOf<V>(*const interpolator)(const V&, const V&, Float), const K frame, std::size_t& hint) { template<class K, class V, class R> R interpolateStrict(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, R(*const interpolator)(const V&, const V&, Float), const K frame, std::size_t& hint) {
CORRADE_ASSERT(keys.size() >= 2, "Animation::interpolateStrict(): at least two keyframes required", {}); CORRADE_ASSERT(keys.size() >= 2, "Animation::interpolateStrict(): at least two keyframes required", {});
CORRADE_ASSERT(keys.size() == values.size(), "Animation::interpolateStrict(): keys and values don't have the same size", {}); CORRADE_ASSERT(keys.size() == values.size(), "Animation::interpolateStrict(): keys and values don't have the same size", {});

33
src/Magnum/Animation/Test/InterpolationTest.cpp

@ -27,6 +27,7 @@
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include "Magnum/Animation/Interpolation.h" #include "Magnum/Animation/Interpolation.h"
#include "Magnum/Math/Half.h"
namespace Magnum { namespace Animation { namespace Test { namespace Magnum { namespace Animation { namespace Test {
@ -41,6 +42,9 @@ struct InterpolationTest: TestSuite::Tester {
void interpolateHint(); void interpolateHint();
void interpolateStrictHint(); void interpolateStrictHint();
void interpolateDifferentResultType();
void interpolateStrictDifferentResultType();
void interpolateError(); void interpolateError();
void interpolateStrictError(); void interpolateStrictError();
@ -131,7 +135,10 @@ InterpolationTest::InterpolationTest() {
&InterpolationTest::interpolateStrictHint}, &InterpolationTest::interpolateStrictHint},
Containers::arraySize(HintData)); Containers::arraySize(HintData));
addTests({&InterpolationTest::interpolateError, addTests({&InterpolationTest::interpolateDifferentResultType,
&InterpolationTest::interpolateStrictDifferentResultType,
&InterpolationTest::interpolateError,
&InterpolationTest::interpolateStrictError, &InterpolationTest::interpolateStrictError,
&InterpolationTest::debugExtrapolation}); &InterpolationTest::debugExtrapolation});
@ -205,6 +212,30 @@ void InterpolationTest::interpolateStrictHint() {
CORRADE_COMPARE(hint, 2); CORRADE_COMPARE(hint, 2);
} }
namespace {
using namespace Math::Literals;
const Half HalfValues[]{3.0_h, 1.0_h, 2.5_h, 0.5_h};
Float lerpHalf(const Half& a, const Half& b, Float t) {
return Math::lerp(Float(a), Float(b), t);
}
}
void InterpolationTest::interpolateDifferentResultType() {
std::size_t hint{};
CORRADE_COMPARE((Animation::interpolate<Float, Half, Float>(
Keys, HalfValues, Extrapolation::Extrapolated, Extrapolation::Extrapolated, lerpHalf, 4.75f, hint)), 1.0f);
CORRADE_COMPARE(hint, 2);
}
void InterpolationTest::interpolateStrictDifferentResultType() {
std::size_t hint{};
CORRADE_COMPARE((Animation::interpolateStrict<Float, Half, Float>(
Keys, HalfValues, lerpHalf, 4.75f, hint)), 1.0f);
CORRADE_COMPARE(hint, 2);
}
void InterpolationTest::interpolateError() { void InterpolationTest::interpolateError() {
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};

41
src/Magnum/Animation/Test/TrackTest.cpp

@ -26,6 +26,7 @@
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include "Magnum/Animation/Track.h" #include "Magnum/Animation/Track.h"
#include "Magnum/Math/Half.h"
#include "Magnum/Math/Vector3.h" #include "Magnum/Math/Vector3.h"
namespace Magnum { namespace Animation { namespace Test { namespace Magnum { namespace Animation { namespace Test {
@ -42,6 +43,8 @@ struct TrackTest: TestSuite::Tester {
void at(); void at();
void atStrict(); void atStrict();
void atDifferentResultType();
void atDifferentResultTypeStrict();
}; };
namespace { namespace {
@ -93,6 +96,9 @@ TrackTest::TrackTest() {
addInstancedTests({&TrackTest::at, addInstancedTests({&TrackTest::at,
&TrackTest::atStrict}, Containers::arraySize(AtData)); &TrackTest::atStrict}, Containers::arraySize(AtData));
addTests({&TrackTest::atDifferentResultType,
&TrackTest::atDifferentResultTypeStrict});
} }
using namespace Math::Literals; using namespace Math::Literals;
@ -218,6 +224,41 @@ void TrackTest::atStrict() {
CORRADE_COMPARE(hint, data.expectedHint); CORRADE_COMPARE(hint, data.expectedHint);
} }
namespace {
Float lerpHalf(const Half& a, const Half& b, Float t) {
return Math::lerp(Float(a), Float(b), t);
}
}
void TrackTest::atDifferentResultType() {
using namespace Math::Literals;
const Track<Float, Half, Float> a{
{{0.0f, 3.0_h},
{2.0f, 1.0_h},
{4.0f, 2.5_h},
{5.0f, 0.5_h}}, lerpHalf};
std::size_t hint{};
CORRADE_COMPARE(a.at(4.75f, hint), 1.0f);
CORRADE_COMPARE(a.at(4.75f), 1.0f);
CORRADE_COMPARE(hint, 2);
}
void TrackTest::atDifferentResultTypeStrict() {
using namespace Math::Literals;
const Track<Float, Half, Float> a{
{{0.0f, 3.0_h},
{2.0f, 1.0_h},
{4.0f, 2.5_h},
{5.0f, 0.5_h}}, lerpHalf};
std::size_t hint{};
CORRADE_COMPARE(a.atStrict(4.75f, hint), 1.0f);
CORRADE_COMPARE(hint, 2);
}
}}} }}}
CORRADE_TEST_MAIN(Magnum::Animation::Test::TrackTest) CORRADE_TEST_MAIN(Magnum::Animation::Test::TrackTest)

37
src/Magnum/Animation/Test/TrackViewTest.cpp

@ -26,6 +26,7 @@
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include "Magnum/Animation/Track.h" #include "Magnum/Animation/Track.h"
#include "Magnum/Math/Half.h"
#include "Magnum/Math/Vector3.h" #include "Magnum/Math/Vector3.h"
namespace Magnum { namespace Animation { namespace Test { namespace Magnum { namespace Animation { namespace Test {
@ -40,6 +41,8 @@ struct TrackViewTest: TestSuite::Tester {
void at(); void at();
void atStrict(); void atStrict();
void atDifferentResultType();
void atDifferentResultTypeStrict();
}; };
namespace { namespace {
@ -89,6 +92,9 @@ TrackViewTest::TrackViewTest() {
addInstancedTests({&TrackViewTest::at, addInstancedTests({&TrackViewTest::at,
&TrackViewTest::atStrict}, Containers::arraySize(AtData)); &TrackViewTest::atStrict}, Containers::arraySize(AtData));
addTests({&TrackViewTest::atDifferentResultType,
&TrackViewTest::atDifferentResultTypeStrict});
} }
using namespace Math::Literals; using namespace Math::Literals;
@ -188,6 +194,37 @@ void TrackViewTest::atStrict() {
CORRADE_COMPARE(hint, data.expectedHint); CORRADE_COMPARE(hint, data.expectedHint);
} }
namespace {
using namespace Math::Literals;
const Half HalfValues[]{3.0_h, 1.0_h, 2.5_h, 0.5_h};
Float lerpHalf(const Half& a, const Half& b, Float t) {
return Math::lerp(Float(a), Float(b), t);
}
}
void TrackViewTest::atDifferentResultType() {
const TrackView<Float, Half, Float> a{
{&Keyframes[0].first, Containers::arraySize(Keyframes), sizeof(Keyframes[0])},
HalfValues, lerpHalf};
std::size_t hint{};
CORRADE_COMPARE(a.at(4.75f, hint), 1.0f);
CORRADE_COMPARE(a.at(4.75f), 1.0f);
CORRADE_COMPARE(hint, 2);
}
void TrackViewTest::atDifferentResultTypeStrict() {
const TrackView<Float, Half, Float> a{
{&Keyframes[0].first, Containers::arraySize(Keyframes), sizeof(Keyframes[0])},
HalfValues, lerpHalf};
std::size_t hint{};
CORRADE_COMPARE(a.atStrict(4.75f, hint), 1.0f);
CORRADE_COMPARE(hint, 2);
}
}}} }}}
CORRADE_TEST_MAIN(Magnum::Animation::Test::TrackViewTest) CORRADE_TEST_MAIN(Magnum::Animation::Test::TrackViewTest)

52
src/Magnum/Animation/Track.h

@ -40,6 +40,7 @@ namespace Magnum { namespace Animation {
@brief Animation track @brief Animation track
@tparam K Key type @tparam K Key type
@tparam V Value type @tparam V Value type
@tparam R Result type
Immutable storage of keyframe + value pairs. Immutable storage of keyframe + value pairs.
@ -106,7 +107,11 @@ instead of having data duplicated scattered across disjoint allocations of
@experimental @experimental
*/ */
template<class K, class V> class Track { template<class K, class V, class R
#ifdef DOXYGEN_GENERATING_OUTPUT
= ResultOf<V>
#endif
> class Track {
public: public:
/** @brief Key type */ /** @brief Key type */
typedef K KeyType; typedef K KeyType;
@ -115,7 +120,7 @@ template<class K, class V> class Track {
typedef V ValueType; typedef V ValueType;
/** @brief Animation result type */ /** @brief Animation result type */
typedef ResultOf<V> ResultType; typedef R ResultType;
/** @brief Interpolation function */ /** @brief Interpolation function */
typedef ResultType(*Interpolator)(const ValueType&, const ValueType&, Float); typedef ResultType(*Interpolator)(const ValueType&, const ValueType&, Float);
@ -133,32 +138,32 @@ template<class K, class V> class Track {
explicit Track(Containers::Array<std::pair<K, V>>&& data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolator}, _before{before}, _after{after} {} explicit Track(Containers::Array<std::pair<K, V>>&& data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolator}, _before{before}, _after{after} {}
/** @overload */ /** @overload */
explicit Track(std::initializer_list<std::pair<K, V>> data, Interpolator interpolator, Extrapolation before, Extrapolation after): Track<K, V>{Containers::Array<std::pair<K, V>>{Containers::InPlaceInit, data}, interpolator, before, after} {} explicit Track(std::initializer_list<std::pair<K, V>> data, Interpolator interpolator, Extrapolation before, Extrapolation after): Track<K, V, R>{Containers::Array<std::pair<K, V>>{Containers::InPlaceInit, data}, interpolator, before, after} {}
/** @overload /** @overload
* Equivalent to calling @ref Track(Containers::Array<std::pair<K, V>>&&, Interpolator, Extrapolation, Extrapolation) * Equivalent to calling @ref Track(Containers::Array<std::pair<K, V>>&&, Interpolator, Extrapolation, Extrapolation)
* with both @p before and @p after set to @p extrapolation. * with both @p before and @p after set to @p extrapolation.
*/ */
explicit Track(Containers::Array<std::pair<K, V>>&& data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant) noexcept: Track<K, V>{std::move(data), interpolator, extrapolation, extrapolation} {} explicit Track(Containers::Array<std::pair<K, V>>&& data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant) noexcept: Track<K, V, R>{std::move(data), interpolator, extrapolation, extrapolation} {}
/** @overload */ /** @overload */
explicit Track(std::initializer_list<std::pair<K, V>> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant): Track<K, V>{Containers::Array<std::pair<K, V>>{Containers::InPlaceInit, data}, interpolator, extrapolation} {} explicit Track(std::initializer_list<std::pair<K, V>> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant): Track<K, V, R>{Containers::Array<std::pair<K, V>>{Containers::InPlaceInit, data}, interpolator, extrapolation, extrapolation} {}
/** @brief Copying is not allowed */ /** @brief Copying is not allowed */
Track(const Track<K, V>&) = delete; Track(const Track<K, V, R>&) = delete;
/** @brief Move constructor */ /** @brief Move constructor */
Track(Track<K, V>&&) = default; Track(Track<K, V, R>&&) = default;
/** @brief Copying is not allowed */ /** @brief Copying is not allowed */
Track<K, V>& operator=(const Track<K, V>&) = delete; Track<K, V, R>& operator=(const Track<K, V, R>&) = delete;
/** @brief Move constructor */ /** @brief Move constructor */
Track<K, V>& operator=(Track<K, V>&&) = default; Track<K, V, R>& operator=(Track<K, V, R>&&) = default;
/** @brief Conversion to a view */ /** @brief Conversion to a view */
operator TrackView<K, V>() const noexcept { operator TrackView<K, V, R>() const noexcept {
return TrackView<K, V>{_data, _interpolator, _before, _after}; return TrackView<K, V, R>{_data, _interpolator, _before, _after};
} }
/** @brief Interpolation function */ /** @brief Interpolation function */
@ -214,7 +219,7 @@ template<class K, class V> class Track {
* time, use @ref at(K, std::size_t&) const to supply a search hint. * time, use @ref at(K, std::size_t&) const to supply a search hint.
* @see @ref atStrict() * @see @ref atStrict()
*/ */
ResultOf<V> at(K frame) const { R at(K frame) const {
std::size_t hint{}; std::size_t hint{};
return at(frame, hint); return at(frame, hint);
} }
@ -226,7 +231,7 @@ template<class K, class V> class Track {
* information. * information.
* @see @ref at(K) const, @ref atStrict(K, std::size_t&) const * @see @ref at(K) const, @ref atStrict(K, std::size_t&) const
*/ */
ResultOf<V> at(K frame, std::size_t& hint) const { R at(K frame, std::size_t& hint) const {
return interpolate(keys(), values(), _before, _after, _interpolator, frame, hint); return interpolate(keys(), values(), _before, _after, _interpolator, frame, hint);
} }
@ -237,7 +242,7 @@ template<class K, class V> class Track {
* restrictions. Calls @ref interpolateStrict(), see its documentation * restrictions. Calls @ref interpolateStrict(), see its documentation
* for more information. * for more information.
*/ */
ResultOf<V> atStrict(K frame, std::size_t& hint) const { R atStrict(K frame, std::size_t& hint) const {
return interpolateStrict(keys(), values(), _interpolator, frame, hint); return interpolateStrict(keys(), values(), _interpolator, frame, hint);
} }
@ -251,12 +256,17 @@ template<class K, class V> class Track {
@brief Animation track view @brief Animation track view
@tparam K Key type @tparam K Key type
@tparam V Value type @tparam V Value type
@tparam R Result type
Unlike @ref Track this is a non-owning view onto keyframe + value pairs. See Unlike @ref Track this is a non-owning view onto keyframe + value pairs. See
its documentation for more information. its documentation for more information.
@experimental @experimental
*/ */
template<class K, class V> class TrackView { template<class K, class V, class R
#ifdef DOXYGEN_GENERATING_OUTPUT
= ResultOf<V>
#endif
> class TrackView {
public: public:
/** @brief Key type */ /** @brief Key type */
typedef K KeyType; typedef K KeyType;
@ -265,7 +275,7 @@ template<class K, class V> class TrackView {
typedef V ValueType; typedef V ValueType;
/** @brief Animation result type */ /** @brief Animation result type */
typedef ResultOf<V> ResultType; typedef R ResultType;
/** @brief Interpolation function */ /** @brief Interpolation function */
typedef ResultType(*Interpolator)(const ValueType&, const ValueType&, Float); typedef ResultType(*Interpolator)(const ValueType&, const ValueType&, Float);
@ -287,7 +297,7 @@ template<class K, class V> class TrackView {
* Equivalent to calling @ref TrackView(const Containers::StridedArrayView<const K>&, const Containers::StridedArrayView<const V>&, Interpolator, Extrapolation, Extrapolation) * Equivalent to calling @ref TrackView(const Containers::StridedArrayView<const K>&, const Containers::StridedArrayView<const V>&, Interpolator, Extrapolation, Extrapolation)
* with both @p before and @p after set to @p extrapolation. * with both @p before and @p after set to @p extrapolation.
*/ */
constexpr explicit TrackView(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView<K, V>{keys, values, interpolator, extrapolation, extrapolation} {} constexpr explicit TrackView(const Containers::StridedArrayView<const K>& keys, const Containers::StridedArrayView<const V>& values, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView<K, V, R>{keys, values, interpolator, extrapolation, extrapolation} {}
/** /**
* @brief Construct from an interleaved array * @brief Construct from an interleaved array
@ -305,7 +315,7 @@ template<class K, class V> class TrackView {
* Equivalent to calling @ref TrackView(Containers::ArrayView<const std::pair<K, V>>, Interpolator, Extrapolation, Extrapolation) * Equivalent to calling @ref TrackView(Containers::ArrayView<const std::pair<K, V>>, Interpolator, Extrapolation, Extrapolation)
* with both @p before and @p after set to @p extrapolation. * with both @p before and @p after set to @p extrapolation.
*/ */
constexpr explicit TrackView(Containers::ArrayView<const std::pair<K, V>> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView<K, V>{data, interpolator, extrapolation, extrapolation} {} constexpr explicit TrackView(Containers::ArrayView<const std::pair<K, V>> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView<K, V, R>{data, interpolator, extrapolation, extrapolation} {}
/** /**
* @brief Extrapolation behavior before first keyframe * @brief Extrapolation behavior before first keyframe
@ -351,7 +361,7 @@ template<class K, class V> class TrackView {
* time, use @ref at(K, std::size_t&) const to supply a search hint. * time, use @ref at(K, std::size_t&) const to supply a search hint.
* @see @ref atStrict(K, std::size_t&) const * @see @ref atStrict(K, std::size_t&) const
*/ */
ResultOf<V> at(K frame) const { R at(K frame) const {
std::size_t hint{}; std::size_t hint{};
return at(frame, hint); return at(frame, hint);
} }
@ -363,7 +373,7 @@ template<class K, class V> class TrackView {
* information. * information.
* @see @ref at(K) const, @ref atStrict(K, std::size_t&) const * @see @ref at(K) const, @ref atStrict(K, std::size_t&) const
*/ */
ResultOf<V> at(K frame, std::size_t& hint) const { R at(K frame, std::size_t& hint) const {
return interpolate(_keys, _values, _before, _after, _interpolator, frame, hint); return interpolate(_keys, _values, _before, _after, _interpolator, frame, hint);
} }
@ -374,7 +384,7 @@ template<class K, class V> class TrackView {
* restrictions. Calls @ref interpolateStrict(), see its documentation * restrictions. Calls @ref interpolateStrict(), see its documentation
* for more information. * for more information.
*/ */
ResultOf<V> atStrict(K frame, std::size_t& hint) const { R atStrict(K frame, std::size_t& hint) const {
return interpolateStrict(_keys, _values, _interpolator, frame, hint); return interpolateStrict(_keys, _values, _interpolator, frame, hint);
} }

Loading…
Cancel
Save