/* * Copyright (C) 2020 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #ifndef ANTKEEPER_ANIMATION_HPP #define ANTKEEPER_ANIMATION_HPP #include #include #include #include /** * Abstract base class for animations. */ class animation_base { public: animation_base(); /** * Advances the animation position (t) by @p dt. * * @param dt Delta time by which the animation position will be advanced. */ virtual void advance(double dt) = 0; /** * Sets the animation position to @p t. * * @param t Position in time to which the animation position will be set. */ void seek(double t); void reset(); /// Returns the current position in time of the animation. double get_position() const; /// Sets the callback that's executed when the animation is started. void set_start_callback(std::function callback); /// Sets the callback that's executed when the animation ends. void set_end_callback(std::function callback); /// Sets the callback that's executed when the animation loops. void set_loop_callback(std::function callback); protected: double position; std::function start_callback; std::function end_callback; std::function loop_callback; }; /** * Templated keyframe animation class. */ template class animation: public animation_base { public: /// Scheduled function consisting of a time and function object. typedef std::tuple keyframe; /// Interpolator function type. typedef typename std::decay>::type interpolator_type; /// Creates an animation animation(); /// @copydoc animation_base::advance() virtual void advance(double dt); /** * Adds a keyframe to the animation. * * @param k Keyframe to add. */ void insert_keyframe(const keyframe& k); /** * Removes all keyframes on `[start, end)`. * * @param start Starting position in time (inclusive). * @param end Ending position in time (non-inclusive). */ void remove_keyframes(double start, double end); /** * Returns all the keyframes on `[start, end)`. * * @param start Starting position in time (inclusive). * @param end Ending position in time (non-inclusive). * @return All keyframes on `[start, end)`. */ std::list get_keyframes(double start, double end) const; /// Removes all keyframes from the animation. void clear(); /** * Sets the frame interpolator function object. * * @param interpolator Frame interpolator function object. */ void set_interpolator(interpolator_type interpolator); /** * Sets the callback that's executed on each frame of animation. * * @param callback Frame callback which receives the value of the interpolated frames. */ void set_frame_callback(std::function callback); private: //static bool keyframe_compare(const keyframe& a, const keyframe & b); struct keyframe_compare { inline bool operator()(const keyframe& lhs, const keyframe& rhs) const { return std::get<0>(lhs) < std::get<0>(rhs); } }; interpolator_type interpolator; std::function frame_callback; std::set keyframes; }; /* template bool animation::keyframe_compare(const keyframe& a, const keyframe & b) { return std::get<0>(a) < std::get<0>(b); } */ template animation::animation(): interpolator(nullptr), frame_callback(nullptr), keyframes(keyframe_compare()) {} template void animation::advance(double dt) { position += dt; if (frame_callback != nullptr && interpolator != nullptr) { auto upper_bound = keyframes.upper_bound({position, T()}); auto lower_bound = upper_bound; --lower_bound; if (lower_bound != keyframes.end() && upper_bound != keyframes.end()) { // Calculate interpolated frame double t0 = std::get<0>(*lower_bound); double t1 = std::get<0>(*upper_bound); double alpha = (position - t0) / (t1 - t0); T frame = interpolator(std::get<1>(*lower_bound), std::get<1>(*upper_bound), alpha); // Pass frame to frame callback frame_callback(frame); } } } template void animation::insert_keyframe(const keyframe& k) { keyframes.emplace(k); } template void animation::remove_keyframes(double start, double end) { auto lower_bound = keyframes.lower_bound({start, T()}); auto upper_bound = keyframes.upper_bound({end, T()}); keyframes.erase(lower_bound, upper_bound); } template std::list::keyframe> animation::get_keyframes(double start, double end) const { std::list keyframe_list; auto lower_bound = keyframes.lower_bound({start, T()}); auto upper_bound = keyframes.upper_bound({end, T()}); for (auto iterator = lower_bound; iterator != upper_bound; ++iterator) { keyframe_list.push_back(*iterator); } return keyframe_list; } template void animation::clear() { keyframes.clear(); } template void animation::set_interpolator(interpolator_type interpolator) { this->interpolator = interpolator; } template void animation::set_frame_callback(std::function callback) { this->frame_callback = callback; } #endif // ANTKEEPER_ANIMATION_HPP