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}