use std::fmt::{Display, Formatter};
use std::time::SystemTime;
use crate::animations::Track;
use crate::errors::Error;
use crate::pause;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug)]
pub struct Segment {
repeat: bool,
loopback: u64,
speed: u8,
fps: u8,
tracks: Vec<Track>,
#[cfg_attr(feature = "serde", serde(skip))]
current_time: u64,
}
impl From<Track> for Segment {
fn from(track: Track) -> Self {
Self::default().with_track(track)
}
}
impl Segment {
pub async fn play(&mut self) -> Result<(), Error> {
if self.get_duration() > 0 {
match self.is_repeat() {
true => loop {
self.play_once().await?;
self.current_time = self.loopback;
},
false => self.play_once().await?,
};
}
self.reset();
Ok(())
}
pub(crate) async fn play_once(&mut self) -> Result<(), Error> {
let start_time = SystemTime::now();
let total_duration = self.get_duration();
let theoretical_timeframe_duration = 1000u64 / self.fps as u64;
let mut realtime_timeframe_duration;
while self.current_time < total_duration {
let realtime_start = SystemTime::now();
for track in &mut self.tracks {
track.play_frame([
self.current_time,
self.current_time + theoretical_timeframe_duration,
])?;
}
let realtime_end = SystemTime::now();
realtime_timeframe_duration = realtime_end
.duration_since(realtime_start)
.unwrap()
.as_millis() as u64;
if realtime_timeframe_duration < theoretical_timeframe_duration {
pause!(theoretical_timeframe_duration - realtime_timeframe_duration);
}
self.current_time = start_time.elapsed().unwrap().as_millis() as u64;
}
Ok(())
}
pub(crate) fn reset(&mut self) {
self.current_time = 0;
}
pub fn get_duration(&self) -> u64 {
match !self.tracks.is_empty() {
false => 0,
true => {
let longest_track = self
.tracks
.iter()
.max_by(|x, y| x.get_duration().cmp(&y.get_duration()))
.unwrap();
longest_track.get_duration()
}
}
}
pub fn get_progress(&self) -> u64 {
self.current_time
}
pub fn is_repeat(&self) -> bool {
self.repeat
}
pub fn get_loopback(&self) -> u64 {
self.loopback
}
pub fn get_speed(&self) -> u8 {
self.speed
}
pub fn get_fps(&self) -> u8 {
self.fps
}
pub fn get_tracks(&self) -> &Vec<Track> {
&self.tracks
}
pub fn set_repeat(mut self, repeat: bool) -> Self {
self.repeat = repeat;
self
}
pub fn set_loopback(mut self, loopback: u64) -> Self {
self.loopback = loopback;
self
}
pub fn set_speed(mut self, speed: u8) -> Self {
self.speed = speed;
self
}
pub fn set_fps(mut self, fps: u8) -> Self {
self.fps = fps;
self
}
pub fn set_tracks(mut self, tracks: Vec<Track>) -> Self {
self.tracks = tracks;
self
}
pub fn with_track(mut self, track: Track) -> Self {
self.tracks.push(track);
self
}
}
impl Default for Segment {
fn default() -> Self {
Segment {
repeat: false,
loopback: 0,
speed: 100,
fps: 60,
tracks: vec![],
current_time: 0,
}
}
}
impl Display for Segment {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Segment: {} tracks - duration: {}ms",
self.tracks.len(),
self.get_duration()
)
}
}
#[cfg(test)]
mod tests {
use std::time::SystemTime;
use crate::animations::Track;
use crate::animations::{Keyframe, Segment};
use crate::mocks::output_device::MockOutputDevice;
#[test]
fn test_segment_default() {
let segment = Segment::default();
assert!(!segment.is_repeat());
assert_eq!(segment.get_loopback(), 0);
assert_eq!(segment.get_speed(), 100);
assert_eq!(segment.get_fps(), 60);
assert!(segment.get_tracks().is_empty());
assert_eq!(segment.get_duration(), 0);
}
#[test]
fn test_segment_setters() {
let segment = Segment::default()
.set_repeat(true)
.set_loopback(100)
.set_speed(150)
.set_fps(100)
.set_tracks(vec![
Track::new(MockOutputDevice::new(50)),
Track::new(MockOutputDevice::new(100)),
]);
assert!(segment.is_repeat());
assert_eq!(segment.get_loopback(), 100);
assert_eq!(segment.get_speed(), 150);
assert_eq!(segment.get_fps(), 100);
assert_eq!(segment.get_tracks().len(), 2);
}
#[test]
fn test_segment_reset() {
let mut segment = Segment {
current_time: 100,
..Default::default()
};
segment.reset();
assert_eq!(segment.get_progress(), 0);
}
#[test]
fn test_segment_duration() {
let segment = Segment::default().set_tracks(vec![
Track::new(MockOutputDevice::new(50))
.with_keyframe(Keyframe::new(10, 0, 500))
.with_keyframe(Keyframe::new(20, 600, 4000)),
Track::new(MockOutputDevice::new(100))
.with_keyframe(Keyframe::new(10, 3000, 3300))
.with_keyframe(Keyframe::new(20, 3500, 3800)),
]);
assert_eq!(segment.get_duration(), 4000);
}
#[tokio::test]
async fn test_segment_play_once() {
let mut segment = Segment::default()
.set_tracks(vec![
Track::new(MockOutputDevice::new(50))
.with_keyframe(Keyframe::new(10, 0, 100))
.with_keyframe(Keyframe::new(20, 200, 300)),
Track::new(MockOutputDevice::new(100)).with_keyframe(Keyframe::new(10, 300, 500)),
])
.set_fps(100);
assert_eq!(segment.get_progress(), 0);
let start = SystemTime::now();
let play_once = segment.play_once().await;
let elapsed = start.elapsed().unwrap().as_millis();
assert!(play_once.is_ok());
assert!(
(500..550).contains(&elapsed),
"Play once takes longer approx. the time of the longest track: {}",
elapsed
);
assert!(segment.get_progress() >= 500)
}
#[tokio::test]
async fn test_segment_play() {
let mut segment = Segment::default()
.set_tracks(vec![
Track::new(MockOutputDevice::new(50)).with_keyframe(Keyframe::new(10, 0, 100))
])
.set_fps(100);
let start = SystemTime::now();
let play = segment.play().await;
let elapsed = start.elapsed().unwrap().as_millis();
assert!(play.is_ok());
assert!(
(100..150).contains(&elapsed),
"Play takes the same time as play once: {}",
elapsed
);
let mut segment = segment.set_repeat(true);
tokio::select! {
_ = segment.play() => assert!(false, "Infinite play should not finish first"),
_ = tokio::time::sleep(std::time::Duration::from_millis(500)) => assert!(true, "Infinite play is infinite")
}
}
#[test]
fn test_track_to_segment() {
let segment = Segment::from(Track::new(MockOutputDevice::new(50)));
assert_eq!(segment.get_tracks().len(), 1);
}
#[test]
fn test_display_implementation() {
let segment = Segment::default()
.with_track(
Track::new(MockOutputDevice::new(50))
.with_keyframe(Keyframe::new(10, 0, 500))
.with_keyframe(Keyframe::new(20, 600, 4000)),
)
.with_track(
Track::new(MockOutputDevice::new(100))
.with_keyframe(Keyframe::new(10, 3000, 3300))
.with_keyframe(Keyframe::new(20, 3500, 3800)),
);
let expected_display = "Segment: 2 tracks - duration: 4000ms";
assert_eq!(format!("{}", segment), expected_display);
}
}