mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
9.1 KiB
211 lines
9.1 KiB
#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(), @ref Track::before(), @ref Track::after(), |
|
@ref TrackView::before(), @ref TrackView::after() |
|
@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 |
|
@tparam R Result 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. |
|
|
|
Used internally from @ref Track::at() / @ref TrackView::at(), see @ref Track |
|
documentation for more information. |
|
|
|
@see @ref interpolateStrict(), @ref Math::select(), @ref Math::lerp(), |
|
@ref Math::slerp(), @ref Math::sclerp() |
|
@experimental |
|
*/ |
|
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 |
|
|
|
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. |
|
|
|
Used internally from @ref Track::atStrict() / @ref TrackView::atStrict(), see |
|
@ref Track documentation for more information. |
|
|
|
@see @ref Math::select(), @ref Math::lerp(), @ref Math::slerp(), |
|
@ref Math::sclerp() |
|
@experimental |
|
*/ |
|
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, 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", {}); |
|
|
|
/* 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, 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() == 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
|
|
|