hyperion/instance/
smoothing.rs1use std::time::{Duration, Instant};
2
3use crate::models;
4
5pub struct Smoothing {
9 config: models::Smoothing,
10 led_data: Vec<models::Color>,
11 current_data: Vec<models::Color16>,
12 target_data: Vec<models::Color16>,
13 target_time: Instant,
14 previous_write_time: Instant,
15 next_update: Option<Instant>,
16}
17
18impl Smoothing {
19 pub fn new(config: models::Smoothing, led_count: usize) -> Self {
20 let now = Instant::now();
21
22 Self {
23 config,
24 led_data: vec![Default::default(); led_count],
25 current_data: vec![Default::default(); led_count],
26 target_data: vec![Default::default(); led_count],
27 target_time: now,
28 previous_write_time: now,
29 next_update: None,
30 }
31 }
32
33 fn plan_update(&mut self, now: Instant) -> SmoothingUpdate {
35 if self.config.enable && now < self.target_time {
36 let next_update = self.next_update.unwrap_or(
38 now + Duration::from_micros(
39 1_000_000_000 / (1000. * self.config.update_frequency) as u64,
40 ),
41 );
42
43 self.next_update = Some(next_update);
44
45 let delta_time = (self.target_time - now).as_micros() as u64;
47 let k = 1.
48 - 1. * (delta_time as f32)
49 / (self.target_time - self.previous_write_time).as_micros() as f32;
50
51 for (tgt, prev) in self.target_data.iter().zip(self.current_data.iter_mut()) {
53 let r_diff = tgt.red as i32 - prev.red as i32;
54 let g_diff = tgt.green as i32 - prev.green as i32;
55 let b_diff = tgt.blue as i32 - prev.blue as i32;
56
57 prev.red = (prev.red as i32 + r_diff.signum() * (k * r_diff.abs() as f32) as i32)
58 .clamp(0, 65535) as u16;
59 prev.green = (prev.green as i32
60 + g_diff.signum() * (k * g_diff.abs() as f32) as i32)
61 .clamp(0, 65535) as u16;
62 prev.blue = (prev.blue as i32 + b_diff.signum() * (k * b_diff.abs() as f32) as i32)
63 .clamp(0, 65535) as u16;
64 }
65 } else {
66 if self.config.enable {
68 self.next_update = None;
69 } else {
70 self.next_update = Some(now);
72 }
73
74 self.current_data.copy_from_slice(&self.target_data);
76 }
77
78 for (src, dst) in self.current_data.iter().zip(self.led_data.iter_mut()) {
80 *dst = crate::color::color_to8(*src);
81 }
82
83 if self.next_update.is_some() {
84 SmoothingUpdate::Running
85 } else {
86 SmoothingUpdate::Settled
87 }
88 }
89
90 pub fn set_target(&mut self, color_data: &[models::Color16]) {
91 self.target_data.copy_from_slice(color_data);
93
94 let now = Instant::now();
96 self.previous_write_time = now;
97 self.target_time = now + Duration::from_millis(self.config.time_ms as _);
98
99 self.plan_update(now);
100 }
101
102 pub async fn update(&mut self) -> (&[models::Color], SmoothingUpdate) {
103 if let Some(next_update) = self.next_update {
104 if next_update > Instant::now() {
106 tokio::time::sleep_until(next_update.into()).await
107 }
108
109 self.next_update = None;
111 let update = self.plan_update(Instant::now());
112
113 (&self.led_data, update)
114 } else {
115 futures::future::pending().await
117 }
118 }
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum SmoothingUpdate {
123 Running,
124 Settled,
125}