/* * Copyright (C) 2021 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_CHANNEL_HPP #define ANTKEEPER_ANIMATION_CHANNEL_HPP #include #include #include /** * Single channel in a keyframe animation. * * @see animation */ template class animation_channel { public: /// Keyframe consisting of a time and a value. typedef std::tuple keyframe; /** * Creates an animation channel. * * @param id ID of the channel. */ animation_channel(int id); /// Creates an animation channel. animation_channel(); /// Creates an animation channel. animation_channel(const animation_channel& other); /// Assigns the contents of another channel to this channel. animation_channel& operator=(const animation_channel& other); /** * 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); /// Removes all keyframes from the animation. void remove_keyframes(); /** * Finds the keyframes to the left and right of @p position. * * @param position Position in time. * @return Array containing the the keyframes on the left and right of @p position. */ std::array find_keyframes(double position) const; /** * Finds 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 find_keyframes(double start, double end) const; /// Returns the ID of the animation channel. int get_id() const; /// Returns the duration of the animation channel. double get_duration() const; private: struct keyframe_compare { inline bool operator()(const keyframe& lhs, const keyframe& rhs) const { return std::get<0>(lhs) < std::get<0>(rhs); } }; int id; std::set keyframes; }; template animation_channel::animation_channel(int id): id(id), keyframes(keyframe_compare()) {} template animation_channel::animation_channel(): animation_channel(-1) {} template animation_channel::animation_channel(const animation_channel& other): id(other.id), keyframes(other.keyframes) {} template animation_channel& animation_channel::operator=(const animation_channel& other) { id = other.id; keyframes = other.keyframes; } template void animation_channel::insert_keyframe(const keyframe& k) { keyframes.emplace(k); } template void animation_channel::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 void animation_channel::remove_keyframes() { keyframes.clear(); } template std::array::keyframe*, 2> animation_channel::find_keyframes(double position) const { // Find the following keyframe auto upper_bound = keyframes.upper_bound({position, T()}); // Find the preceding keyframe auto lower_bound = upper_bound; --lower_bound; std::array frames; frames[0] = (lower_bound != keyframes.end()) ? &(*lower_bound) : nullptr; frames[1] = (upper_bound != keyframes.end()) ? &(*upper_bound) : nullptr; return frames; } template std::list::keyframe> animation_channel::find_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 inline int animation_channel::get_id() const { return id; } template double animation_channel::get_duration() const { if (keyframes.empty()) { return 0.0; } return std::get<0>(*keyframes.rbegin()); } #endif // ANTKEEPER_ANIMATION_CHANNEL_HPP