Skip to main content

hermes_five/animations/
segment.rs

1use std::fmt::{Display, Formatter};
2use std::time::SystemTime;
3
4use crate::animations::Track;
5use crate::errors::Error;
6use crate::pause;
7
8/// Represents an [`Animation`](crate::animations::Animation) unit, called a `Segment`.
9///
10/// A `Segment` is composed of multiple [`Track`](Track)s, each containing sets of [`Keyframe`](Track) associated with an [`Output`](crate::devices::Output).
11///
12/// The `Segment` plays the keyframes of its track in a logical and temporal order.
13/// - A segment searches for keyframe to execute and updates the associated devices based on a given
14///   number of times per second, as defined by its `fps` property.
15/// - A segment can be set to repeat in a loop within an [`Animation`](crate::animations::Animation) from a starting point in time called `loopback`.
16///
17/// # Example
18///
19/// Here is an example of defining a segment to animate a small robot with two actuators (a LED and a servo).
20/// The robot will perform a waving motion using its servo and LED.
21/// ```no_run
22/// use hermes_five::animations::{Easing, Keyframe, Segment, Track};
23/// use hermes_five::hardware::Board;
24/// use hermes_five::devices::{Led, Servo};
25/// use hermes_five::io::RemoteIo;
26///
27/// #[hermes_five::runtime]
28/// async fn main() {
29///     // Define a board on COM4.
30///     let board = Board::new(RemoteIo::new("COM4")).open();
31///
32///     // Define a servo attached to the board on PIN 9 (default servo position is 90°).
33///     let servo = Servo::new(&board, 9, 90).unwrap();
34///     // Create a track for the servo.
35///     let servo_track = Track::new(servo)
36///         // Turns the servo to 180° in 1000ms
37///         .with_keyframe(Keyframe::new(180, 0, 1000).set_transition(Easing::SineInOut))
38///         // Turns the servo to 0° in 1000ms
39///         .with_keyframe(Keyframe::new(0, 2000, 3000).set_transition(Easing::SineInOut));
40///
41///     // Define a LED attached to the board on PIN 13.
42///     let led = Led::new(&board, 13, false).unwrap();
43///     // Create a track for the LED.
44///     let led_track = Track::new(led)
45///         // Turns the LED fully (instantly)
46///         .with_keyframe(Keyframe::new(255, 0, 1))
47///         // Fade out the LED in 1000ms
48///         .with_keyframe(Keyframe::new(0, 2000, 3000));
49///
50///     // Create an animation Segment for this:
51///     let segment = Segment::default()
52///         .with_track(servo_track)
53///         .with_track(led_track)
54///         .set_repeat(true);
55/// }
56/// ```
57#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
58#[derive(Clone, Debug)]
59pub struct Segment {
60    /// Determines whether the segment should replay in a loop (starting from the [`Segment::loopback`] time).
61    repeat: bool,
62    /// The point in time (in ms) the animation will restart the loop when `loop` is set to true (default: 0).
63    loopback: u64,
64    /// Controls the speed of the animation. (default: 100)
65    /// Defined as a percentage of standard time. For example:
66    /// - 50% means time moves twice as slow, so 1000ms lasts 2000ms in real time.
67    /// - 200% means time moves twice as fast, so 1000ms lasts 500ms in real time
68    speed: u8,
69    /// The number of frames per second (fps) for running the animation (default: 40fps).
70    /// - Higher fps results in smoother animations.
71    /// - Desired `fps` is not guaranteed to be reached (specially high fps values).
72    /// - The `fps` can be overridden for each [`Segment`] in the animation.
73    fps: u8,
74    /// The tracks for this segment.
75    tracks: Vec<Track>,
76
77    // ########################################
78    // # Volatile utility data.
79    /// The current time (in ms) the segment is currently at.
80    #[cfg_attr(feature = "serde", serde(skip))]
81    current_time: u64,
82}
83
84impl From<Track> for Segment {
85    fn from(track: Track) -> Self {
86        Self::default().with_track(track)
87    }
88}
89
90impl Segment {
91    /// Plays the segment. If `repeat` is true, the segment will loop indefinitely.
92    pub async fn play(&mut self) -> Result<(), Error> {
93        if self.get_duration() > 0 {
94            match self.is_repeat() {
95                true => loop {
96                    self.play_once().await?;
97                    self.current_time = self.loopback;
98                },
99                false => self.play_once().await?,
100            };
101        }
102
103        self.reset();
104        Ok(())
105    }
106
107    /// Plays all tracks once only.
108    pub(crate) async fn play_once(&mut self) -> Result<(), Error> {
109        let start_time = SystemTime::now();
110
111        let total_duration = self.get_duration();
112        // The theoretical time a frame should take is defined by the `fps` settings for the segment.
113        // This is theoretical since this is the minimum amount of time we want a single timeframe to last.
114        // But it may take more time if the segment have to run multiple tracks / keyframes for this timeframe.
115        let theoretical_timeframe_duration = 1000u64 / self.fps as u64;
116        // The realtime a frame took: at this point this is 0ms, but we will measure that in the following.
117        let mut realtime_timeframe_duration;
118
119        // As long as we did not reach the segment duration, we know we have some work to do.
120        while self.current_time < total_duration {
121            // Take a measure of time here (will be used to measure realtime_timeframe_duration)
122            let realtime_start = SystemTime::now();
123
124            // The timeframe starts now (current_time).
125            // It ends, in theory, after a realtime_timeframe_duration amount of time.
126            // However, if realtime_timeframe_duration was longer on the last timeframe, we can
127            // anticipate it to be longer this time too.
128            // let timeframe_end =
129            //     self.current_time + theoretical_timeframe_duration.max(realtime_timeframe_duration);
130
131            // Ask each track to play the timeframe.
132            for track in &mut self.tracks {
133                track.play_frame([
134                    self.current_time,
135                    self.current_time + theoretical_timeframe_duration,
136                ])?;
137            }
138
139            // Here we can know how long actually took the timeframe.
140            let realtime_end = SystemTime::now();
141            realtime_timeframe_duration = realtime_end
142                .duration_since(realtime_start)
143                .unwrap()
144                .as_millis() as u64;
145
146            // If the timeframe took less time than expected: we need to wait.
147            if realtime_timeframe_duration < theoretical_timeframe_duration {
148                pause!(theoretical_timeframe_duration - realtime_timeframe_duration);
149            }
150
151            self.current_time = start_time.elapsed().unwrap().as_millis() as u64;
152        }
153
154        Ok(())
155    }
156
157    /// Resets the segment's current time to 0.
158    pub(crate) fn reset(&mut self) {
159        self.current_time = 0;
160        /*for track in &mut self.tracks {
161            track.get_device().as_mut().stop()
162        }*/
163    }
164
165    /// Gets the total duration of the segment.
166    ///
167    /// The duration is determined by the longest track in the segment.
168    pub fn get_duration(&self) -> u64 {
169        match !self.tracks.is_empty() {
170            false => 0,
171            true => {
172                let longest_track = self
173                    .tracks
174                    .iter()
175                    .max_by(|x, y| x.get_duration().cmp(&y.get_duration()))
176                    .unwrap();
177                longest_track.get_duration()
178            }
179        }
180    }
181
182    /// Gets the current play time.
183    pub fn get_progress(&self) -> u64 {
184        self.current_time
185    }
186    /// Checks if the segment should repeat.
187    pub fn is_repeat(&self) -> bool {
188        self.repeat
189    }
190    /// Returns the loopback time.
191    pub fn get_loopback(&self) -> u64 {
192        self.loopback
193    }
194    /// Returns the playback speed as a percentage (between 0% and 100%).
195    pub fn get_speed(&self) -> u8 {
196        self.speed
197    }
198    /// Returns the frames per second (fps) for the segment.
199    pub fn get_fps(&self) -> u8 {
200        self.fps
201    }
202    /// Returns the tracks in the segment.
203    pub fn get_tracks(&self) -> &Vec<Track> {
204        &self.tracks
205    }
206
207    /// Sets whether the segment should repeat and returns the updated segment.
208    pub fn set_repeat(mut self, repeat: bool) -> Self {
209        self.repeat = repeat;
210        self
211    }
212    /// Sets the loopback time and returns the updated segment.
213    pub fn set_loopback(mut self, loopback: u64) -> Self {
214        self.loopback = loopback;
215        self
216    }
217    /// Sets the playback speed as a percentage (between 0% and 100%) and returns the updated segment.
218    pub fn set_speed(mut self, speed: u8) -> Self {
219        self.speed = speed;
220        self
221    }
222    /// Sets the frames per second (fps) and returns the updated segment.
223    pub fn set_fps(mut self, fps: u8) -> Self {
224        self.fps = fps;
225        self
226    }
227    /// Sets the tracks for the segment and returns the updated segment.
228    pub fn set_tracks(mut self, tracks: Vec<Track>) -> Self {
229        self.tracks = tracks;
230        self
231    }
232
233    /// Adds a track to the segment and returns the updated segment.
234    pub fn with_track(mut self, track: Track) -> Self {
235        self.tracks.push(track);
236        self
237    }
238}
239
240impl Default for Segment {
241    fn default() -> Self {
242        Segment {
243            repeat: false,
244            loopback: 0,
245            speed: 100,
246            fps: 60,
247            tracks: vec![],
248            current_time: 0,
249        }
250    }
251}
252
253impl Display for Segment {
254    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
255        write!(
256            f,
257            "Segment: {} tracks - duration: {}ms",
258            self.tracks.len(),
259            self.get_duration()
260        )
261    }
262}
263
264#[cfg(test)]
265mod tests {
266    use std::time::SystemTime;
267
268    use crate::animations::Track;
269    use crate::animations::{Keyframe, Segment};
270    use crate::mocks::output_device::MockOutputDevice;
271
272    #[test]
273    fn test_segment_default() {
274        let segment = Segment::default();
275        assert!(!segment.is_repeat());
276        assert_eq!(segment.get_loopback(), 0);
277        assert_eq!(segment.get_speed(), 100);
278        assert_eq!(segment.get_fps(), 60);
279        assert!(segment.get_tracks().is_empty());
280        assert_eq!(segment.get_duration(), 0);
281    }
282
283    #[test]
284    fn test_segment_setters() {
285        let segment = Segment::default()
286            .set_repeat(true)
287            .set_loopback(100)
288            .set_speed(150)
289            .set_fps(100)
290            .set_tracks(vec![
291                Track::new(MockOutputDevice::new(50)),
292                Track::new(MockOutputDevice::new(100)),
293            ]);
294
295        assert!(segment.is_repeat());
296        assert_eq!(segment.get_loopback(), 100);
297        assert_eq!(segment.get_speed(), 150);
298        assert_eq!(segment.get_fps(), 100);
299        assert_eq!(segment.get_tracks().len(), 2);
300    }
301
302    #[test]
303    fn test_segment_reset() {
304        let mut segment = Segment {
305            current_time: 100,
306            ..Default::default()
307        };
308        segment.reset();
309        assert_eq!(segment.get_progress(), 0);
310    }
311
312    #[test]
313    fn test_segment_duration() {
314        let segment = Segment::default().set_tracks(vec![
315            Track::new(MockOutputDevice::new(50))
316                .with_keyframe(Keyframe::new(10, 0, 500))
317                .with_keyframe(Keyframe::new(20, 600, 4000)),
318            Track::new(MockOutputDevice::new(100))
319                .with_keyframe(Keyframe::new(10, 3000, 3300))
320                .with_keyframe(Keyframe::new(20, 3500, 3800)),
321        ]);
322
323        assert_eq!(segment.get_duration(), 4000);
324    }
325
326    #[tokio::test]
327    async fn test_segment_play_once() {
328        let mut segment = Segment::default()
329            .set_tracks(vec![
330                Track::new(MockOutputDevice::new(50))
331                    .with_keyframe(Keyframe::new(10, 0, 100))
332                    .with_keyframe(Keyframe::new(20, 200, 300)),
333                Track::new(MockOutputDevice::new(100)).with_keyframe(Keyframe::new(10, 300, 500)),
334            ])
335            .set_fps(100);
336
337        assert_eq!(segment.get_progress(), 0);
338        let start = SystemTime::now();
339        let play_once = segment.play_once().await;
340        let elapsed = start.elapsed().unwrap().as_millis();
341        assert!(play_once.is_ok());
342        assert!(
343            (500..550).contains(&elapsed),
344            "Play once takes longer approx. the time of the longest track: {}",
345            elapsed
346        );
347        assert!(segment.get_progress() >= 500)
348    }
349
350    #[tokio::test]
351    async fn test_segment_play() {
352        let mut segment = Segment::default()
353            .set_tracks(vec![
354                Track::new(MockOutputDevice::new(50)).with_keyframe(Keyframe::new(10, 0, 100))
355            ])
356            .set_fps(100);
357
358        // ########################################
359        // Play no-repeat
360
361        let start = SystemTime::now();
362        let play = segment.play().await;
363        let elapsed = start.elapsed().unwrap().as_millis();
364        assert!(play.is_ok());
365        assert!(
366            (100..150).contains(&elapsed),
367            "Play takes the same time as play once: {}",
368            elapsed
369        );
370
371        // ########################################
372        // Play repeat
373
374        let mut segment = segment.set_repeat(true);
375
376        tokio::select! {
377            _ = segment.play() => assert!(false, "Infinite play should not finish first"),
378            _ = tokio::time::sleep(std::time::Duration::from_millis(500)) => assert!(true, "Infinite play is infinite")
379        }
380    }
381
382    #[test]
383    fn test_track_to_segment() {
384        let segment = Segment::from(Track::new(MockOutputDevice::new(50)));
385        assert_eq!(segment.get_tracks().len(), 1);
386    }
387
388    #[test]
389    fn test_display_implementation() {
390        let segment = Segment::default()
391            .with_track(
392                Track::new(MockOutputDevice::new(50))
393                    .with_keyframe(Keyframe::new(10, 0, 500))
394                    .with_keyframe(Keyframe::new(20, 600, 4000)),
395            )
396            .with_track(
397                Track::new(MockOutputDevice::new(100))
398                    .with_keyframe(Keyframe::new(10, 3000, 3300))
399                    .with_keyframe(Keyframe::new(20, 3500, 3800)),
400            );
401
402        let expected_display = "Segment: 2 tracks - duration: 4000ms";
403        assert_eq!(format!("{}", segment), expected_display);
404    }
405}