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}