use std::fmt::{Display, Formatter};
use crate::animations::Keyframe;
use crate::devices::Output;
use crate::errors::Error;
use crate::utils::{Range, State};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug)]
pub struct Track {
device: Box<dyn Output>,
keyframes: Vec<Keyframe>,
#[cfg_attr(feature = "serde", serde(skip))]
previous: State,
#[cfg_attr(feature = "serde", serde(skip))]
current: State,
}
impl Track {
#[allow(private_bounds)]
pub fn new<T: Output + 'static>(device: T) -> Self {
let history = device.get_state();
Self {
device: Box::new(device),
keyframes: vec![],
previous: history.clone(),
current: history,
}
}
pub fn get_duration(&self) -> u64 {
match !self.keyframes.is_empty() {
false => 0,
true => {
let last_keyframe = self
.keyframes
.iter()
.max_by(|x, y| x.get_end().cmp(&(y.get_end())))
.unwrap();
last_keyframe.get_end()
}
}
}
pub(crate) fn play_frame<F: Into<Range<u64>>>(&mut self, timeframe: F) -> Result<(), Error> {
let timeframe = timeframe.into();
let keyframe = self.get_best_keyframe(timeframe);
match keyframe {
None => (),
Some(keyframe) => {
self.update_history(keyframe.get_target());
let progress = keyframe.compute_target_coefficient(timeframe.end);
let state =
self.device
.scale_state(self.previous.clone(), keyframe.get_target(), progress);
self.device.set_state(state)?;
}
};
Ok(())
}
fn get_best_keyframe<R: Into<Range<u64>>>(&mut self, timeframe: R) -> Option<Keyframe> {
let timeframe = timeframe.into();
self.keyframes
.iter()
.filter(|kf| {
kf.get_start() >= timeframe.start && kf.get_start() < timeframe.end ||
kf.get_end() >= timeframe.start && kf.get_end() < timeframe.end ||
kf.get_start() <= timeframe.start && kf.get_end() > timeframe.end
})
.max_by(|a, b| a.get_end().cmp(&b.get_end()))
.cloned()
}
fn update_history(&mut self, new_state: State) {
if self.current != new_state {
self.previous = self.current.clone();
self.current = new_state;
}
}
pub fn get_device(&self) -> &dyn Output {
&*self.device
}
pub fn get_keyframes(&self) -> &Vec<Keyframe> {
&self.keyframes
}
#[allow(rustdoc::private_intra_doc_links)]
pub fn with_keyframe(mut self, keyframe: Keyframe) -> Self {
self.keyframes.push(keyframe);
self
}
}
impl Display for Track {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Track: {} keyframes - duration: {}ms",
self.keyframes.len(),
self.get_duration()
)
}
}
#[cfg(test)]
mod tests {
use crate::animations::Keyframe;
use crate::mocks::output_device::MockOutputDevice;
use crate::utils::Range;
use super::*;
#[test]
fn test_new_track() {
let actuator = MockOutputDevice::new(5);
let track = Track::new(actuator);
assert_eq!(track.get_keyframes().len(), 0);
assert_eq!(track.previous.as_integer(), 5);
assert_eq!(track.current.as_integer(), 5);
let track = track.with_keyframe(Keyframe::new(50, 0, 2000));
assert_eq!(track.get_keyframes().len(), 1);
}
#[test]
fn test_get_duration() {
let actuator = MockOutputDevice::new(5);
let track = Track::new(actuator);
assert_eq!(
track.get_duration(),
0,
"Track with no keyframe have a 0 duration."
);
let track = track
.with_keyframe(Keyframe::new(50, 0, 2000))
.with_keyframe(Keyframe::new(100, 500, 2200))
.with_keyframe(Keyframe::new(100, 100, 1000));
assert_eq!(
track.get_duration(),
2200,
"Track duration is the end time of the latest keyframe."
);
}
#[test]
fn test_update_history() {
let actuator = MockOutputDevice::new(5);
let mut track = Track::new(actuator);
assert_eq!(track.previous.as_integer(), 5);
assert_eq!(track.current.as_integer(), 5);
track.update_history(75.into());
assert_eq!(track.previous.as_integer(), 5); assert_eq!(track.current.as_integer(), 75);
track.update_history(100.into());
assert_eq!(track.previous.as_integer(), 75); assert_eq!(track.current.as_integer(), 100); }
#[test]
fn test_get_best_keyframe() {
let mut track = Track::new(MockOutputDevice::new(100))
.with_keyframe(Keyframe::new(60, 0, 2000))
.with_keyframe(Keyframe::new(70, 500, 2200))
.with_keyframe(Keyframe::new(80, 100, 2100));
let keyframe = track.get_best_keyframe([0, 100]);
assert!(keyframe.is_some());
assert_eq!(keyframe.unwrap().get_target().as_integer(), 60);
let keyframe = track.get_best_keyframe([300, 400]);
assert!(keyframe.is_some());
assert_eq!(keyframe.unwrap().get_target().as_integer(), 80);
let keyframe = track.get_best_keyframe([600, 800]);
assert!(keyframe.is_some());
assert_eq!(keyframe.unwrap().get_target().as_integer(), 70);
let keyframe = track.get_best_keyframe([3000, 3200]);
assert!(keyframe.is_none());
}
#[test]
fn test_play_frame_no_keyframes() {
let actuator = MockOutputDevice::new(5);
let mut track = Track::new(actuator);
let result = track.play_frame(Range {
start: 0,
end: 1000,
});
assert!(result.is_ok());
}
#[test]
fn test_play_frame() {
let actuator = MockOutputDevice::new(0);
let mut track = Track::new(actuator);
let result = track.play_frame([500, 1500]);
assert!(result.is_ok());
assert_eq!(track.previous.as_integer(), 0);
assert_eq!(track.current.as_integer(), 0);
let mut track = track
.with_keyframe(Keyframe::new(50, 0, 2000))
.with_keyframe(Keyframe::new(70, 500, 2500))
.with_keyframe(Keyframe::new(90, 100, 1000));
let result = track.play_frame([500, 1500]); assert!(result.is_ok());
assert_eq!(track.previous.as_integer(), 0);
assert_eq!(track.current.as_integer(), 70); assert_eq!(track.get_device().get_state().as_integer(), 35); }
#[test]
fn test_display_implementation() {
let track = Track::new(MockOutputDevice::new(5))
.with_keyframe(Keyframe::new(50, 0, 2000))
.with_keyframe(Keyframe::new(100, 500, 2200))
.with_keyframe(Keyframe::new(100, 100, 1000));
let expected_display = "Track: 3 keyframes - duration: 2200ms";
assert_eq!(format!("{}", track), expected_display);
}
}