#ifndef Magnum_Animation_Track_h #define Magnum_Animation_Track_h /* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Vladimír Vondruš 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 Class @ref Magnum::Animation::Track, @ref Magnum::Animation::TrackViewStorage, @ref Magnum::Animation::TrackView */ #include #include "Magnum/Animation/Animation.h" #include "Magnum/Animation/Interpolation.h" #include "Magnum/Math/Range.h" namespace Magnum { namespace Animation { /** @brief Animation track @tparam K Key type @tparam V Value type @tparam R Result type Immutable storage of keyframe + value pairs. Usually used in combination with the @ref Player class, but it's possible to use it separately as well. @section Animation-Track-usage Basic usage Animation track is defined by a list of keyframes (time+value pairs), interpolator function and extrapolation behavior. @snippet MagnumAnimation.cpp Track-usage @section Animation-Track-interpolators Types and interpolators The track supports arbitrary types for keys, values and interpolators. These are common combinations: @m_class{m-fullwidth} Interpolation type | Value type | Result type | Interpolator ------------------- | ----------------- | ------------- | ------------ Constant | any `V` | `V` | @ref 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::Quaternion | @ref Math::Quaternion | @ref Math::lerp(const Quaternion&, const Quaternion&, T) "Math::lerp()" 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()" It's also possible to supply a generic interpolation behavior by passing the @ref Interpolation enum to the constructor. In case the interpolator function is not passed in as well, it's autodetected using @ref interpolatorFor(). See its documentation for more information. The @ref Interpolation enum is then stored in @ref interpolation() and acts as a hint for desired interpolation behavior for users who might want to use their own interpolator. @section Animation-Track-performance Performance tuning The snippet shown above is convenience-oriented at a cost of sacrificing some performance. You have the following options: @subsection Animation-Track-performance-hint Keyframe hinting The @ref Track and @ref TrackView classes are fully stateless and the @ref at(K) const function performs a linear search for matching keyframe from the beginning every time. You can use @ref at(K, std::size_t&) const to remember last used keyframe index and pass it in the next iteration as a hint: @snippet MagnumAnimation.cpp Track-performance-hint @subsection Animation-Track-performance-strict Strict interpolation While it's possible to have different @ref Extrapolation modes for frames outside of the track range with graceful handling of single- or zero-frame animations, the additional checks have some impact. The @ref atStrict() has implicit @ref Extrapolation::Extrapolated behavior and assumes there are always at least two keyframes, resulting in more compact interpolation code. If your animation data satisfy the prerequisites, simply use it in place of @ref at(): @snippet MagnumAnimation.cpp Track-performance-strict @subsection Animation-Track-performance-cache Cache-efficient data layout Usually multiple tracks (translation, rotation, scaling) are combined together to form a single animation. In order to achieve better data layout, consider interleaving the data and passing them using @ref Corrade::Containers::StridedArrayView to multiple @ref TrackView instead of having data duplicated scattered across disjoint allocations of @ref Track instances: @snippet MagnumAnimation.cpp Track-performance-cache @subsection Animation-Track-performance-interpolator Interpolator function choice The interpolator function has a direct effect on animation performance. You can choose a less complex interpolator (constant instead of linear or linear instead of spheric linear, for example) either during construction or passing it directly to @ref at() / @ref atStrict(). The @ref interpolator() can act as a hint on what kind of function should be chosen. Depending on how the track was constructed, passing the interpolator directly to @ref at() / @ref atStrict() usually also results in it being inlined by the compiler and thus faster than an indirect function call. Note that when constructing the track by just passing @ref Interpolator to the constructor, the function is chosen by @ref interpolatorFor(), which favors correctness over performance. See its documentation for more information. @experimental */ template #endif > class Track { public: /** @brief Key type */ typedef K KeyType; /** @brief Value type */ typedef V ValueType; /** @brief Animation result type */ typedef R ResultType; /** @brief Interpolation function */ typedef ResultType(*Interpolator)(const ValueType&, const ValueType&, Float); /** * @brief Construct an empty track * * The @ref data(), @ref keys(), @ref values() and @ref interpolator() * functions return @cpp nullptr @ce, @ref at() always returns a * default-constructed value. */ explicit Track() noexcept: _data{}, _interpolator{}, _before{}, _after{} {} /** * @brief Construct with custom interpolator * @param data Keyframe data * @param interpolator Interpolator function * @param before Extrapolation behavior * @param after Extrapolation behavior after * * The keyframe data are assumed to be stored in sorted order. It's not * an error to have two successive keyframes with the same frame value. * The @ref interpolation() field is set to @ref Interpolation::Custom. * See @ref Track(Containers::Array>&&, Interpolation, Interpolator, Extrapolation, Extrapolation) or * @ref Track(Containers::Array>&&, Interpolation, Extrapolation, Extrapolation) * for an alternative. */ explicit Track(Containers::Array>&& data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolator}, _interpolation{Interpolation::Custom}, _before{before}, _after{after} {} /** @overload */ explicit Track(std::initializer_list> data, Interpolator interpolator, Extrapolation before, Extrapolation after): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolator, before, after} {} /** @overload * Equivalent to calling @ref Track(Containers::Array>&&, Interpolator, Extrapolation, Extrapolation) * with both @p before and @p after set to @p extrapolation. */ explicit Track(Containers::Array>&& data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant) noexcept: Track{std::move(data), interpolator, extrapolation, extrapolation} {} /** @overload */ explicit Track(std::initializer_list> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolator, extrapolation, extrapolation} {} /** * @brief Construct with both generic and custom interpolator * @param data Keyframe data * @param interpolation Interpolation behavior * @param interpolator Interpolator function * @param before Extrapolation behavior * @param after Extrapolation behavior after * * The keyframe data are assumed to be stored in sorted order. It's not * an error to have two successive keyframes with the same frame value. * @p interpolation acts as a behavior hint to users that might want to * supply their own interpolator function to @ref at() or * @ref atStrict(). */ explicit Track(Containers::Array>&& data, Interpolation interpolation, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolator}, _interpolation{interpolation}, _before{before}, _after{after} {} /** @overload */ explicit Track(std::initializer_list> data, Interpolation interpolation, Interpolator interpolator, Extrapolation before, Extrapolation after): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolation, interpolator, before, after} {} /** @overload * Equivalent to calling @ref Track(Containers::Array>&&, Interpolation, Interpolator, Extrapolation, Extrapolation) * with both @p before and @p after set to @p extrapolation. */ explicit Track(Containers::Array>&& data, Interpolation interpolation, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant) noexcept: Track{std::move(data), interpolation, interpolator, extrapolation, extrapolation} {} /** @overload */ explicit Track(std::initializer_list> data, Interpolation interpolation, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Constant): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolation, interpolator, extrapolation} {} /** * @brief Construct with generic interpolation behavior * @param data Keyframe data * @param interpolation Interpolation behavior * @param before Extrapolation behavior * @param after Extrapolation behavior after * * The keyframe data are assumed to be stored in sorted order. It's not * an error to have two successive keyframes with the same frame value. * The @ref interpolator() function is autodetected from * @p interpolation using @ref interpolatorFor(). See its documentation * for more information. */ explicit Track(Containers::Array>&& data, Interpolation interpolation, Extrapolation before, Extrapolation after) noexcept: _data{std::move(data)}, _interpolator{interpolatorFor(interpolation)}, _interpolation{interpolation}, _before{before}, _after{after} {} /** @overload */ explicit Track(std::initializer_list> data, Interpolation interpolation, Extrapolation before, Extrapolation after): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolation, before, after} {} /** @overload * Equivalent to calling @ref Track(Containers::Array>&&, Interpolation, Extrapolation, Extrapolation) * with both @p before and @p after set to @p extrapolation. */ explicit Track(Containers::Array>&& data, Interpolation interpolation, Extrapolation extrapolation = Extrapolation::Constant) noexcept: Track{std::move(data), interpolation, extrapolation, extrapolation} {} /** @overload */ explicit Track(std::initializer_list> data, Interpolation interpolation, Extrapolation extrapolation = Extrapolation::Constant): Track{Containers::Array>{Containers::InPlaceInit, data}, interpolation, extrapolation, extrapolation} {} /** @brief Copying is not allowed */ Track(const Track&) = delete; /** @brief Move constructor */ Track(Track&&) = default; /** @brief Copying is not allowed */ Track& operator=(const Track&) = delete; /** @brief Move constructor */ Track& operator=(Track&&) = default; /** @brief Conversion to a view */ operator TrackView() const noexcept { return TrackView{_data, _interpolation, _interpolator, _before, _after}; } /** * @brief Interpolation behavior * * Acts as a behavior hint to users that might want to supply their own * interpolator function to @ref at() or @ref atStrict(). * @see @ref interpolator() */ Interpolation interpolation() const { return _interpolation; } /** * @brief Interpolation function * * @see @ref interpolation() */ Interpolator interpolator() const { return _interpolator; } /** * @brief Extrapolation behavior before first keyframe * * @see @ref after(), @ref at() */ Extrapolation before() const { return _before; } /** * @brief Extrapolation behavior after last keyframe * * @see @ref before(), @ref at() */ Extrapolation after() const { return _after; } /** * @brief Duration of the track * * Calculated from first and last keyframe. If there are no keyframes, * a default-constructed value is returned. Use @ref Math::join() to * calculate combined duration for a set of tracks. */ Math::Range1D duration() const { return _data.empty() ? Math::Range1D{} : Math::Range1D{_data.front().first, _data.back().first}; } /** @brief Keyframe count */ std::size_t size() const { return _data.size(); } /** * @brief Keyframe data * * @see @ref keys(), @ref values(), @ref operator[]() */ Containers::ArrayView> data() const { return _data; } /** * @brief Key data * * @see @ref data(), @ref values(), @ref operator[]() */ Containers::StridedArrayView keys() const { return _data ? Containers::StridedArrayView{&_data[0].first, _data.size(), sizeof(std::pair)} : nullptr; } /** * @brief Value data * * @see @ref data(), @ref keys(), @ref operator[]() */ Containers::StridedArrayView values() const { return _data ? Containers::StridedArrayView{&_data[0].second, _data.size(), sizeof(std::pair)} : nullptr; } /** * @brief Keyframe access * * @see @ref size() */ const std::pair& operator[](std::size_t i) const { return _data[i]; } /** * @brief Animated value at a given time * * Calls @ref interpolate(), see its documentation for more * information. Note that this function performs a linear search every * time, use @ref at(K, std::size_t&) const to supply a search hint. * @see @ref atStrict(K, std::size_t&) const, * @ref at(Interpolator, K) const */ R at(K frame) const { std::size_t hint{}; return at(_interpolator, frame, hint); } /** * @brief Animated value at a given time * * Calls @ref interpolate(), see its documentation for more * information. * @see @ref at(K) const, @ref atStrict(K, std::size_t&) const, * @ref at(Interpolator, K, std::size_t&) const */ R at(K frame, std::size_t& hint) const { return at(_interpolator, frame, hint); } /** * @brief Animated value at a given time * * Unlike @ref at(K) const calls @ref interpolate() with * @p interpolator, overriding the interpolator function set in * constructor. See its documentation for more information. * @see @ref at(K, std::size_t&) const, * @ref atStrict(Interpolator, K, std::size_t&) const */ R at(Interpolator interpolator, K frame) const { std::size_t hint{}; return at(interpolator, frame, hint); } /** * @brief Animated value at a given time * * Unlike @ref at(K, std::size_t&) const calls @ref interpolate() with * @p interpolator, overriding the interpolator function set in * constructor. See its documentation for more information. * @see @ref atStrict(Interpolator, K, std::size_t&) const */ R at(Interpolator interpolator, K frame, std::size_t& hint) const { return interpolate(keys(), values(), _before, _after, interpolator, frame, hint); } /** * @brief Animated value at a given time * * A faster version of @ref at(K, std::size_t&) const with some * restrictions. Calls @ref interpolateStrict(), see its documentation * for more information. * @see @ref atStrict(Interpolator, K, std::size_t&) const */ R atStrict(K frame, std::size_t& hint) const { return atStrict(_interpolator, frame, hint); } /** * @brief Animated value at a given time * * Unlike @ref atStrict(K, std::size_t&) const calls @ref interpolate() * with @p interpolator, overriding the interpolator function set in * constructor. See its documentation for more information. * @see @ref at(K, std::size_t&) const */ R atStrict(Interpolator interpolator, K frame, std::size_t& hint) const { return interpolateStrict(keys(), values(), interpolator, frame, hint); } private: Containers::Array> _data; Interpolator _interpolator; Interpolation _interpolation; Extrapolation _before, _after; }; /** @brief Type-erased track view storage Cast to @ref TrackView of correct type to get access to all members. @experimental */ template class TrackViewStorage { public: /** @brief Key type */ typedef K KeyType; constexpr /*implicit*/ TrackViewStorage() noexcept: _keys{}, _values{}, _interpolator{}, _interpolation{}, _before{}, _after{} {} /** * @brief Interpolation behavior * * Acts as a behavior hint to users that might want to supply their own * interpolator function to @ref TrackView::at() or * @ref TrackView::atStrict(). * @see @ref TrackView::interpolator() */ Interpolation interpolation() const { return _interpolation; } /** * @brief Extrapolation behavior before first keyframe * * @see @ref TrackView::after(), @ref TrackView::at(), * @ref TrackView::atStrict() */ Extrapolation before() const { return _before; } /** * @brief Extrapolation behavior after last keyframe * * @see @ref TrackView::before(), @ref TrackView::at(), * @ref TrackView::atStrict() */ Extrapolation after() const { return _after; } /** * @brief Duration of the track * * Calculated from first and last keyframe. If there are no keyframes, * a default-constructed value is returned. Use @ref Math::join() to * calculate combined duration for a set of tracks. */ Math::Range1D duration() const { return _keys.empty() ? Math::Range1D{} : Math::Range1D{_keys.front(), _keys.back()}; } /** @brief Keyframe count */ std::size_t size() const { return _keys.size(); } /** * @brief Key data * * @see @ref TrackView::values(), @ref TrackView::operator[]() */ Containers::StridedArrayView keys() const { return _keys; } private: template friend class TrackView; template explicit TrackViewStorage(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, R(*interpolator)(const V&, const V&, Float), Extrapolation before, Extrapolation after) noexcept: _keys{keys}, _values{reinterpret_cast&>(values)}, _interpolator{reinterpret_cast(interpolator)}, _interpolation{interpolation}, _before{before}, _after{after} {} Containers::StridedArrayView _keys; Containers::StridedArrayView _values; void(*_interpolator)(void); Interpolation _interpolation; Extrapolation _before, _after; }; /** @brief Animation track view @tparam K Key type @tparam V Value type @tparam R Result type Unlike @ref Track this is a non-owning view onto keyframe + value pairs. See its documentation for more information. @experimental */ template #endif > class TrackView: public TrackViewStorage { public: /** @brief Value type */ typedef V ValueType; /** @brief Animation result type */ typedef R ResultType; /** @brief Interpolation function */ typedef ResultType(*Interpolator)(const ValueType&, const ValueType&, Float); /** * @brief Construct an empty track * * The @ref keys(), @ref values() and @ref interpolator() functions * return @cpp nullptr @ce, @ref at() always returns a * default-constructed value. */ /*implicit*/ TrackView() noexcept {} /** * @brief Construct with custom interpolator * @param keys Frame keys * @param values Frame values * @param interpolator Interpolation function * @param before Extrapolation behavior before * @param after Extrapolation behavior after * * The keyframe data are assumed to be stored in sorted order. It's not * an error to have two successive keyframes with the same frame value. * The @ref interpolation() field is set to @ref Interpolation::Custom. * See @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolation, Interpolator, Extrapolation, Extrapolation) or * @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolation, Extrapolation, Extrapolation) * for an alternative. */ /*implicit*/ TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{keys, values, Interpolation::Custom, interpolator, before, after} {} /** @overload * Equivalent to calling @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolator, Extrapolation, Extrapolation) * with both @p before and @p after set to @p extrapolation. */ /*implicit*/ TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{keys, values, interpolator, extrapolation, extrapolation} {} /** * @brief Construct with custom interpolator from an interleaved array * @param data Keyframe data * @param interpolator Interpolation function * @param before Extrapolation behavior before * @param after Extrapolation behavior after * * Converts @p data to a pair of strided array views and calls * @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolator, Extrapolation, Extrapolation). */ /*implicit*/ TrackView(Containers::ArrayView> data, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackView{Containers::StridedArrayView{data ? &data[0].first : nullptr, data.size(), sizeof(std::pair)}, Containers::StridedArrayView{data ? &data[0].second : nullptr, data.size(), sizeof(std::pair)}, interpolator, before, after} {} /** @overload * Equivalent to calling @ref TrackView(Containers::ArrayView>, Interpolator, Extrapolation, Extrapolation) * with both @p before and @p after set to @p extrapolation. */ explicit TrackView(Containers::ArrayView> data, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{data, interpolator, extrapolation, extrapolation} {} /** * @brief Construct with both generic and custom interpolator * @param keys Frame keys * @param values Frame values * @param interpolation Interpolation behavior * @param interpolator Interpolator function * @param before Extrapolation behavior before * @param after Extrapolation behavior after * * The keyframe data are assumed to be stored in sorted order. It's not * an error to have two successive keyframes with the same frame value. * @p interpolation acts as a behavior hint to users that might want to * supply their own interpolator function to @ref at() or * @ref atStrict(). */ /*implicit*/ TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{keys, values, interpolation, interpolator, before, after} {} /** @overload * Equivalent to calling @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolation, Interpolator, Extrapolation, Extrapolation) * with both @p before and @p after set to @p extrapolation. */ /*implicit*/ TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{keys, values, interpolation, interpolator, extrapolation, extrapolation} {} /** * @brief Construct with both generic and custom interpolator from an interleaved array * @param data Keyframe data * @param interpolation Interpolation behavior * @param interpolator Interpolator function * @param before Extrapolation behavior before * @param after Extrapolation behavior after * * Converts @p data to a pair of strided array views and calls * @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolator, Extrapolation, Extrapolation). */ /*implicit*/ TrackView(Containers::ArrayView> data, Interpolation interpolation, Interpolator interpolator, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{Containers::StridedArrayView{data ? &data[0].first : nullptr, data.size(), sizeof(std::pair)}, Containers::StridedArrayView{data ? &data[0].second : nullptr, data.size(), sizeof(std::pair)}, interpolation, interpolator, before, after} {} /** @overload * Equivalent to calling @ref TrackView(Containers::ArrayView>, Interpolation, Interpolator, Extrapolation, Extrapolation) * with both @p before and @p after set to @p extrapolation. */ /*implicit*/ TrackView(Containers::ArrayView> data, Interpolation interpolation, Interpolator interpolator, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{data, interpolation, interpolator, extrapolation, extrapolation} {} /** * @brief Construct with generic interpolation behavior * @param keys Frame keys * @param values Frame values * @param interpolation Interpolation behavior * @param before Extrapolation behavior before * @param after Extrapolation behavior after * * The keyframe data are assumed to be stored in sorted order. It's not * an error to have two successive keyframes with the same frame value. * The @ref interpolator() function is autodetected from * @p interpolation using @ref interpolatorFor(). See its documentation * for more information. */ /*implicit*/ TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, Extrapolation before, Extrapolation after) noexcept: TrackViewStorage{keys, values, interpolation, interpolatorFor(interpolation), before, after} {} /** @overload * Equivalent to calling @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolation, Extrapolation, Extrapolation) * with both @p before and @p after set to @p extrapolation. */ /*implicit*/ TrackView(const Containers::StridedArrayView& keys, const Containers::StridedArrayView& values, Interpolation interpolation, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{keys, values, interpolation, extrapolation, extrapolation} {} /** * @brief Construct with generic interpolation behavior from an interleaved array * @param data Keyframe data * @param interpolation Interpolation behavior * @param before Extrapolation behavior before * @param after Extrapolation behavior after * * Converts @p data to a pair of strided array views and calls * @ref TrackView(const Containers::StridedArrayView&, const Containers::StridedArrayView&, Interpolator, Extrapolation, Extrapolation). */ /*implicit*/ TrackView(Containers::ArrayView> data, Interpolation interpolation, Extrapolation before, Extrapolation after) noexcept: TrackView{Containers::StridedArrayView{data ? &data[0].first : nullptr, data.size(), sizeof(std::pair)}, Containers::StridedArrayView{data ? &data[0].second : nullptr, data.size(), sizeof(std::pair)}, interpolation, before, after} {} /** @overload * Equivalent to calling @ref TrackView(Containers::ArrayView>, Interpolation, Extrapolation, Extrapolation) * with both @p before and @p after set to @p extrapolation. */ /*implicit*/ TrackView(Containers::ArrayView> data, Interpolation interpolation, Extrapolation extrapolation = Extrapolation::Extrapolated) noexcept: TrackView{data, interpolation, extrapolation, extrapolation} {} /** * @brief Interpolation function * * @see @ref interpolation() */ Interpolator interpolator() const { return reinterpret_cast(TrackViewStorage::_interpolator); } /** * @brief Value data * * @see @ref keys(), @ref operator[]() */ Containers::StridedArrayView values() const { return reinterpret_cast&>(TrackViewStorage::_values); } /** * @brief Keyframe access * * @see @ref size() */ std::pair operator[](std::size_t i) const { return {TrackViewStorage::_keys[i], values()[i]}; } /** * @brief Animated value at a given time * * Calls @ref interpolate(), see its documentation for more * information. Note that this function performs a linear search every * time, use @ref at(K, std::size_t&) const to supply a search hint. * @see @ref atStrict(K, std::size_t&) const, * @ref at(Interpolator, K) const */ R at(K frame) const { std::size_t hint{}; return at(interpolator(), frame, hint); } /** * @brief Animated value at a given time * * Calls @ref interpolate(), see its documentation for more * information. * @see @ref at(K) const, @ref atStrict(K, std::size_t&) const, * @ref at(Interpolator, K, std::size_t&) const */ R at(K frame, std::size_t& hint) const { return at(interpolator(), frame, hint); } /** * @brief Animated value at a given time * * Unlike @ref at(K) const calls @ref interpolate() with * @p interpolator, overriding the interpolator function set in * constructor. See its documentation for more information. * @see @ref at(K, std::size_t&) const, * @ref atStrict(Interpolator, K, std::size_t&) const */ R at(Interpolator interpolator, K frame) const { std::size_t hint{}; return at(interpolator, frame, hint); } /** * @brief Animated value at a given time * * Unlike @ref at(K, std::size_t&) const calls @ref interpolate() with * @p interpolator, overriding the interpolator function set in * constructor. See its documentation for more information. * @see @ref atStrict(Interpolator, K, std::size_t&) const */ R at(Interpolator interpolator, K frame, std::size_t& hint) const { return interpolate(TrackViewStorage::_keys, values(), TrackViewStorage::_before, TrackViewStorage::_after, interpolator, frame, hint); } /** * @brief Animated value at a given time * * A faster version of @ref at(K, std::size_t&) const with some * restrictions. Calls @ref interpolateStrict(), see its documentation * for more information. * @see @ref atStrict(Interpolator, K, std::size_t&) const */ R atStrict(K frame, std::size_t& hint) const { return atStrict(interpolator(), frame, hint); } /** * @brief Animated value at a given time * * Unlike @ref atStrict(K, std::size_t&) const calls @ref interpolate() * with @p interpolator, overriding the interpolator function set in * constructor. See its documentation for more information. * @see @ref at(K, std::size_t&) const */ R atStrict(Interpolator interpolator, K frame, std::size_t& hint) const { return interpolateStrict(TrackViewStorage::_keys, values(), interpolator, frame, hint); } }; }} #endif