mirror of https://github.com/mosra/magnum.git
9 changed files with 638 additions and 1 deletions
@ -0,0 +1,46 @@ |
|||||||
|
#ifndef Magnum_Animation_Animation_h |
||||||
|
#define Magnum_Animation_Animation_h |
||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Forward declarations for @ref Magnum::Animation namespace |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Magnum/Types.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Animation { |
||||||
|
|
||||||
|
namespace Implementation { |
||||||
|
template<class> struct TypeTraits; |
||||||
|
} |
||||||
|
|
||||||
|
template<class V> using ResultOf = typename Implementation::TypeTraits<V>::ResultType; |
||||||
|
|
||||||
|
enum class Extrapolation: UnsignedByte; |
||||||
|
|
||||||
|
}} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,38 @@ |
|||||||
|
# |
||||||
|
# This file is part of Magnum. |
||||||
|
# |
||||||
|
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
# Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
# |
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
# copy of this software and associated documentation files (the "Software"), |
||||||
|
# to deal in the Software without restriction, including without limitation |
||||||
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
# and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
# Software is furnished to do so, subject to the following conditions: |
||||||
|
# |
||||||
|
# The above copyright notice and this permission notice shall be included |
||||||
|
# in all copies or substantial portions of the Software. |
||||||
|
# |
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
# DEALINGS IN THE SOFTWARE. |
||||||
|
# |
||||||
|
|
||||||
|
set(MagnumAnimation_HEADERS |
||||||
|
Animation.h |
||||||
|
Interpolation.h) |
||||||
|
|
||||||
|
# Force IDEs to display all header files in project view |
||||||
|
add_custom_target(MagnumAnimation SOURCES ${MagnumAnimation_HEADERS}) |
||||||
|
set_target_properties(MagnumAnimation PROPERTIES FOLDER "Magnum/Animation") |
||||||
|
|
||||||
|
install(FILES ${MagnumAnimation_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Animation) |
||||||
|
|
||||||
|
if(BUILD_TESTS) |
||||||
|
add_subdirectory(Test) |
||||||
|
endif() |
||||||
@ -0,0 +1,46 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Interpolation.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Animation { |
||||||
|
|
||||||
|
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||||
|
Debug& operator<<(Debug& debug, const Extrapolation value) { |
||||||
|
switch(value) { |
||||||
|
/* LCOV_EXCL_START */ |
||||||
|
#define _c(value) case Extrapolation::value: return debug << "Animation::Extrapolation::" #value; |
||||||
|
_c(DefaultConstructed) |
||||||
|
_c(Constant) |
||||||
|
_c(Extrapolated) |
||||||
|
#undef _c |
||||||
|
/* LCOV_EXCL_STOP */ |
||||||
|
} |
||||||
|
|
||||||
|
return debug << "Animation::Extrapolation(" << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << ")"; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
}} |
||||||
@ -0,0 +1,201 @@ |
|||||||
|
#ifndef Magnum_Animation_Interpolation_h |
||||||
|
#define Magnum_Animation_Interpolation_h |
||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Alias @ref Magnum::Animation::ResultOf, enum @ref Magnum::Animation::Extrapolation, function @ref Magnum::Animation::interpolate(), @ref Magnum::Animation::interpolateStrict() |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Corrade/Containers/StridedArrayView.h> |
||||||
|
|
||||||
|
#include "Magnum/Magnum.h" |
||||||
|
#include "Magnum/Math/Functions.h" |
||||||
|
#ifdef CORRADE_MSVC2015_COMPATIBILITY |
||||||
|
#include "Magnum/Animation/Animation.h" /* ResultOf alias on MSVC 2015 */ |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace Magnum { namespace Animation { |
||||||
|
|
||||||
|
namespace Implementation { |
||||||
|
template<class T> struct TypeTraits { |
||||||
|
typedef T ResultType; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
@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). |
||||||
|
@experimental |
||||||
|
*/ |
||||||
|
#ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */ |
||||||
|
template<class V> using ResultOf = typename Implementation::TypeTraits<V>::ResultType; |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Animation extrapolation behavior |
||||||
|
|
||||||
|
Describes what value is returned for frames outside of keyframe range for given |
||||||
|
track (frame lower than first keyframe or frame larger or equal to last |
||||||
|
keyframe). |
||||||
|
@see @ref interpolate() |
||||||
|
@experimental |
||||||
|
*/ |
||||||
|
enum class Extrapolation: UnsignedByte { |
||||||
|
/**
|
||||||
|
* Values of first two / last two keyframes are extrapolated. In case |
||||||
|
* there is only one keyframe, it's passed to both inputs of the |
||||||
|
* interpolator. Implicit behavior in @ref interpolateStrict(). |
||||||
|
*/ |
||||||
|
Extrapolated, |
||||||
|
|
||||||
|
/**
|
||||||
|
* Value of first/last keyframe is used. In other words, for the first |
||||||
|
* keyframe the interpolator is called with first two keyframes and |
||||||
|
* interpolation factor set to `0.0f`; for the last keyframe the |
||||||
|
* interpolator is called with last two keyframes and interpolation factor |
||||||
|
* set to `1.0f`. In case there is only one keyframe, it's passed to both |
||||||
|
* inputs of the interpolator. |
||||||
|
*/ |
||||||
|
Constant, |
||||||
|
|
||||||
|
/** Default-constructed value is returned. */ |
||||||
|
DefaultConstructed, |
||||||
|
|
||||||
|
/** @todo repeat? that would duplicate the play count feature though */ |
||||||
|
}; |
||||||
|
|
||||||
|
/** @debugoperatorenum{Extrapolation} */ |
||||||
|
MAGNUM_EXPORT Debug& operator<<(Debug& debug, Extrapolation value); |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Interpolate animation value |
||||||
|
@tparam K Key type |
||||||
|
@tparam V Value type |
||||||
|
@param keys Keys |
||||||
|
@param values Values |
||||||
|
@param before Extrapolation mode before first keyframe |
||||||
|
@param after Extrapolation mode after last keyframe |
||||||
|
@param interpolator Interpolator function |
||||||
|
@param frame Frame at which to interpolate |
||||||
|
@param hint Hint for keyframe search |
||||||
|
|
||||||
|
Does a linear search over the keyframes until it finds last keyframe which is |
||||||
|
not larger than @p frame. Once the keyframe is found, reference to it and the immediately following keyframe is passed to @p interpolator along with |
||||||
|
calculated interpolation factor, returning the interpolated value. |
||||||
|
|
||||||
|
- In case the first keyframe is already larger than @p frame or @p frame is |
||||||
|
larger or equal to the last keyframe, either the first two or last two |
||||||
|
keyframes are used and value is extrapolated according to @p before / |
||||||
|
@p after. |
||||||
|
- In case only one keyframe is present, its value is used for both sides of |
||||||
|
the interpolator. |
||||||
|
- In case no keyframes are present, default-constructed value is returned. |
||||||
|
|
||||||
|
The @p hint parameter hints where to start the linear search and is updated |
||||||
|
with keyframe index matching @p frame. If @p frame is earlier than @p hint, the |
||||||
|
search is restarted from the beginning. |
||||||
|
@see @ref interpolateStrict(), @ref Math::select(), @ref Math::lerp(), |
||||||
|
@ref Math::slerp(), @ref Math::sclerp() |
||||||
|
@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); |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Interpolate animation value with strict constraints |
||||||
|
|
||||||
|
Does a linear search over the keyframes until it finds last keyframe which is |
||||||
|
not larger than @p frame. Once the keyframe is found, reference to it and the immediately following keyframe is passed to @p interpolator along with |
||||||
|
calculated interpolation factor, returning the interpolated value. The @p hint |
||||||
|
parameter hints where to start the linear search and is updated with keyframe |
||||||
|
index matching @p frame. If @p frame is earlier than @p hint, the search is |
||||||
|
restarted from the beginning. |
||||||
|
|
||||||
|
This is a stricter but more performant version of @ref interpolate() with |
||||||
|
implicit @ref Extrapolation::Extrapolated behavior. Expects that there are |
||||||
|
always at least two keyframes. |
||||||
|
@see @ref Math::select(), @ref Math::lerp(), @ref Math::slerp(), |
||||||
|
@ref Math::sclerp() |
||||||
|
@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> 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) { |
||||||
|
CORRADE_ASSERT(keys.size() == values.size(), "Animation::interpolate(): keys and values don't have the same size", {}); |
||||||
|
|
||||||
|
/* No data, return default-constructed value */ |
||||||
|
if(!keys.size()) return {}; |
||||||
|
|
||||||
|
/* Only one frame, return it verbatim (or default-constructed, if desired) */ |
||||||
|
if(keys.size() == 1) { |
||||||
|
if((frame < keys[0] && before == Extrapolation::DefaultConstructed) || |
||||||
|
(frame > keys[0] && after == Extrapolation::DefaultConstructed)) |
||||||
|
return {}; |
||||||
|
|
||||||
|
return interpolator(values[0], values[0], 0.0f); |
||||||
|
} |
||||||
|
|
||||||
|
/* Rewind from the beginning if hint is too late */ |
||||||
|
if(hint >= keys.size() || frame < keys[hint]) hint = 0; |
||||||
|
|
||||||
|
/* Go through the keys until we find a pair that is around given time */ |
||||||
|
while(hint + 2 < keys.size() && frame >= keys[hint + 1]) |
||||||
|
++hint; |
||||||
|
|
||||||
|
/* Special extrapolation outside of range. Usual extrapolation is handled
|
||||||
|
below. */ |
||||||
|
if(frame < keys[hint]) { |
||||||
|
if(before == Extrapolation::DefaultConstructed) return {}; |
||||||
|
if(before == Extrapolation::Constant) frame = keys[hint]; |
||||||
|
} else if(frame >= keys[hint + 1]) { |
||||||
|
if(after == Extrapolation::DefaultConstructed) return {}; |
||||||
|
if(after == Extrapolation::Constant) frame = keys[hint + 1]; |
||||||
|
} |
||||||
|
|
||||||
|
return interpolator(values[hint], values[hint + 1], |
||||||
|
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) { |
||||||
|
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", {}); |
||||||
|
|
||||||
|
/* Rewind from the beginning if hint is too late */ |
||||||
|
if(hint >= keys.size() || frame < keys[hint]) hint = 0; |
||||||
|
|
||||||
|
/* Go through the keys until we find a pair that is around given time */ |
||||||
|
while(hint + 2 < keys.size() && frame >= keys[hint + 1]) |
||||||
|
++hint; |
||||||
|
|
||||||
|
return interpolator(values[hint], values[hint + 1], |
||||||
|
Math::lerpInverted(keys[hint], keys[hint + 1], frame)); |
||||||
|
} |
||||||
|
|
||||||
|
}} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
# |
||||||
|
# This file is part of Magnum. |
||||||
|
# |
||||||
|
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
# Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
# |
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
# copy of this software and associated documentation files (the "Software"), |
||||||
|
# to deal in the Software without restriction, including without limitation |
||||||
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
# and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
# Software is furnished to do so, subject to the following conditions: |
||||||
|
# |
||||||
|
# The above copyright notice and this permission notice shall be included |
||||||
|
# in all copies or substantial portions of the Software. |
||||||
|
# |
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
# DEALINGS IN THE SOFTWARE. |
||||||
|
# |
||||||
|
|
||||||
|
corrade_add_test(AnimationInterpolationTest InterpolationTest.cpp LIBRARIES Magnum) |
||||||
|
|
||||||
|
set_property(TARGET |
||||||
|
AnimationInterpolationTest |
||||||
|
APPEND PROPERTY COMPILE_DEFINITIONS "CORRADE_GRACEFUL_ASSERT") |
||||||
|
|
||||||
|
set_target_properties( |
||||||
|
AnimationInterpolationTest |
||||||
|
PROPERTIES FOLDER "Magnum/Animation/Test") |
||||||
@ -0,0 +1,252 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <sstream> |
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
|
||||||
|
#include "Magnum/Animation/Interpolation.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Animation { namespace Test { |
||||||
|
|
||||||
|
struct InterpolationTest: TestSuite::Tester { |
||||||
|
explicit InterpolationTest(); |
||||||
|
|
||||||
|
void interpolate(); |
||||||
|
void interpolateStrict(); |
||||||
|
void interpolateSingleKeyframe(); |
||||||
|
void interpolateNoKeyframe(); |
||||||
|
|
||||||
|
void interpolateHint(); |
||||||
|
void interpolateStrictHint(); |
||||||
|
|
||||||
|
void interpolateError(); |
||||||
|
void interpolateStrictError(); |
||||||
|
|
||||||
|
void debugExtrapolation(); |
||||||
|
}; |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
const struct { |
||||||
|
const char* name; |
||||||
|
Extrapolation extrapolationBefore; |
||||||
|
Extrapolation extrapolationAfter; |
||||||
|
Float time; |
||||||
|
Float expectedValue, expectedValueStrict; |
||||||
|
std::size_t expectedHint; |
||||||
|
} Data[] { |
||||||
|
{"before default-constructed", |
||||||
|
Extrapolation::DefaultConstructed, Extrapolation::Extrapolated, |
||||||
|
-1.0f, 0.0f, 4.0f, 0}, |
||||||
|
{"before constant", |
||||||
|
Extrapolation::Constant, Extrapolation::Extrapolated, |
||||||
|
-1.0f, 3.0f, 4.0f, 0}, |
||||||
|
{"before extrapolated", |
||||||
|
Extrapolation::Extrapolated, Extrapolation::DefaultConstructed, |
||||||
|
-1.0f, 4.0f, 4.0f, 0}, |
||||||
|
{"during first", |
||||||
|
Extrapolation::DefaultConstructed, Extrapolation::DefaultConstructed, |
||||||
|
1.5f, 1.5f, 1.5f, 0}, |
||||||
|
{"during second", |
||||||
|
Extrapolation::DefaultConstructed, Extrapolation::DefaultConstructed, |
||||||
|
4.75f, 1.0f, 1.0f, 2}, |
||||||
|
{"after default-constructed", |
||||||
|
Extrapolation::Extrapolated, Extrapolation::DefaultConstructed, |
||||||
|
6.0f, 0.0f, -1.5f, 2}, |
||||||
|
{"after constant", |
||||||
|
Extrapolation::Extrapolated, Extrapolation::Constant, |
||||||
|
6.0f, 0.5f, -1.5f, 2}, |
||||||
|
{"after extrapolated", |
||||||
|
Extrapolation::DefaultConstructed, Extrapolation::Extrapolated, |
||||||
|
6.0f, -1.5f, -1.5f, 2} |
||||||
|
}; |
||||||
|
|
||||||
|
const struct { |
||||||
|
const char* name; |
||||||
|
Extrapolation extrapolation; |
||||||
|
Float time; |
||||||
|
Float expectedValue; |
||||||
|
} SingleKeyframeData[] { |
||||||
|
{"before default-constructed", |
||||||
|
Extrapolation::DefaultConstructed, -1.0f, 0.0f}, |
||||||
|
{"before constant", |
||||||
|
Extrapolation::Constant, -1.0f, 3.0f}, |
||||||
|
{"before extrapolated", |
||||||
|
Extrapolation::Extrapolated, -1.0f, 3.0f}, |
||||||
|
{"at", |
||||||
|
Extrapolation::DefaultConstructed, 0.0f, 3.0f}, |
||||||
|
{"after default-constructed", |
||||||
|
Extrapolation::DefaultConstructed, 1.0f, 0.0f}, |
||||||
|
{"after constant", |
||||||
|
Extrapolation::Constant, 1.0f, 3.0f}, |
||||||
|
{"after extrapolated", |
||||||
|
Extrapolation::Extrapolated, 1.0f, 3.0f} |
||||||
|
}; |
||||||
|
|
||||||
|
const struct { |
||||||
|
const char* name; |
||||||
|
std::size_t hint; |
||||||
|
} HintData[] { |
||||||
|
{"before", 1}, |
||||||
|
{"at", 2}, |
||||||
|
{"after", 3}, |
||||||
|
{"out of bounds", 405780454} |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
InterpolationTest::InterpolationTest() { |
||||||
|
addInstancedTests({&InterpolationTest::interpolate, |
||||||
|
&InterpolationTest::interpolateStrict}, |
||||||
|
Containers::arraySize(Data)); |
||||||
|
|
||||||
|
addInstancedTests({&InterpolationTest::interpolateSingleKeyframe}, |
||||||
|
Containers::arraySize(SingleKeyframeData)); |
||||||
|
|
||||||
|
addTests({&InterpolationTest::interpolateNoKeyframe}); |
||||||
|
|
||||||
|
addInstancedTests({&InterpolationTest::interpolateHint, |
||||||
|
&InterpolationTest::interpolateStrictHint}, |
||||||
|
Containers::arraySize(HintData)); |
||||||
|
|
||||||
|
addTests({&InterpolationTest::interpolateError, |
||||||
|
&InterpolationTest::interpolateStrictError, |
||||||
|
|
||||||
|
&InterpolationTest::debugExtrapolation}); |
||||||
|
} |
||||||
|
|
||||||
|
namespace { |
||||||
|
constexpr Float Keys[]{0.0f, 2.0f, 4.0f, 5.0f}; |
||||||
|
constexpr Float Values[]{3.0f, 1.0f, 2.5f, 0.5f}; |
||||||
|
} |
||||||
|
|
||||||
|
void InterpolationTest::interpolate() { |
||||||
|
const auto& data = Data[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
std::size_t hint{}; |
||||||
|
CORRADE_COMPARE((Animation::interpolate<Float, Float>( |
||||||
|
Keys, Values, data.extrapolationBefore, data.extrapolationAfter, |
||||||
|
Math::lerp, data.time, hint)), data.expectedValue); |
||||||
|
CORRADE_COMPARE(hint, data.expectedHint); |
||||||
|
} |
||||||
|
|
||||||
|
void InterpolationTest::interpolateStrict() { |
||||||
|
const auto& data = Data[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
std::size_t hint{}; |
||||||
|
CORRADE_COMPARE((Animation::interpolateStrict<Float, Float>( |
||||||
|
Keys, Values, Math::lerp, data.time, hint)), data.expectedValueStrict); |
||||||
|
CORRADE_COMPARE(hint, data.expectedHint); |
||||||
|
} |
||||||
|
|
||||||
|
void InterpolationTest::interpolateSingleKeyframe() { |
||||||
|
const auto& data = SingleKeyframeData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
std::size_t hint{}; |
||||||
|
CORRADE_COMPARE((Animation::interpolate<Float, Float>( |
||||||
|
Containers::arrayView(Keys).prefix(1), |
||||||
|
Containers::arrayView(Values).prefix(1), |
||||||
|
data.extrapolation, data.extrapolation, |
||||||
|
Math::lerp, data.time, hint)), data.expectedValue); |
||||||
|
CORRADE_COMPARE(hint, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void InterpolationTest::interpolateNoKeyframe() { |
||||||
|
std::size_t hint{}; |
||||||
|
CORRADE_COMPARE((Animation::interpolate<Float, Float>( |
||||||
|
nullptr, nullptr, Extrapolation::Extrapolated, |
||||||
|
Extrapolation::Extrapolated, Math::lerp, 3.5f, hint)), Float{}); |
||||||
|
CORRADE_COMPARE(hint, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void InterpolationTest::interpolateHint() { |
||||||
|
const auto& data = HintData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
std::size_t hint = data.hint; |
||||||
|
CORRADE_COMPARE((Animation::interpolate<Float, Float>( |
||||||
|
Keys, Values, Extrapolation::Extrapolated, Extrapolation::Extrapolated, |
||||||
|
Math::lerp, 4.75f, hint)), 1.0f); |
||||||
|
CORRADE_COMPARE(hint, 2); |
||||||
|
} |
||||||
|
|
||||||
|
void InterpolationTest::interpolateStrictHint() { |
||||||
|
const auto& data = HintData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
std::size_t hint = data.hint; |
||||||
|
CORRADE_COMPARE((Animation::interpolateStrict<Float, Float>( |
||||||
|
Keys, Values, Math::lerp, 4.75f, hint)), 1.0f); |
||||||
|
CORRADE_COMPARE(hint, 2); |
||||||
|
} |
||||||
|
|
||||||
|
void InterpolationTest::interpolateError() { |
||||||
|
std::ostringstream out; |
||||||
|
Error redirectError{&out}; |
||||||
|
|
||||||
|
{ |
||||||
|
std::size_t hint{}; |
||||||
|
Animation::interpolate<Float, Float>(Keys, nullptr, Extrapolation::Extrapolated, Extrapolation::Extrapolated, Math::lerp, 0.0f, hint); |
||||||
|
} |
||||||
|
|
||||||
|
CORRADE_COMPARE(out.str(), |
||||||
|
"Animation::interpolate(): keys and values don't have the same size\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void InterpolationTest::interpolateStrictError() { |
||||||
|
std::ostringstream out; |
||||||
|
Error redirectError{&out}; |
||||||
|
|
||||||
|
{ |
||||||
|
std::size_t hint{}; |
||||||
|
Animation::interpolateStrict<Float, Float>( |
||||||
|
Containers::arrayView(Keys).prefix(1), |
||||||
|
Containers::arrayView(Values).prefix(1), |
||||||
|
Math::lerp, 0.0f, hint); |
||||||
|
} { |
||||||
|
std::size_t hint{}; |
||||||
|
Animation::interpolateStrict<Float, Float>( |
||||||
|
Containers::arrayView(Keys).prefix(3), Values, |
||||||
|
Math::lerp, 0.0f, hint); |
||||||
|
} |
||||||
|
|
||||||
|
CORRADE_COMPARE(out.str(), |
||||||
|
"Animation::interpolateStrict(): at least two keyframes required\n" |
||||||
|
"Animation::interpolateStrict(): keys and values don't have the same size\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void InterpolationTest::debugExtrapolation() { |
||||||
|
std::ostringstream out; |
||||||
|
|
||||||
|
Debug{&out} << Extrapolation::DefaultConstructed << Extrapolation(0xde); |
||||||
|
CORRADE_COMPARE(out.str(), "Animation::Extrapolation::DefaultConstructed Animation::Extrapolation(0xde)\n"); |
||||||
|
} |
||||||
|
|
||||||
|
}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Animation::Test::InterpolationTest) |
||||||
Loading…
Reference in new issue