psyche_utils/grid.rs
1//! Tool used to store and sample grid-like data.
2
3use crate::Scalar;
4use serde::{Deserialize, Serialize};
5use std::ops::{Add, Index, IndexMut, Mul};
6
7/// Collection that holds data in 2d grid-like manner.
8/// Grid can be:
9/// - accessed by inspection of each element;
10/// - filled with same value for all fields;
11/// - filled with values got from closure that produces value for each field individually;
12/// - sampled with any type that implements `GridSampler` trai.
13#[derive(Clone, Default, Serialize, Deserialize)]
14pub struct Grid<T> {
15 cols: usize,
16 rows: usize,
17 fields: Vec<T>,
18}
19
20impl<T> Grid<T> {
21 /// Creates new grid.
22 ///
23 /// # Arguments
24 /// * `cols` - Number of columns.
25 /// * `rows` - Number of rows.
26 /// * `value` - Initial value applied for each field.
27 ///
28 /// # Return
29 /// Instance of grid.
30 ///
31 /// # Example
32 /// ```
33 /// use psyche_utils::grid::{Grid, GridSamplerCluster};
34 ///
35 /// let grid = Grid::new(2, 2, 1.0);
36 /// let sampler = GridSamplerCluster::new((0, 0), (1, 1));
37 /// assert_eq!(grid.sample(sampler).unwrap(), (4.0, 4));
38 /// ```
39 #[inline]
40 pub fn new(cols: usize, rows: usize, value: T) -> Self
41 where
42 T: Clone,
43 {
44 Self {
45 cols,
46 rows,
47 fields: vec![value; cols * rows],
48 }
49 }
50
51 /// Gets number of columns.
52 ///
53 /// # Return
54 /// Number of columns.
55 ///
56 /// # Example
57 /// ```
58 /// use psyche_utils::grid::Grid;
59 ///
60 /// let grid = Grid::new(2, 2, 1.0);
61 /// assert_eq!(grid.cols(), 2);
62 /// ```
63 #[inline]
64 pub fn cols(&self) -> usize {
65 self.cols
66 }
67
68 /// Gets number of rows.
69 ///
70 /// # Return
71 /// Number of rows.
72 ///
73 /// # Example
74 /// ```
75 /// use psyche_utils::grid::Grid;
76 ///
77 /// let grid = Grid::new(2, 2, 1.0);
78 /// assert_eq!(grid.rows(), 2);
79 /// ```
80 #[inline]
81 pub fn rows(&self) -> usize {
82 self.rows
83 }
84
85 /// Gets slice of fields.
86 ///
87 /// # Return
88 /// Reference to slice of fields that holds.
89 ///
90 /// # Example
91 /// ```
92 /// use psyche_utils::grid::Grid;
93 ///
94 /// let grid = Grid::new(2, 2, 1.0);
95 /// assert_eq!(grid.fields(), &[1.0, 1.0, 1.0, 1.0]);
96 /// ```
97 #[inline]
98 pub fn fields(&self) -> &[T] {
99 &self.fields
100 }
101
102 /// Gets slice of fields.
103 ///
104 /// # Return
105 /// Mutable reference to slice of fields that holds.
106 ///
107 /// # Example
108 /// ```
109 /// use psyche_utils::grid::Grid;
110 ///
111 /// let mut grid = Grid::new(2, 2, 0.0);
112 /// let mut i = 1.0;
113 /// for field in grid.fields_mut() {
114 /// *field = i;
115 /// i += 1.0;
116 /// }
117 /// assert_eq!(grid.fields(), &[1.0, 2.0, 3.0, 4.0]);
118 /// ```
119 #[inline]
120 pub fn fields_mut(&mut self) -> &mut [T] {
121 &mut self.fields
122 }
123
124 /// Fiils grid with same value.
125 ///
126 /// # Arguments
127 /// * `value` - Value that will be applied to each field.
128 ///
129 /// # Example
130 /// ```
131 /// use psyche_utils::grid::Grid;
132 ///
133 /// let mut grid = Grid::new(2, 2, 0.0);
134 /// grid.fill_all(1.0);
135 /// assert_eq!(grid.fields(), &[1.0, 1.0, 1.0, 1.0]);
136 /// ```
137 #[inline]
138 pub fn fill_all(&mut self, value: T)
139 where
140 T: Clone,
141 {
142 self.fields = vec![value; self.cols * self.rows];
143 }
144
145 /// Fiils grid with same value to fields contained by specified bounds.
146 ///
147 /// # Arguments
148 /// * `col_row` - Starting column and row.
149 /// * `size` - Number of columns and rows of bounds.
150 /// * `value` - Value that will be applied to each field.
151 ///
152 /// # Example
153 /// ```
154 /// use psyche_utils::grid::Grid;
155 ///
156 /// let mut grid = Grid::new(2, 2, 0.0);
157 /// grid.fill((1, 0), (1, 2), 1.0);
158 /// assert_eq!(grid.fields(), &[0.0, 1.0, 0.0, 1.0]);
159 /// ```
160 pub fn fill(&mut self, col_row: (usize, usize), size: (usize, usize), value: T)
161 where
162 T: Clone,
163 {
164 for y in col_row.1.min(self.rows)..(col_row.1 + size.1).min(self.rows) {
165 for x in col_row.0.min(self.cols)..(col_row.0 + size.0).min(self.cols) {
166 let index = y * self.cols + x;
167 self.fields[index] = value.clone();
168 }
169 }
170 }
171
172 /// Fiils grid with values got from producer closure.
173 ///
174 /// # Arguments
175 /// * `with` - Closure that will produce value for each field based on their col-row coords.
176 ///
177 /// # Example
178 /// ```
179 /// use psyche_utils::grid::Grid;
180 ///
181 /// let mut grid = Grid::new(2, 2, 0.0);
182 /// grid.fill_with(|col, row| Some((col + row) as f32));
183 /// assert_eq!(grid.fields(), &[0.0, 1.0, 1.0, 2.0]);
184 /// ```
185 pub fn fill_with<F>(&mut self, mut with: F)
186 where
187 F: FnMut(usize, usize) -> Option<T>,
188 {
189 for y in 0..self.rows {
190 for x in 0..self.cols {
191 let index = y * self.cols + x;
192 if let Some(value) = with(x, y) {
193 self.fields[index] = value;
194 }
195 }
196 }
197 }
198
199 /// Inspect and/or edit fields with closure.
200 ///
201 /// # Arguments
202 /// * `with` - Closure that will inspect and allow to edit each field.
203 ///
204 /// # Example
205 /// ```
206 /// use psyche_utils::grid::Grid;
207 ///
208 /// let mut grid = Grid::new(2, 2, 0.0);
209 /// grid.with(|col, row, field| *field = (col + row) as f32);
210 /// assert_eq!(grid.fields(), &[0.0, 1.0, 1.0, 2.0]);
211 /// ```
212 pub fn with<F>(&mut self, mut with: F)
213 where
214 F: FnMut(usize, usize, &mut T),
215 {
216 for (index, field) in self.fields.iter_mut().enumerate() {
217 let x = index % self.cols;
218 let y = index / self.rows;
219 with(x, y, field);
220 }
221 }
222
223 /// Sample grid fields using given sampler.
224 ///
225 /// # Arguments
226 /// * `sampler` - Sampler object that implements `GridSampler` trait.
227 ///
228 /// # Example
229 /// ```
230 /// use psyche_utils::grid::{Grid, GridSamplerCluster};
231 ///
232 /// let grid = Grid::new(2, 2, 1.0);
233 /// let sampler = GridSamplerCluster::new((0, 0), (1, 1));
234 /// assert_eq!(grid.sample(sampler).unwrap(), (4.0, 4));
235 /// ```
236 pub fn sample<S, W>(&self, sampler: S) -> Option<(T, W)>
237 where
238 S: GridSampler<T, W>,
239 {
240 sampler.sample(self)
241 }
242}
243
244impl<T> Index<(usize, usize)> for Grid<T> {
245 type Output = T;
246
247 #[inline]
248 fn index(&self, index: (usize, usize)) -> &T {
249 let index = index.1 * self.cols + index.0;
250 self.fields.index(index)
251 }
252}
253
254impl<T> IndexMut<(usize, usize)> for Grid<T> {
255 #[inline]
256 fn index_mut(&mut self, index: (usize, usize)) -> &mut T {
257 let index = index.1 * self.cols + index.0;
258 self.fields.index_mut(index)
259 }
260}
261
262impl<T> Index<[usize; 2]> for Grid<T> {
263 type Output = T;
264
265 #[inline]
266 fn index(&self, index: [usize; 2]) -> &T {
267 let index = index[1] * self.cols + index[0];
268 self.fields.index(index)
269 }
270}
271
272impl<T> IndexMut<[usize; 2]> for Grid<T> {
273 #[inline]
274 fn index_mut(&mut self, index: [usize; 2]) -> &mut T {
275 let index = index[1] * self.cols + index[0];
276 self.fields.index_mut(index)
277 }
278}
279
280/// Trait used to sample pair of single value and weight from grid.
281pub trait GridSampler<T, W> {
282 /// Sample value and weight from given grid.
283 ///
284 /// # Arguments
285 /// * `grid` - Grid that we sample from.
286 ///
287 /// # Return
288 /// Pair of single value and weight as result of grid sampling.
289 ///
290 /// # Example
291 /// ```
292 /// use psyche_utils::grid::{Grid, GridSampler};
293 ///
294 /// struct MySampler;
295 ///
296 /// impl GridSampler<f32, usize> for MySampler {
297 /// fn sample(self, grid: &Grid<f32>) -> Option<(f32, usize)> {
298 /// let value = grid.fields().iter().cloned().sum();
299 /// let weight = grid.fields().len();
300 /// Some((value, weight))
301 /// }
302 /// }
303 ///
304 /// let grid = Grid::new(2, 2, 1.0);
305 /// let sampler = MySampler {};
306 /// assert_eq!(grid.sample(sampler).unwrap(), (4.0, 4));
307 /// ```
308 fn sample(self, grid: &Grid<T>) -> Option<(T, W)>;
309}
310
311/// Trait used to obtain zero value for given type. It is used by built-in samplers and it's
312/// implemented for `f32` and `f64` types so if you want to sample any other type of grid, you have
313/// implement this trait for that type.
314pub trait GridSampleZeroValue<T> {
315 /// produce zero value for given type.
316 ///
317 /// # Return
318 /// Zero value of given type.
319 ///
320 /// # Example
321 /// ```
322 /// use psyche_utils::grid::{Grid, GridSamplerCluster, GridSampleZeroValue};
323 /// use std::ops::Add;
324 ///
325 /// #[derive(Debug, Copy, Clone, Eq, PartialEq)]
326 /// struct Integer(pub i32);
327 ///
328 /// impl Add for Integer {
329 /// type Output = Integer;
330 /// fn add(self, other: Integer) -> Integer { Integer(self.0 + other.0) }
331 /// }
332 ///
333 /// impl GridSampleZeroValue<Self> for Integer {
334 /// fn sample_zero_value() -> Self { Integer(0) }
335 /// }
336 ///
337 /// let grid = Grid::new(2, 2, Integer(1));
338 /// let sampler = GridSamplerCluster::new((0, 0), (1, 1));
339 /// assert_eq!(grid.sample(sampler).unwrap(), (Integer(4), 4));
340 /// ```
341 fn sample_zero_value() -> T;
342}
343
344impl GridSampleZeroValue<Self> for f32 {
345 fn sample_zero_value() -> Self {
346 0.0
347 }
348}
349
350impl GridSampleZeroValue<Self> for f64 {
351 fn sample_zero_value() -> Self {
352 0.0
353 }
354}
355
356/// Grid sampler that sum fields contained by cluster bounds.
357///
358/// # Note
359/// Weight component of sampling result equals number of sampled fields.
360///
361/// # Example
362/// ```
363/// use psyche_utils::grid::{Grid, GridSamplerCluster};
364///
365/// let grid = Grid::new(2, 2, 1.0);
366/// let sampler = GridSamplerCluster::new((0, 0), (1, 1));
367/// assert_eq!(grid.sample(sampler).unwrap(), (4.0, 4));
368/// ```
369#[derive(Debug, Clone, PartialEq, Eq)]
370pub struct GridSamplerCluster {
371 /// Bounds column and row starting point.
372 pub from: (usize, usize),
373 /// Bounds number of columns and rows that defines cluster size.
374 pub to: (usize, usize),
375}
376
377impl GridSamplerCluster {
378 #[inline]
379 pub fn new(from: (usize, usize), to: (usize, usize)) -> Self {
380 Self { from, to }
381 }
382
383 pub fn center(center: (usize, usize), size: (usize, usize)) -> Self {
384 let extents = (size.0 / 2, size.1 / 2);
385 let from = (
386 if extents.0 > center.0 {
387 0
388 } else {
389 center.0 - extents.0
390 },
391 if extents.1 > center.1 {
392 0
393 } else {
394 center.1 - extents.1
395 },
396 );
397 let to = (center.0 + extents.0, center.1 + extents.1);
398 Self { from, to }
399 }
400
401 pub fn center_extents(center: (usize, usize), extents: (usize, usize)) -> Self {
402 let from = (
403 if extents.0 > center.0 {
404 0
405 } else {
406 center.0 - extents.0
407 },
408 if extents.1 > center.1 {
409 0
410 } else {
411 center.1 - extents.1
412 },
413 );
414 let to = (center.0 + extents.0, center.1 + extents.1);
415 Self { from, to }
416 }
417
418 pub fn valid_from(&self) -> (usize, usize) {
419 (self.from.0.min(self.to.0), self.from.1.min(self.to.1))
420 }
421
422 pub fn valid_to(&self) -> (usize, usize) {
423 (self.from.0.max(self.to.0), self.from.1.max(self.to.1))
424 }
425
426 pub fn cells(&self) -> usize {
427 let from = self.valid_from();
428 let to = self.valid_to();
429 (to.0 - from.0) * (to.1 - from.1)
430 }
431}
432
433impl<T> GridSampler<T, usize> for GridSamplerCluster
434where
435 T: GridSampleZeroValue<T> + Add<Output = T> + Clone,
436{
437 fn sample(self, grid: &Grid<T>) -> Option<(T, usize)> {
438 if grid.cols() > 0 && grid.rows() > 0 {
439 let from = self.valid_from();
440 let mut to = self.valid_to();
441 to.0 = to.0.min(grid.cols() - 1);
442 to.1 = to.1.min(grid.rows() - 1);
443 let mut result = T::sample_zero_value();
444 let mut count = 0;
445 for y in from.1..=to.1 {
446 for x in from.0..=to.0 {
447 result = result + grid[(x, y)].clone();
448 count += 1;
449 }
450 }
451 Some((result, count))
452 } else {
453 None
454 }
455 }
456}
457
458/// Grid sampler that uses field distance to center and maximum range - each field is scaled by
459/// weight produced from that distance-in-range equation.
460///
461/// # Note
462/// Weight component of sampling result equals sum of weights of each sampled fields.
463///
464/// # Example
465/// ```
466/// use psyche_utils::grid::{Grid, GridSamplerDistance};
467///
468/// let grid = Grid::new(2, 2, 1.0);
469/// let sampler = GridSamplerDistance::new((0.0, 0.0), 1.0, (1.0, 1.0));
470/// assert_eq!(grid.sample(sampler).unwrap(), (1.0, 1.0));
471/// ```
472#[derive(Debug, Clone, PartialEq)]
473pub struct GridSamplerDistance {
474 /// XY scalar position of sampler center.
475 pub center: (Scalar, Scalar),
476 /// Range of sampling.
477 pub range: Scalar,
478 /// Scale mapping between grid cell and world cell.
479 pub cell_size: (Scalar, Scalar),
480}
481
482impl GridSamplerDistance {
483 #[inline]
484 pub fn new(center: (Scalar, Scalar), range: Scalar, cell_size: (Scalar, Scalar)) -> Self {
485 Self {
486 center,
487 range,
488 cell_size,
489 }
490 }
491}
492
493impl<T> GridSampler<T, Scalar> for GridSamplerDistance
494where
495 T: GridSampleZeroValue<T> + Add<Output = T> + Clone + Mul<Scalar, Output = T>,
496{
497 fn sample(self, grid: &Grid<T>) -> Option<(T, Scalar)> {
498 if grid.cols() > 0 && grid.rows() > 0 {
499 let mut result = T::sample_zero_value();
500 let mut total_weight = 0.0;
501 for y in 0..grid.rows() {
502 for x in 0..grid.cols() {
503 let value = grid[(x, y)].clone();
504 let dx = x as Scalar * self.cell_size.0 - self.center.0;
505 let dy = y as Scalar * self.cell_size.1 - self.center.1;
506 let distance = (dx * dx + dy * dy).sqrt();
507 if distance < self.range {
508 let weight = 1.0 - distance / self.range;
509 result = result + value * weight;
510 total_weight += weight;
511 }
512 }
513 }
514 Some((result, total_weight))
515 } else {
516 None
517 }
518 }
519}
520
521#[cfg(test)]
522mod tests {
523 use super::*;
524
525 #[test]
526 fn test_sample() {
527 let sampler = GridSamplerCluster::center((1, 1), (2, 2));
528 assert_eq!(sampler, GridSamplerCluster::new((0, 0), (2, 2)));
529
530 let grid = Grid::new(3, 3, 1.0);
531 let sample = grid.sample(sampler).unwrap();
532 assert_eq!((sample.0 as i32, sample.1), (9, 9));
533
534 let sampler = GridSamplerCluster::center((0, 0), (2, 2));
535 let sample = grid.sample(sampler).unwrap();
536 assert_eq!((sample.0 as i32, sample.1), (4, 4));
537
538 let sampler = GridSamplerCluster::center((2, 2), (2, 2));
539 let sample = grid.sample(sampler).unwrap();
540 assert_eq!((sample.0 as i32, sample.1), (4, 4));
541
542 let grid = Grid::new(9, 1, 1.0);
543 let sampler = GridSamplerDistance::new((0.0, 0.0), 3.0, (1.0, 1.0));
544 let sample = grid.sample(sampler).unwrap();
545
546 assert_eq!((sample.0 as i32, sample.1 as i32), (2, 2));
547 let sampler = GridSamplerDistance::new((4.0, 0.0), 3.0, (1.0, 1.0));
548 let sample = grid.sample(sampler).unwrap();
549 assert_eq!((sample.0 as i32, sample.1 as i32), (3, 3));
550 let sampler = GridSamplerDistance::new((8.0, 0.0), 3.0, (1.0, 1.0));
551 let sample = grid.sample(sampler).unwrap();
552 assert_eq!((sample.0 as i32, sample.1 as i32), (2, 2));
553 }
554}