💿🐜 Antkeeper source code https://antkeeper.com
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.

556 lines
12 KiB

  1. /*
  2. * Copyright (C) 2020 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #ifndef ANTKEEPER_ANIMATION_HPP
  20. #define ANTKEEPER_ANIMATION_HPP
  21. #include <algorithm>
  22. #include <functional>
  23. #include <set>
  24. #include <tuple>
  25. #include <type_traits>
  26. #include <unordered_map>
  27. /**
  28. * Abstract base class for animations.
  29. */
  30. class animation_base
  31. {
  32. public:
  33. animation_base();
  34. /**
  35. * Advances the animation position (t) by @p dt.
  36. *
  37. * @param dt Delta time by which the animation position will be advanced.
  38. */
  39. virtual void advance(double dt) = 0;
  40. /**
  41. * Sets the animation position to @p t.
  42. *
  43. * @param t Position in time to which the animation position will be set.
  44. */
  45. void seek(double t);
  46. /// Sets the animation position to `0.0`.
  47. void rewind();
  48. /// Enables or disables looping of the animation.
  49. void loop(bool enabled);
  50. /// Pauses the animation.
  51. void pause();
  52. /// Plays the animation.
  53. void play();
  54. /// Stops the animation, rewinds it, and resets the loop count.
  55. void stop();
  56. /**
  57. * Sets the speed of the animation.
  58. *
  59. * @param speed Speed multiplier.
  60. */
  61. void set_speed(double speed);
  62. /// Returns `true` if looping of the animation is enabled, `false` otherwise.
  63. bool is_looped() const;
  64. /// Returns `true` if the animation is paused, `false` otherwise.
  65. bool is_paused() const;
  66. /// Returns `true` if the animation is stopped, `false` otherwise.
  67. bool is_stopped() const;
  68. /// Returns the current position in time of the animation.
  69. double get_position() const;
  70. /// Returns the current loop count of the animation.
  71. int get_loop_count() const;
  72. /// Returns the duration of the animation.
  73. virtual double get_duration() const = 0;
  74. /// Sets the callback that's executed when the animation is started from a stopped state.
  75. void set_start_callback(std::function<void()> callback);
  76. /// Sets the callback that's executed when a non-looped animation has finished.
  77. void set_end_callback(std::function<void()> callback);
  78. /**
  79. * Sets the callback that's executed when the animation loops.
  80. *
  81. * @param callback Loop callback function which is passed the current loop count.
  82. */
  83. void set_loop_callback(std::function<void(int)> callback);
  84. protected:
  85. bool looped;
  86. int loop_count;
  87. bool paused;
  88. bool stopped;
  89. double position;
  90. double speed;
  91. std::function<void()> start_callback;
  92. std::function<void()> end_callback;
  93. std::function<void(int)> loop_callback;
  94. };
  95. inline bool animation_base::is_looped() const
  96. {
  97. return looped;
  98. }
  99. inline bool animation_base::is_paused() const
  100. {
  101. return paused;
  102. }
  103. inline bool animation_base::is_stopped() const
  104. {
  105. return stopped;
  106. }
  107. inline double animation_base::get_position() const
  108. {
  109. return position;
  110. }
  111. inline int animation_base::get_loop_count() const
  112. {
  113. return loop_count;
  114. }
  115. template <typename T>
  116. class animation_channel
  117. {
  118. public:
  119. /// Keyframe consisting of a time and a value.
  120. typedef std::tuple<double, T> keyframe;
  121. /**
  122. * Creates an animation channel.
  123. *
  124. * @param id ID of the channel.
  125. */
  126. animation_channel(int id);
  127. /// Creates an animation channel.
  128. animation_channel();
  129. /// Creates an animation channel.
  130. animation_channel(const animation_channel& other);
  131. /// Assigns the contents of another channel to this channel.
  132. animation_channel& operator=(const animation_channel& other);
  133. /**
  134. * Adds a keyframe to the animation.
  135. *
  136. * @param k Keyframe to add.
  137. */
  138. void insert_keyframe(const keyframe& k);
  139. /**
  140. * Removes all keyframes on `[start, end)`.
  141. *
  142. * @param start Starting position in time (inclusive).
  143. * @param end Ending position in time (non-inclusive).
  144. */
  145. void remove_keyframes(double start, double end);
  146. /// Removes all keyframes from the animation.
  147. void remove_keyframes();
  148. /**
  149. * Finds the keyframes to the left and right of @p position.
  150. *
  151. * @param position Position in time.
  152. * @return Array containing the the keyframes on the left and right of @p position.
  153. */
  154. std::array<const keyframe*, 2> find_keyframes(double position) const;
  155. /**
  156. * Finds all the keyframes on `[start, end)`.
  157. *
  158. * @param start Starting position in time (inclusive).
  159. * @param end Ending position in time (non-inclusive).
  160. * @return All keyframes on `[start, end)`.
  161. */
  162. std::list<keyframe> find_keyframes(double start, double end) const;
  163. /// Returns the ID of the animation channel.
  164. int get_id() const;
  165. /// Returns the duration of the animation channel.
  166. double get_duration() const;
  167. private:
  168. struct keyframe_compare
  169. {
  170. inline bool operator()(const keyframe& lhs, const keyframe& rhs) const
  171. {
  172. return std::get<0>(lhs) < std::get<0>(rhs);
  173. }
  174. };
  175. int id;
  176. std::set<keyframe, keyframe_compare> keyframes;
  177. };
  178. template <typename T>
  179. animation_channel<T>::animation_channel(int id):
  180. id(id),
  181. keyframes(keyframe_compare())
  182. {}
  183. template <typename T>
  184. animation_channel<T>::animation_channel():
  185. animation_channel(-1)
  186. {}
  187. template <typename T>
  188. animation_channel<T>::animation_channel(const animation_channel& other):
  189. id(other.id),
  190. keyframes(other.keyframes)
  191. {}
  192. template <typename T>
  193. animation_channel<T>& animation_channel<T>::operator=(const animation_channel& other)
  194. {
  195. id = other.id;
  196. keyframes = other.keyframes;
  197. }
  198. template <typename T>
  199. void animation_channel<T>::insert_keyframe(const keyframe& k)
  200. {
  201. keyframes.emplace(k);
  202. }
  203. template <typename T>
  204. void animation_channel<T>::remove_keyframes(double start, double end)
  205. {
  206. auto lower_bound = keyframes.lower_bound({start, T()});
  207. auto upper_bound = keyframes.upper_bound({end, T()});
  208. keyframes.erase(lower_bound, upper_bound);
  209. }
  210. template <typename T>
  211. void animation_channel<T>::remove_keyframes()
  212. {
  213. keyframes.clear();
  214. }
  215. template <typename T>
  216. std::array<const typename animation_channel<T>::keyframe*, 2> animation_channel<T>::find_keyframes(double position) const
  217. {
  218. // Find the following keyframe
  219. auto upper_bound = keyframes.upper_bound({position, T()});
  220. // Find the preceding keyframe
  221. auto lower_bound = upper_bound;
  222. --lower_bound;
  223. std::array<const keyframe*, 2> frames;
  224. frames[0] = (lower_bound != keyframes.end()) ? &(*lower_bound) : nullptr;
  225. frames[1] = (upper_bound != keyframes.end()) ? &(*upper_bound) : nullptr;
  226. return frames;
  227. }
  228. template <typename T>
  229. std::list<typename animation_channel<T>::keyframe> animation_channel<T>::find_keyframes(double start, double end) const
  230. {
  231. std::list<keyframe> keyframe_list;
  232. auto lower_bound = keyframes.lower_bound({start, T()});
  233. auto upper_bound = keyframes.upper_bound({end, T()});
  234. for (auto iterator = lower_bound; iterator != upper_bound; ++iterator)
  235. {
  236. keyframe_list.push_back(*iterator);
  237. }
  238. return keyframe_list;
  239. }
  240. template <typename T>
  241. inline int animation_channel<T>::get_id() const
  242. {
  243. return id;
  244. }
  245. template <typename T>
  246. double animation_channel<T>::get_duration() const
  247. {
  248. if (keyframes.empty())
  249. {
  250. return 0.0;
  251. }
  252. return std::get<0>(*keyframes.rbegin());
  253. }
  254. /**
  255. * Templated keyframe animation class.
  256. */
  257. template <typename T>
  258. class animation: public animation_base
  259. {
  260. public:
  261. /// Channel for this animation type.
  262. typedef animation_channel<T> channel;
  263. // Keyframe type for this animation.
  264. typedef typename channel::keyframe keyframe;
  265. /// Interpolator function type.
  266. typedef typename std::decay<std::function<T(const T&, const T&, double)>>::type interpolator_type;
  267. /// Creates an animation.
  268. animation();
  269. /// @copydoc animation_base::advance()
  270. virtual void advance(double dt);
  271. /**
  272. * Adds a channel to the animation.
  273. *
  274. * @param id ID of the channel.
  275. * @return Added or pre-existing channel.
  276. */
  277. channel* add_channel(int id);
  278. /**
  279. * Removes a channel from the animation.
  280. *
  281. * @param id ID of the channel to remove.
  282. */
  283. void remove_channel(int id);
  284. /// Removes all channels from the animation.
  285. void remove_channels();
  286. /**
  287. * Sets the frame interpolator function object.
  288. *
  289. * @param interpolator Frame interpolator function object.
  290. */
  291. void set_interpolator(interpolator_type interpolator);
  292. /**
  293. * Sets the callback that's executed on each frame of animation.
  294. *
  295. * @param callback Frame callback which receives the index of an animation channel and value of an interpolated frame.
  296. */
  297. void set_frame_callback(std::function<void(int, const T&)> callback);
  298. /**
  299. * Returns the channel with the specified ID.
  300. *
  301. * @param id ID of the channel to get.
  302. */
  303. const channel* get_channel(int id) const;
  304. /// @copydoc animation::get_channel(int) const
  305. channel* get_channel(int id);
  306. /// @copydoc animation_base::get_duration() const
  307. virtual double get_duration() const;
  308. private:
  309. std::unordered_map<int, channel> channels;
  310. interpolator_type interpolator;
  311. std::function<void(int, const T&)> frame_callback;
  312. };
  313. template <typename T>
  314. animation<T>::animation():
  315. interpolator(nullptr),
  316. frame_callback(nullptr)
  317. {}
  318. template <typename T>
  319. void animation<T>::advance(double dt)
  320. {
  321. if (paused || stopped)
  322. {
  323. return;
  324. }
  325. // Advance position by dt
  326. position += dt * speed;
  327. // Determine duration of the animation
  328. double duration = get_duration();
  329. if (position < duration)
  330. {
  331. if (frame_callback != nullptr && interpolator != nullptr)
  332. {
  333. for (std::size_t i = 0; i < channels.size(); ++i)
  334. {
  335. auto frames = channels[i].find_keyframes(position);
  336. if (frames[0] != nullptr && frames[1] != nullptr)
  337. {
  338. // Calculate interpolated frame
  339. double t0 = std::get<0>(*frames[0]);
  340. double t1 = std::get<0>(*frames[1]);
  341. double alpha = (position - t0) / (t1 - t0);
  342. T frame = interpolator(std::get<1>(*frames[0]), std::get<1>(*frames[1]), alpha);
  343. // Pass frame to frame callback
  344. frame_callback(static_cast<int>(i), frame);
  345. }
  346. else if (frames[0] != nullptr)
  347. {
  348. // Pass frame to frame callback
  349. frame_callback(static_cast<int>(i), std::get<1>(*frames[0]));
  350. }
  351. else if (frames[1] != nullptr)
  352. {
  353. // Pass frame to frame callback
  354. frame_callback(static_cast<int>(i), std::get<1>(*frames[1]));
  355. }
  356. }
  357. }
  358. }
  359. else
  360. {
  361. if (looped)
  362. {
  363. ++loop_count;
  364. // Subtract duration of animation from position
  365. position -= duration;
  366. // Execute loop callback
  367. if (loop_callback)
  368. {
  369. loop_callback(loop_count);
  370. }
  371. // Call frame callback on looped frame
  372. if (frame_callback)
  373. {
  374. advance(0.0);
  375. }
  376. }
  377. else
  378. {
  379. // Call frame callback for end frame
  380. if (frame_callback != nullptr)
  381. {
  382. for (std::size_t i = 0; i < channels.size(); ++i)
  383. {
  384. auto frames = channels[i].find_keyframes(channels[i].get_duration());
  385. if (frames[0] != nullptr)
  386. {
  387. frame_callback(static_cast<int>(i), std::get<1>(*frames[0]));
  388. }
  389. }
  390. }
  391. stopped = true;
  392. // Call end callback
  393. if (end_callback)
  394. {
  395. end_callback();
  396. }
  397. }
  398. }
  399. }
  400. template <typename T>
  401. typename animation<T>::channel* animation<T>::add_channel(int id)
  402. {
  403. return &(*channels.emplace(id, id).first).second;
  404. }
  405. template <typename T>
  406. void animation<T>::remove_channel(int id)
  407. {
  408. auto it = channels.find(id);
  409. if (it != channels.end())
  410. {
  411. channels.erase(it);
  412. }
  413. }
  414. template <typename T>
  415. void animation<T>::remove_channels()
  416. {
  417. channels.clear();
  418. }
  419. template <typename T>
  420. void animation<T>::set_interpolator(interpolator_type interpolator)
  421. {
  422. this->interpolator = interpolator;
  423. }
  424. template <typename T>
  425. void animation<T>::set_frame_callback(std::function<void(int, const T&)> callback)
  426. {
  427. this->frame_callback = callback;
  428. }
  429. template <typename T>
  430. const typename animation<T>::channel* animation<T>::get_channel(int id) const
  431. {
  432. auto it = channels.find(id);
  433. if (it != channels.end())
  434. {
  435. return &it->second;
  436. }
  437. return nullptr;
  438. }
  439. template <typename T>
  440. typename animation<T>::channel* animation<T>::get_channel(int id)
  441. {
  442. auto it = channels.find(id);
  443. if (it != channels.end())
  444. {
  445. return &it->second;
  446. }
  447. return nullptr;
  448. }
  449. template <typename T>
  450. double animation<T>::get_duration() const
  451. {
  452. double duration = 0.0;
  453. for (auto it = channels.begin(); it != channels.end(); ++it)
  454. {
  455. duration = std::max<double>(duration, it->second.get_duration());
  456. }
  457. return duration;
  458. }
  459. #endif // ANTKEEPER_ANIMATION_HPP