spatial_led/spatial_led/
directional.rs

1use alloc::collections::BTreeSet;
2
3use crate::Vec2;
4use crate::{color::ColorType, led::Led, Filter, Sled};
5
6use smallvec::SmallVec;
7
8/// # directional read and write methods
9impl<Color: ColorType> Sled<Color> {
10    fn raycast_for_indices(&self, start: Vec2, dir: Vec2) -> SmallVec<[usize; 4]> {
11        let dist = 100_000.0;
12        let end = start + dir * dist;
13
14        let mut intersections = smallvec::smallvec![];
15        for (seg_index, segment) in self.line_segments.iter().enumerate() {
16            if let Some(t) = segment.intersects_line(start, end) {
17                let index = self.alpha_to_index(t, seg_index);
18                intersections.push(index);
19            }
20        }
21
22        intersections
23    }
24
25    /* direction setters/getters */
26
27    /// Returns A [Filter] containing each [LED](Led) in the given direction from the center point.
28    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
29    ///
30    /// If no LEDs exist at the given direction, the Filter will be empty.
31    ///
32    /// O(SEGMENTS)
33    pub fn at_dir(&self, dir: Vec2) -> Filter {
34        self.at_dir_from(dir, self.center_point)
35    }
36
37    /// Returns A [Filter] containing each [LED](Led) in the given direction from a given point.
38    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
39    ///
40    /// Currently returns no more than 4 LEDs, may change in the future.
41    pub fn at_dir_from(&self, dir: Vec2, pos: Vec2) -> Filter {
42        let intersecting_indices = self.raycast_for_indices(pos, dir);
43        intersecting_indices
44            .iter()
45            .map(|i| *i as u16)
46            .collect::<BTreeSet<u16>>()
47            .into()
48    }
49
50    /// Modulates the color of each [LED](Led) in the given direction from the center point.
51    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
52    ///
53    /// Returns false if there is no LED in that direction, true otherwise.
54    ///
55    /// O(SEGMENTS)
56    ///
57    ///```rust
58    ///# use spatial_led::{Sled, SledError, Vec2};
59    ///# use palette::rgb::Rgb;
60    ///# fn demo() -> Result<(), SledError> {
61    ///# let mut sled = Sled::<Rgb>::new("./benches/config.yap")?;
62    /// sled.modulate_at_dir(Vec2::new(0.0, 1.0), |led| led.color * 2.0);
63    ///# Ok(())
64    ///# }
65    /// ```
66    pub fn modulate_at_dir<F: Fn(&Led<Color>) -> Color>(
67        &mut self,
68        dir: Vec2,
69        color_rule: F,
70    ) -> bool {
71        self.modulate_at_dir_from(dir, self.center_point, color_rule)
72    }
73
74    /// Modulates the color of each [LED](Led) in the given direction from a given point.
75    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
76    ///
77    /// Returns false if there is no LED in that direction, true otherwise.
78    ///
79    /// O(SEGMENTS)
80    ///
81    ///```rust
82    ///# use spatial_led::{Sled, SledError, Vec2};
83    ///# use palette::rgb::Rgb;
84    ///# fn demo() -> Result<(), SledError> {
85    ///# let mut sled = Sled::<Rgb>::new("./benches/config.yap")?;
86    /// let dir = Vec2::new(-1.0, 0.0);
87    /// let from = Vec2::new(0.25, -0.6);
88    /// sled.modulate_at_dir_from(dir, from, |led| {
89    ///     led.color * 2.0
90    /// });
91    ///# Ok(())
92    ///# }
93    /// ```
94    pub fn modulate_at_dir_from<F: Fn(&Led<Color>) -> Color>(
95        &mut self,
96        dir: Vec2,
97        pos: Vec2,
98        color_rule: F,
99    ) -> bool {
100        let intersecting_indices = self.raycast_for_indices(pos, dir);
101
102        if intersecting_indices.is_empty() {
103            return false;
104        }
105
106        for index in intersecting_indices {
107            let led = &mut self.leds[index];
108            led.color = color_rule(led);
109        }
110
111        true
112    }
113
114    /// Sets the color of each [LED](Led) in the given direction from the center.
115    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
116    ///
117    /// Returns false if there is no LED in that direction, true otherwise.
118    ///
119    /// O(SEGMENTS)
120    pub fn set_at_dir(&mut self, dir: Vec2, color: Color) -> bool {
121        self.set_at_dir_from(dir, self.center_point, color)
122    }
123
124    /// Sets the color of each [LED](Led) in the given direction from a given point.
125    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
126    ///
127    /// Returns false if there is no LED in that direction, true otherwise.
128    ///
129    /// O(SEGMENTS)
130    pub fn set_at_dir_from(&mut self, dir: Vec2, pos: Vec2, color: Color) -> bool {
131        let intersecting_indices = self.raycast_for_indices(pos, dir);
132
133        if intersecting_indices.is_empty() {
134            return false;
135        }
136
137        for index in intersecting_indices {
138            self.leds[index].color = color;
139        }
140
141        true
142    }
143
144    /* angle setters/getters */
145
146    /// Returns A [Filter] containing each [LED](Led) whose direction relative to the center point forms a given radian angle.
147    ///
148    /// Equivalent to `at_dir(Vec2::new(angle.cos(), angle.sin()));`
149    ///
150    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
151    ///
152    /// If no LEDs exist at the given direction, the Filter will be empty.
153    ///
154    /// Currently returns no more than 4 LEDs, may change in the future.
155    ///
156    /// O(SEGMENTS)
157    pub fn at_angle(&self, angle: f32) -> Filter {
158        let dir = Vec2::from_angle(angle);
159        self.at_dir(dir)
160    }
161
162    /// Returns A [Filter] containing each [LED](Led) whose direction relative to a point forms a given radian angle.
163    ///
164    /// Equivalent to `at_dir_from(Vec2::new(angle.cos(), angle.sin()), pos);`
165    ///
166    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
167    ///
168    /// If no LEDs exist at the given direction, the Filter will be empty.
169    ///
170    /// Currently returns no more than 4 LEDs, may change in the future.
171    ///
172    /// O(SEGMENTS)
173    pub fn at_angle_from(&self, angle: f32, pos: Vec2) -> Filter {
174        let dir = Vec2::from_angle(angle);
175        self.at_dir_from(dir, pos)
176    }
177
178    /// Modulates the color of each [LED](Led) whose direction relative to the center point forms a given radian angle.
179    ///
180    /// Equivalent to `modulate_at_dir(Vec2::new(angle.cos(), angle.sin()), /*-snip-*/);`
181    ///
182    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
183    ///
184    /// Returns false if there is no LED at that angle, true otherwise.
185    ///
186    /// O(SEGMENTS)
187    ///
188    ///```rust
189    ///# use spatial_led::{Sled, SledError, Vec2};
190    ///# use palette::rgb::Rgb;
191    /// use core::f32::consts::PI;
192    ///# fn demo() -> Result<(), SledError> {
193    ///# let mut sled = Sled::<Rgb>::new("./benches/config.yap")?;
194    /// sled.modulate_at_angle(PI / 4.0, |led| led.color * 2.0);
195    ///# Ok(())
196    ///# }
197    /// ```
198    pub fn modulate_at_angle<F: Fn(&Led<Color>) -> Color>(
199        &mut self,
200        angle: f32,
201        color_rule: F,
202    ) -> bool {
203        self.modulate_at_angle_from(angle, self.center_point, color_rule)
204    }
205
206    /// Modulates the color of each [LED](Led) whose direction relative to a point forms a given radian angle.
207    ///
208    /// Equivalent to `modulate_at_dir_from(Vec2::new(angle.cos(), angle.sin()), pos, /*-snip-*/);`
209    ///
210    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
211    ///
212    /// Returns false if there is no LED at that angle, true otherwise.
213    ///
214    /// O(SEGMENTS)
215    ///
216    ///```rust
217    ///# use spatial_led::{Sled, SledError, Vec2};
218    ///# use palette::rgb::Rgb;
219    /// use core::f32::consts::PI;
220    ///# fn demo() -> Result<(), SledError> {
221    ///# let mut sled = Sled::<Rgb>::new("./benches/config.yap")?;
222    /// let angle = PI * 1.25;
223    /// let from = Vec2::new(0.3, 0.2);
224    /// sled.modulate_at_angle_from(angle, from, |led| led.color * 2.0);
225    ///# Ok(())
226    ///# }
227    /// ```
228    pub fn modulate_at_angle_from<F: Fn(&Led<Color>) -> Color>(
229        &mut self,
230        angle: f32,
231        pos: Vec2,
232        color_rule: F,
233    ) -> bool {
234        let dir = Vec2::from_angle(angle);
235        self.modulate_at_dir_from(dir, pos, color_rule)
236    }
237
238    /// Sets the color of each [LED](Led) whose direction relative to the center point forms a given radian angle.
239    /// Equivalent to `set_at_dir(Vec2::new(angle.cos(), angle.sin()), color);`
240    ///
241    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
242    ///
243    /// Returns false if there is no LED at that angle, true otherwise.
244    ///
245    /// O(SEGMENTS)
246    pub fn set_at_angle(&mut self, angle: f32, color: Color) -> bool {
247        self.set_at_angle_from(angle, self.center_point, color)
248    }
249
250    /// Sets the color of each [LED](Led) whose direction relative to a point forms a given radian angle.
251    /// Equivalent to `set_at_dir(Vec2::new(angle.cos(), angle.sin()), pos, color);`
252    ///
253    /// Calculated by performing a raycast against each line segment and finding the closest LED to the point of contact.
254    ///
255    /// Returns false if there is no LED at that angle, true otherwise.
256    ///
257    /// O(SEGMENTS)
258    pub fn set_at_angle_from(&mut self, angle: f32, pos: Vec2, color: Color) -> bool {
259        let dir = Vec2::from_angle(angle);
260        self.set_at_dir_from(dir, pos, color)
261    }
262}