use std::fmt::{Display, Formatter};
use parking_lot::RwLock;
use crate::errors::Error;
use crate::utils::{task, EventHandler, EventManager, TaskHandler};
use crate::animations::{Segment, Track};
use std::sync::Arc;
pub enum AnimationEvent {
OnSegmentDone,
OnStart,
OnComplete,
}
impl From<AnimationEvent> for String {
fn from(event: AnimationEvent) -> Self {
let event = match event {
AnimationEvent::OnSegmentDone => "segment_done",
AnimationEvent::OnStart => "start",
AnimationEvent::OnComplete => "complete",
};
event.into()
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug)]
pub struct Animation {
segments: Vec<Segment>,
#[cfg_attr(feature = "serde", serde(skip))]
current: Arc<RwLock<usize>>,
#[cfg_attr(feature = "serde", serde(skip))]
interval: Arc<RwLock<Option<TaskHandler>>>,
#[cfg_attr(feature = "serde", serde(skip))]
events: EventManager,
}
impl Default for Animation {
fn default() -> Self {
Self {
segments: vec![],
current: Arc::new(RwLock::new(0)),
interval: Arc::new(RwLock::new(None)),
events: Default::default(),
}
}
}
impl Animation {
pub fn play(&mut self) -> &mut Self {
let events_clone = self.events.clone();
let mut self_clone = self.clone();
self.events.emit(AnimationEvent::OnStart, self.clone());
if self.get_duration() > 0 {
let handler = task::run(async move {
for index in self_clone.get_current()..self_clone.segments.len() {
*self_clone.current.write() = index;
let segment_playing = self_clone.segments.get_mut(index).unwrap();
segment_playing.play().await?;
events_clone.emit(AnimationEvent::OnSegmentDone, segment_playing.clone());
}
*self_clone.current.write() = 0; *self_clone.interval.write() = None;
events_clone.emit(AnimationEvent::OnComplete, self_clone);
Ok(())
})
.unwrap();
*self.interval.write() = Some(handler);
}
self
}
pub fn pause(&mut self) -> &mut Self {
self.cancel_animation();
self
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> &mut Self {
let current = self.get_current();
let was_running = self.cancel_animation();
if let Some(segment_playing) = self.segments.get_mut(current) {
segment_playing.reset();
}
match current < self.segments.len() - 1 {
true => *self.current.write() = current + 1,
false => *self.current.write() = 0,
}
if was_running {
self.play();
}
self
}
pub fn stop(&mut self) -> &mut Self {
if self.cancel_animation() {
let current = self.get_current();
if let Some(segment) = self.segments.get_mut(current) {
segment.reset();
}
}
*self.current.write() = 0;
self
}
pub fn is_playing(&self) -> bool {
self.interval.read().as_ref().is_some()
}
pub fn get_duration(&self) -> u64 {
self.segments
.iter()
.map(|segment| match segment.is_repeat() {
true => u64::MAX,
false => segment.get_duration(),
})
.sum()
}
pub fn get_progress(&self) -> u64 {
let current_segment_index = *self.current.read();
match self.segments.get(current_segment_index) {
None => 0,
Some(segment_playing) => segment_playing.get_progress(),
}
}
fn cancel_animation(&mut self) -> bool {
let was_running = match self.interval.read().as_ref() {
None => false,
Some(handler) => {
handler.abort();
true
}
};
if was_running {
*self.interval.write() = None;
}
was_running
}
pub fn get_segments(&self) -> &Vec<Segment> {
&self.segments
}
pub fn set_segments(mut self, segments: Vec<Segment>) -> Self {
self.segments = segments;
self
}
pub fn with_segment(mut self, segment: Segment) -> Self {
self.segments.push(segment);
self
}
pub fn get_current(&self) -> usize {
*self.current.read()
}
pub fn set_current(&self, index: usize) {
*self.current.write() = index;
}
pub fn on<S, F, T, Fut>(&self, event: S, callback: F) -> EventHandler
where
S: Into<String>,
T: 'static + Send + Sync + Clone,
F: FnMut(T) -> Fut + Send + 'static,
Fut: std::future::Future<Output = Result<(), Error>> + Send + 'static,
{
self.events.on(event, callback)
}
}
impl From<Segment> for Animation {
fn from(segment: Segment) -> Self {
Animation::default().with_segment(segment)
}
}
impl From<Track> for Animation {
fn from(track: Track) -> Self {
Animation::from(Segment::from(track))
}
}
impl Display for Animation {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"Animation [duration={}, segments={}]",
match self.get_duration() {
u64::MAX => String::from("INF"),
duration => format!("{}ms", duration),
},
self.segments.len()
)?;
for segment in &self.segments {
writeln!(
f,
" Segment [duration={}ms, repeat={}, fps={}, speed={}] :",
segment.get_duration(),
segment.is_repeat(),
segment.get_fps(),
segment.get_speed()
)?;
for track in segment.get_tracks() {
writeln!(
f,
" Track [duration={}ms, device={}]:",
track.get_duration(),
track.get_device()
)?;
for keyframe in track.get_keyframes() {
writeln!(
f,
" Keyframe {}ms to {}ms: {} [transition={:?}]",
keyframe.get_start(),
keyframe.get_end(),
keyframe.get_target(),
keyframe.get_transition(),
)?;
}
}
}
write!(f, "")
}
}
#[cfg(test)]
mod tests {
use serial_test::serial;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use crate::animations::Keyframe;
use crate::mocks::output_device::MockOutputDevice;
use crate::pause;
use super::*;
fn create_animation() -> Animation {
let segment = Segment::from(
Track::new(MockOutputDevice::new(40)).with_keyframe(Keyframe::new(100, 0, 190)),
);
Animation::default()
.with_segment(segment.clone())
.with_segment(segment.clone())
.with_segment(segment.clone())
.with_segment(segment.clone())
.with_segment(segment.clone())
.with_segment(segment.clone())
}
#[test]
fn test_animation() {
let animation = Animation::default();
assert_eq!(animation.get_current(), 0);
assert_eq!(animation.get_progress(), 0);
assert!(!animation.is_playing());
let animation = create_animation();
assert_eq!(animation.get_current(), 0);
assert_eq!(animation.get_segments().len(), 6);
assert_eq!(animation.get_duration(), 190 * 6);
let animation = animation.set_segments(vec![]);
assert_eq!(animation.get_segments().len(), 0);
assert_eq!(animation.get_duration(), 0);
let animation = animation.with_segment(
Segment::from(
Track::new(MockOutputDevice::new(40)).with_keyframe(Keyframe::new(100, 0, 190)),
)
.set_repeat(true),
);
assert_eq!(animation.get_segments().len(), 1);
assert_eq!(animation.get_duration(), u64::MAX);
assert_eq!(animation.to_string(), "Animation [duration=INF, segments=1]\n Segment [duration=190ms, repeat=true, fps=60, speed=100] :\n Track [duration=190ms, device=MockActuator [state=40]]:\n Keyframe 0ms to 190ms: 100 [transition=Linear]\n");
let animation = animation.set_segments(vec![Segment::from(
Track::new(MockOutputDevice::new(40)).with_keyframe(Keyframe::new(100, 0, 190)),
)]);
assert_eq!(animation.get_duration(), 190);
assert_eq!(animation.to_string(), "Animation [duration=190ms, segments=1]\n Segment [duration=190ms, repeat=false, fps=60, speed=100] :\n Track [duration=190ms, device=MockActuator [state=40]]:\n Keyframe 0ms to 190ms: 100 [transition=Linear]\n");
}
#[test]
fn test_animation_converters() {
let animation_from_track = Animation::from(Track::new(MockOutputDevice::new(40)));
assert_eq!(animation_from_track.get_current(), 0);
assert_eq!(animation_from_track.get_segments().len(), 1);
let animation_from_segment = Animation::from(Segment::default());
assert_eq!(animation_from_segment.get_current(), 0);
assert_eq!(animation_from_segment.get_segments().len(), 1);
}
#[serial]
#[hermes_five_macros::test]
async fn test_play_animation() {
let mut animation = create_animation();
assert_eq!(animation.get_current(), 0);
assert_eq!(animation.get_progress(), 0);
assert!(!animation.is_playing());
let flag = Arc::new(AtomicBool::new(false));
let active_segment = Arc::new(AtomicUsize::new(0));
let moved_flag = flag.clone();
animation.on(AnimationEvent::OnStart, move |animation: Animation| {
let captured_flag = moved_flag.clone();
async move {
captured_flag.store(true, Ordering::SeqCst);
assert_eq!(animation.get_current(), 0);
Ok(())
}
});
let moved_active_segment = active_segment.clone();
animation.on(AnimationEvent::OnSegmentDone, move |_: Segment| {
let captured_active_segment = moved_active_segment.clone();
async move {
let index = captured_active_segment.load(Ordering::SeqCst) + 1;
captured_active_segment.store(index, Ordering::SeqCst);
Ok(())
}
});
let moved_flag = flag.clone();
animation.on(AnimationEvent::OnComplete, move |animation: Animation| {
let captured_flag = moved_flag.clone();
async move {
captured_flag.store(false, Ordering::SeqCst);
assert_eq!(animation.get_current(), 5);
Ok(())
}
});
assert!(!flag.load(Ordering::SeqCst));
animation.play();
assert!(animation.is_playing());
pause!(150);
assert!(flag.load(Ordering::SeqCst));
pause!(500);
assert!(animation.get_current() > 0);
assert_eq!(
animation.get_current(),
active_segment.load(Ordering::SeqCst)
);
pause!(1000);
assert!(!flag.load(Ordering::SeqCst));
}
#[serial]
#[hermes_five_macros::test]
async fn test_animation_controls() {
let mut animation = create_animation();
assert_eq!(animation.get_current(), 0);
animation.play();
pause!(250);
assert_eq!(animation.get_current(), 1);
animation.pause();
pause!(220);
assert!(animation.interval.read().is_none());
assert_eq!(animation.get_current(), 1);
animation.play();
pause!(250);
assert_eq!(animation.get_current(), 2);
animation.next();
animation.next();
assert_eq!(animation.get_current(), 4);
animation.set_current(5);
assert_eq!(animation.get_current(), 5);
animation.stop();
assert!(animation.interval.read().is_none());
assert_eq!(animation.get_current(), 0);
}
#[serial]
#[hermes_five_macros::test]
async fn test_animation_skip() {
let mut animation = create_animation();
assert_eq!(animation.get_current(), 0);
animation.play();
pause!(230);
assert_eq!(animation.get_current(), 1);
animation.pause();
pause!(300);
assert_eq!(animation.get_current(), 1);
animation.next();
pause!(300);
assert_eq!(animation.get_current(), 2);
animation.play().next();
pause!(250);
assert_eq!(animation.get_current(), 4);
animation.next().next().next();
assert_eq!(animation.get_current(), 1);
}
}