1use crate::data::DataSeries;
8use crate::error::ChartResult;
9use crate::time::{Milliseconds, TimeProvider};
10
11pub type Progress = u8;
18
19#[derive(Debug, Clone, Copy, PartialEq)]
21pub enum EasingFunction {
22 Linear,
24 EaseIn,
26 EaseOut,
28 EaseInOut,
30}
31
32impl EasingFunction {
33 pub fn apply(self, t: f32) -> f32 {
35 match self {
36 EasingFunction::Linear => t,
37 EasingFunction::EaseIn => t * t,
38 EasingFunction::EaseOut => 1.0 - (1.0 - t) * (1.0 - t),
39 EasingFunction::EaseInOut => {
40 if t < 0.5 {
41 2.0 * t * t
42 } else {
43 1.0 - 2.0 * (1.0 - t) * (1.0 - t)
44 }
45 }
46 }
47 }
48}
49
50pub trait Interpolatable: Clone {
52 fn interpolate(self, other: Self, progress: f32) -> Option<Self>;
54}
55
56impl Interpolatable for f32 {
57 fn interpolate(self, other: Self, progress: f32) -> Option<Self> {
58 Some(self + (other - self) * progress)
59 }
60}
61
62impl Interpolatable for i32 {
63 fn interpolate(self, other: Self, progress: f32) -> Option<Self> {
64 Some(self + ((other - self) as f32 * progress) as i32)
65 }
66}
67
68impl Interpolatable for crate::data::point::Point2D {
69 fn interpolate(self, other: Self, progress: f32) -> Option<Self> {
70 Some(crate::data::point::Point2D::new(
71 self.x + (other.x - self.x) * progress,
72 self.y + (other.y - self.y) * progress,
73 ))
74 }
75}
76
77impl Interpolatable for crate::data::point::IntPoint {
78 fn interpolate(self, other: Self, progress: f32) -> Option<Self> {
79 Some(crate::data::point::IntPoint::new(
80 self.x + ((other.x - self.x) as f32 * progress) as i32,
81 self.y + ((other.y - self.y) as f32 * progress) as i32,
82 ))
83 }
84}
85
86impl Interpolatable for crate::data::series::StaticDataSeries<crate::data::point::Point2D, 256> {
87 fn interpolate(self, other: Self, progress: f32) -> Option<Self> {
88 let mut result = crate::data::series::StaticDataSeries::new();
89
90 let min_len = self.len().min(other.len());
92
93 for i in 0..min_len {
94 if let (Some(from_point), Some(to_point)) = (self.get(i), other.get(i)) {
95 if let Some(interpolated_point) = from_point.interpolate(to_point, progress) {
96 if result.push(interpolated_point).is_err() {
97 return None; }
99 }
100 }
101 }
102
103 Some(result)
104 }
105}
106
107impl<const N: usize> Interpolatable for heapless::Vec<f32, N> {
108 fn interpolate(self, other: Self, progress: f32) -> Option<Self> {
109 let mut result = heapless::Vec::new();
110
111 let min_len = self.len().min(other.len());
113
114 for i in 0..min_len {
115 if let (Some(from_val), Some(to_val)) = (self.get(i), other.get(i)) {
116 let interpolated = from_val + (to_val - from_val) * progress;
117 if result.push(interpolated).is_err() {
118 return None; }
120 }
121 }
122
123 Some(result)
124 }
125}
126
127#[derive(Debug, Clone)]
132pub struct ChartAnimator<T: Interpolatable> {
133 from_state: T,
135 to_state: T,
137 easing: EasingFunction,
139}
140
141impl<T: Interpolatable> ChartAnimator<T> {
142 pub fn new(from: T, to: T, easing: EasingFunction) -> Self {
149 Self {
150 from_state: from,
151 to_state: to,
152 easing,
153 }
154 }
155
156 pub fn value_at(&self, progress: Progress) -> Option<T> {
164 let linear_progress = (progress as f32) / 100.0;
165 let eased_progress = self.easing.apply(linear_progress);
166 self.from_state
167 .clone()
168 .interpolate(self.to_state.clone(), eased_progress)
169 }
170
171 pub fn set_target(&mut self, new_to: T) {
176 self.to_state = new_to;
177 }
178
179 pub fn set_states(&mut self, new_from: T, new_to: T) {
185 self.from_state = new_from;
186 self.to_state = new_to;
187 }
188
189 pub fn from_state(&self) -> T {
191 self.from_state.clone()
192 }
193
194 pub fn to_state(&self) -> T {
196 self.to_state.clone()
197 }
198
199 pub fn easing(&self) -> EasingFunction {
201 self.easing
202 }
203
204 pub fn set_easing(&mut self, easing: EasingFunction) {
206 self.easing = easing;
207 }
208}
209
210#[derive(Debug, Clone)]
216pub struct MultiStateAnimator<T: Interpolatable, const N: usize> {
217 keyframes: heapless::Vec<(Progress, T), N>,
219 easing_functions: heapless::Vec<EasingFunction, N>,
221}
222
223impl<T: Interpolatable, const N: usize> MultiStateAnimator<T, N> {
224 pub fn new() -> Self {
226 Self {
227 keyframes: heapless::Vec::new(),
228 easing_functions: heapless::Vec::new(),
229 }
230 }
231
232 pub fn add_keyframe(
242 &mut self,
243 progress: Progress,
244 state: T,
245 easing: EasingFunction,
246 ) -> ChartResult<()> {
247 self.keyframes.push((progress, state)).map_err(|_| {
248 crate::error::ChartError::DataError(crate::error::DataError::BUFFER_FULL)
249 })?;
250
251 self.easing_functions.push(easing).map_err(|_| {
252 crate::error::ChartError::DataError(crate::error::DataError::BUFFER_FULL)
253 })?;
254
255 self.keyframes.sort_by_key(|(progress, _)| *progress);
257
258 Ok(())
259 }
260
261 pub fn value_at(&self, progress: Progress) -> Option<T> {
269 if self.keyframes.is_empty() {
270 return None;
271 }
272
273 if self.keyframes.len() == 1 {
274 return Some(self.keyframes[0].1.clone());
275 }
276
277 let mut from_idx = 0;
279 let mut to_idx = 0;
280
281 for (i, (keyframe_progress, _)) in self.keyframes.iter().enumerate() {
282 if *keyframe_progress <= progress {
283 from_idx = i;
284 } else {
285 to_idx = i;
286 break;
287 }
288 }
289
290 if from_idx == self.keyframes.len() - 1 {
292 return Some(self.keyframes[from_idx].1.clone());
293 }
294
295 if to_idx == 0 && from_idx > 0 {
297 to_idx = self.keyframes.len() - 1;
298 }
299
300 let (from_progress, from_state) = &self.keyframes[from_idx];
301 let (to_progress, to_state) = &self.keyframes[to_idx];
302
303 let progress_range = to_progress.saturating_sub(*from_progress);
305 if progress_range == 0 {
306 return Some(from_state.clone());
307 }
308
309 let local_progress =
310 (progress.saturating_sub(*from_progress) as f32) / (progress_range as f32);
311 let easing = self
312 .easing_functions
313 .get(to_idx)
314 .copied()
315 .unwrap_or(EasingFunction::Linear);
316 let eased_progress = easing.apply(local_progress);
317
318 from_state
319 .clone()
320 .interpolate(to_state.clone(), eased_progress)
321 }
322
323 pub fn keyframe_count(&self) -> usize {
325 self.keyframes.len()
326 }
327
328 pub fn clear(&mut self) {
330 self.keyframes.clear();
331 self.easing_functions.clear();
332 }
333}
334
335impl<T: Interpolatable, const N: usize> Default for MultiStateAnimator<T, N> {
336 fn default() -> Self {
337 Self::new()
338 }
339}
340
341#[derive(Debug)]
347pub struct StreamingAnimator<T: Copy + Clone> {
348 buffer: crate::memory::ManagedSlidingWindow<T, 100>,
350 interpolation_progress: Progress,
352 smooth_interpolation: bool,
354}
355
356impl<T: Copy + Clone> StreamingAnimator<T> {
357 pub fn new() -> Self {
359 Self {
360 buffer: crate::memory::ManagedSlidingWindow::new(),
361 interpolation_progress: 0,
362 smooth_interpolation: true,
363 }
364 }
365
366 pub fn push_data(&mut self, point: T) {
371 self.buffer.push(point);
372 self.interpolation_progress = 0;
374 }
375
376 pub fn current_data(&self) -> impl Iterator<Item = T> + '_ {
378 self.buffer.iter()
379 }
380
381 pub fn set_interpolation_progress(&mut self, progress: Progress) {
386 self.interpolation_progress = progress;
387 }
388
389 pub fn interpolation_progress(&self) -> Progress {
391 self.interpolation_progress
392 }
393
394 pub fn set_smooth_interpolation(&mut self, enabled: bool) {
399 self.smooth_interpolation = enabled;
400 }
401
402 pub fn is_smooth_interpolation_enabled(&self) -> bool {
404 self.smooth_interpolation
405 }
406
407 pub fn capacity(&self) -> usize {
409 100 }
411
412 pub fn len(&self) -> usize {
414 self.buffer.len()
415 }
416
417 pub fn is_empty(&self) -> bool {
419 self.buffer.is_empty()
420 }
421
422 pub fn clear(&mut self) {
424 self.buffer.clear();
425 self.interpolation_progress = 0;
426 }
427
428 pub fn update_with_delta(
436 &mut self,
437 _delta_time: Milliseconds,
438 ) -> crate::error::AnimationResult<bool> {
439 Ok(false)
442 }
443}
444
445impl<T: Copy + Clone> Default for StreamingAnimator<T> {
446 fn default() -> Self {
447 Self::new()
448 }
449}
450
451#[derive(Debug, Clone)]
457pub struct TimeBasedProgress {
458 duration_ms: Milliseconds,
460 start_time_ms: Option<Milliseconds>,
462 looping: bool,
464}
465
466impl TimeBasedProgress {
467 pub fn new(duration_ms: Milliseconds) -> Self {
472 Self {
473 duration_ms,
474 start_time_ms: None,
475 looping: false,
476 }
477 }
478
479 pub fn new_looping(duration_ms: Milliseconds) -> Self {
484 Self {
485 duration_ms,
486 start_time_ms: None,
487 looping: true,
488 }
489 }
490
491 pub fn progress_from_time<T: TimeProvider>(&mut self, time_provider: &T) -> Progress {
499 let current_time = time_provider.current_time_ms();
500
501 if self.start_time_ms.is_none() {
503 self.start_time_ms = Some(current_time);
504 return 0;
505 }
506
507 let start_time = self.start_time_ms.unwrap();
508 let elapsed = current_time.saturating_sub(start_time);
509
510 if self.looping {
511 let cycle_progress = elapsed % self.duration_ms;
513 ((cycle_progress as f32 / self.duration_ms as f32) * 100.0) as Progress
514 } else {
515 if elapsed >= self.duration_ms {
517 100
518 } else {
519 ((elapsed as f32 / self.duration_ms as f32) * 100.0) as Progress
520 }
521 }
522 }
523
524 pub fn progress_from_elapsed(&self, elapsed_ms: Milliseconds) -> Progress {
532 if self.looping {
533 let cycle_progress = elapsed_ms % self.duration_ms;
534 ((cycle_progress as f32 / self.duration_ms as f32) * 100.0) as Progress
535 } else if elapsed_ms >= self.duration_ms {
536 100
537 } else {
538 ((elapsed_ms as f32 / self.duration_ms as f32) * 100.0) as Progress
539 }
540 }
541
542 pub fn reset(&mut self) {
544 self.start_time_ms = None;
545 }
546
547 pub fn is_complete<T: TimeProvider>(&self, time_provider: &T) -> bool {
552 if self.looping {
553 return false; }
555
556 if let Some(start_time) = self.start_time_ms {
557 let current_time = time_provider.current_time_ms();
558 let elapsed = current_time.saturating_sub(start_time);
559 elapsed >= self.duration_ms
560 } else {
561 false }
563 }
564
565 pub fn duration_ms(&self) -> Milliseconds {
567 self.duration_ms
568 }
569
570 pub fn set_duration_ms(&mut self, duration_ms: Milliseconds) {
572 self.duration_ms = duration_ms;
573 }
574
575 pub fn is_looping(&self) -> bool {
577 self.looping
578 }
579
580 pub fn set_looping(&mut self, looping: bool) {
582 self.looping = looping;
583 }
584}
585
586#[cfg(test)]
587mod tests {
588 use super::*;
589 use crate::time::ManualTimeProvider;
590
591 #[test]
592 fn test_easing_functions() {
593 assert_eq!(EasingFunction::Linear.apply(0.5), 0.5);
594 assert_eq!(EasingFunction::EaseIn.apply(0.5), 0.25);
595 assert_eq!(EasingFunction::EaseOut.apply(0.5), 0.75);
596 assert_eq!(EasingFunction::EaseInOut.apply(0.5), 0.5);
597 }
598
599 #[test]
600 fn test_interpolatable_f32() {
601 let result = 10.0f32.interpolate(20.0, 0.5);
602 assert_eq!(result, Some(15.0));
603 }
604
605 #[test]
606 fn test_interpolatable_i32() {
607 let result = 10i32.interpolate(20, 0.5);
608 assert_eq!(result, Some(15));
609 }
610
611 #[test]
612 fn test_chart_animator() {
613 let animator = ChartAnimator::new(0.0f32, 100.0, EasingFunction::Linear);
614
615 assert_eq!(animator.value_at(0), Some(0.0));
616 assert_eq!(animator.value_at(50), Some(50.0));
617 assert_eq!(animator.value_at(100), Some(100.0));
618 }
619
620 #[test]
621 fn test_chart_animator_easing() {
622 let animator = ChartAnimator::new(0.0f32, 100.0, EasingFunction::EaseIn);
623
624 let value_at_50 = animator.value_at(50).unwrap();
625 assert!(value_at_50 < 50.0); }
627
628 #[test]
629 fn test_multi_state_animator() {
630 let mut animator: MultiStateAnimator<f32, 4> = MultiStateAnimator::new();
631
632 animator
633 .add_keyframe(0, 0.0, EasingFunction::Linear)
634 .unwrap();
635 animator
636 .add_keyframe(50, 25.0, EasingFunction::Linear)
637 .unwrap();
638 animator
639 .add_keyframe(100, 100.0, EasingFunction::Linear)
640 .unwrap();
641
642 assert_eq!(animator.value_at(0), Some(0.0));
643 assert_eq!(animator.value_at(25), Some(12.5));
644 assert_eq!(animator.value_at(50), Some(25.0));
645 assert_eq!(animator.value_at(75), Some(62.5));
646 assert_eq!(animator.value_at(100), Some(100.0));
647 }
648
649 #[test]
650 fn test_streaming_animator() {
651 let mut animator = StreamingAnimator::new();
652
653 assert!(animator.is_empty());
654
655 animator.push_data(1.0f32);
656 animator.push_data(2.0f32);
657
658 assert_eq!(animator.len(), 2);
659 assert!(!animator.is_empty());
660
661 let data: heapless::Vec<f32, 100> = animator.current_data().collect();
662 let expected: heapless::Vec<f32, 100> = heapless::Vec::from_slice(&[1.0, 2.0]).unwrap();
663 assert_eq!(data, expected);
664 }
665
666 #[test]
667 fn test_time_based_progress() {
668 let mut progress_calc = TimeBasedProgress::new(1000); let mut time_provider = ManualTimeProvider::new();
670
671 assert_eq!(progress_calc.progress_from_time(&time_provider), 0);
673
674 time_provider.advance_ms(500);
676 assert_eq!(progress_calc.progress_from_time(&time_provider), 50);
677
678 time_provider.advance_ms(500);
680 assert_eq!(progress_calc.progress_from_time(&time_provider), 100);
681
682 time_provider.advance_ms(500);
684 assert_eq!(progress_calc.progress_from_time(&time_provider), 100);
685 }
686
687 #[test]
688 fn test_time_based_progress_looping() {
689 let mut progress_calc = TimeBasedProgress::new_looping(1000); let mut time_provider = ManualTimeProvider::new();
691
692 assert_eq!(progress_calc.progress_from_time(&time_provider), 0);
694
695 time_provider.advance_ms(1000);
697 assert_eq!(progress_calc.progress_from_time(&time_provider), 0);
698
699 time_provider.advance_ms(500);
701 assert_eq!(progress_calc.progress_from_time(&time_provider), 50);
702 }
703
704 #[test]
705 fn test_progress_from_elapsed() {
706 let progress_calc = TimeBasedProgress::new(2000); assert_eq!(progress_calc.progress_from_elapsed(0), 0);
709 assert_eq!(progress_calc.progress_from_elapsed(1000), 50);
710 assert_eq!(progress_calc.progress_from_elapsed(2000), 100);
711 assert_eq!(progress_calc.progress_from_elapsed(3000), 100); }
713}