spatial_led/driver/mod.rs
1use core::time::Duration;
2
3use alloc::boxed::Box;
4
5use crate::{color::ColorType, time::Instant, Led, Sled, SledError, Vec2};
6
7/// A driver representing instants with `std::time::Instant`
8#[cfg(feature = "std")]
9pub type Driver<COLOR> = CustomDriver<std::time::Instant, COLOR>;
10
11mod data;
12pub use data::Data;
13
14#[derive(Clone, Debug)]
15pub struct Time {
16 pub elapsed: Duration,
17 pub delta: Duration,
18}
19
20type SledResult = Result<(), SledError>;
21
22/// Drivers are useful for encapsulating everything you need to drive a complicated lighting effect all in one place.
23pub struct CustomDriver<INSTANT, COLOR>
24where
25 INSTANT: Instant,
26 COLOR: ColorType,
27{
28 sled: Option<Sled<COLOR>>,
29 startup_commands: Box<dyn Fn(&mut Sled<COLOR>, &mut Data) -> SledResult>,
30 compute_commands: Box<dyn Fn(&Sled<COLOR>, &mut Data, &Time) -> SledResult>,
31 draw_commands: Box<dyn Fn(&mut Sled<COLOR>, &Data, &Time) -> SledResult>,
32 startup: INSTANT,
33 last_update: INSTANT,
34
35 data: Data,
36}
37
38impl<INSTANT, COLOR> CustomDriver<INSTANT, COLOR>
39where
40 INSTANT: Instant,
41 COLOR: ColorType,
42{
43 pub fn new() -> Self {
44 CustomDriver {
45 sled: None,
46 startup_commands: Box::new(|_, _| Ok(())),
47 compute_commands: Box::new(|_, _, _| Ok(())),
48 draw_commands: Box::new(|_, _, _| Ok(())),
49 startup: INSTANT::now(),
50 last_update: INSTANT::now(),
51 data: Data::new(),
52 }
53 }
54
55 /// Returns `Some(&Sled)` if the Driver has been mounted, `None` if it hasn't.
56 pub fn sled(&self) -> Option<&Sled<COLOR>> {
57 self.sled.as_ref()
58 }
59
60 /// Returns a duration representing how long it has been since the Driver was initially [mounted](Driver::mount).
61 pub fn elapsed(&self) -> Duration {
62 self.startup.elapsed()
63 }
64
65 /// Define commands to be called as soon as a Sled is [mounted](CustomDriver::mount) to the driver. This is a good place to initialize important data.
66 /// ```rust
67 /// # use spatial_led::{Vec2, Sled, SledResult, driver::{Driver, Data}};
68 /// # use palette::rgb::Rgb;
69 ///
70 /// fn startup(_: &mut Sled<Rgb>, data: &mut Data) -> SledResult {
71 /// let streak_positions = vec![
72 /// Vec2::new(-1.2, 0.3),
73 /// Vec2::new(0.9, 1.6),
74 /// Vec2::new(0.4, -2.3),
75 /// ];
76 ///
77 /// data.set("positions", streak_positions);
78 /// Ok(())
79 /// }
80 ///
81 /// pub fn main() {
82 /// let mut driver = Driver::<Rgb>::new();
83 /// driver.set_startup_commands(startup);
84 /// }
85 /// ```
86 pub fn set_startup_commands<F: Fn(&mut Sled<COLOR>, &mut Data) -> SledResult + 'static>(
87 &mut self,
88 startup_commands: F,
89 ) {
90 self.startup_commands = Box::new(startup_commands);
91 }
92
93 /// Define commands to be called each time [CustomDriver::step()] is called, right before we run [draw commands](CustomDriver::set_draw_commands).
94 /// ```rust
95 ///# use spatial_led::{Vec2, Sled, SledResult, driver::{Driver, Data, Time}};
96 ///# use palette::rgb::Rgb;
97 /// const WIND: Vec2 = Vec2::new(0.25, 1.5);
98 ///
99 /// fn compute(_: &Sled<Rgb>, data: &mut Data, time: &Time) -> SledResult {
100 /// let streak_positions: &mut Vec<Vec2> = data.get_mut("positions")?;
101 /// let elapsed = time.elapsed.as_secs_f32();
102 /// for p in streak_positions {
103 /// *p += WIND * elapsed
104 /// }
105 /// Ok(())
106 /// }
107 ///
108 /// pub fn main() {
109 /// let mut driver = Driver::<Rgb>::new();
110 /// driver.set_compute_commands(compute);
111 /// }
112 ///
113 /// ```
114 pub fn set_compute_commands<F: Fn(&Sled<COLOR>, &mut Data, &Time) -> SledResult + 'static>(
115 &mut self,
116 compute_commands: F,
117 ) {
118 self.compute_commands = Box::new(compute_commands);
119 }
120
121 /// Define commands to be called each time [CustomDriver::step()] is called, right after we run [compute commands](CustomDriver::set_compute_commands).
122 /// ```rust
123 /// # use spatial_led::{Sled, Vec2, SledResult, driver::{Driver, Time, Data}};
124 /// # use palette::rgb::Rgb;
125 ///
126 /// fn draw(sled: &mut Sled<Rgb>, data: &Data, _:&Time) -> SledResult {
127 /// // gradually fade all LEDs to black
128 /// sled.map(|led| led.color * 0.95);
129 ///
130 /// // For each position in our vector, draw white in the direction to it.
131 /// let streak_positions = data.get::<Vec<Vec2>>("positions")?;
132 /// let center = sled.center_point();
133 /// for pos in streak_positions {
134 /// let dir = (pos - center).normalize();
135 /// sled.set_at_dir(dir, Rgb::new(1.0, 1.0, 1.0));
136 /// }
137 /// Ok(())
138 /// }
139 ///
140 /// pub fn main() {
141 /// let mut driver = Driver::new();
142 /// driver.set_draw_commands(draw);
143 /// }
144 ///
145 /// ```
146 pub fn set_draw_commands<F: Fn(&mut Sled<COLOR>, &Data, &Time) -> SledResult + 'static>(
147 &mut self,
148 draw_commands: F,
149 ) {
150 self.draw_commands = Box::new(draw_commands);
151 }
152
153 /// Takes ownership of the given Sled and runs the Driver's [startup commands](Driver::set_startup_commands).
154 pub fn mount(&mut self, mut sled: Sled<COLOR>) {
155 (self.startup_commands)(&mut sled, &mut self.data).unwrap();
156 self.startup = INSTANT::now();
157 self.last_update = self.startup;
158 self.sled = Some(sled);
159 }
160
161 /// Runs the Driver's [compute commands](CustomDriver::set_compute_commands) first, and then runs its [draw commands](CustomDriver::set_draw_commands).
162 pub fn step(&mut self) {
163 if let Some(sled) = &mut self.sled {
164 let time = Time {
165 elapsed: self.startup.elapsed(),
166 delta: self.last_update.elapsed(),
167 };
168
169 self.last_update = INSTANT::now();
170 (self.compute_commands)(sled, &mut self.data, &time).unwrap();
171 (self.draw_commands)(sled, &self.data, &time).unwrap();
172 }
173 }
174
175 pub fn step_by(&mut self, delta: Duration) {
176 self.startup -= delta;
177 self.step();
178 }
179
180 /// Returns full ownership over the Driver's assigned Sled. Panics if [Driver::mount()] was never called.
181 pub fn dismount(&mut self) -> Sled<COLOR> {
182 self.sled.take().unwrap()
183 }
184
185 /// See [Sled::leds()].
186 pub fn leds(&self) -> impl Iterator<Item = &Led<COLOR>> {
187 if let Some(sled) = &self.sled {
188 sled.leds()
189 } else {
190 panic!("Driver has no Sled assigned!")
191 }
192 }
193
194 /// See [Sled::colors()].
195 pub fn colors(&self) -> impl Iterator<Item = &COLOR> + '_ {
196 if let Some(sled) = &self.sled {
197 sled.colors()
198 } else {
199 panic!("Driver has no Sled assigned!")
200 }
201 }
202
203 /// See [Sled::positions()].
204 pub fn positions(&self) -> impl Iterator<Item = Vec2> + '_ {
205 if let Some(sled) = &self.sled {
206 sled.positions()
207 } else {
208 panic!("Driver has no Sled assigned!")
209 }
210 }
211
212 pub fn colors_and_positions(&self) -> impl Iterator<Item = (COLOR, Vec2)> + '_ {
213 if let Some(sled) = &self.sled {
214 sled.colors_and_positions()
215 } else {
216 panic!("Driver has no Sled assigned!")
217 }
218 }
219
220 /// Returns a reference to the Driver's Data. Helpful for displaying data values to the program user.
221 pub fn data(&self) -> &Data {
222 &self.data
223 }
224
225 /// Returns a mutable reference to the Driver's Data. Helpful for changing data values as the user provides input to the program.
226 pub fn data_mut(&mut self) -> &mut Data {
227 &mut self.data
228 }
229}
230
231impl<INSTANT, COLOR> Default for CustomDriver<INSTANT, COLOR>
232where
233 INSTANT: Instant,
234 COLOR: ColorType,
235{
236 fn default() -> Self {
237 Self::new()
238 }
239}