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}