ratio_matrix/
traits.rs

1//! Common traits for working with 2D (matrix) data.
2
3use std::ops::Range;
4
5use crate::{
6    CoordinatesOutOfBoundsError, MatrixCoordinates, MatrixDimensionError, MatrixDimensions,
7    MatrixRange,
8};
9
10/// 2D matrix behavior.
11pub trait Matrix2D<C>: Dimensions2D + Domain2D + MatrixRef<C> + MatrixMut<C> + Sized {
12    /// Create a new matrix from this value slice with the given number of columns in a row.
13    fn new(values: Vec<C>, n_cols: usize) -> Result<Self, MatrixDimensionError>;
14}
15
16/// Trait to handle matrix data by reference.
17pub trait MatrixRef<C> {
18    /// Get a matrix value at this coordinate.
19    fn get<T: Into<MatrixCoordinates>>(
20        &self,
21        coordinates: T,
22    ) -> Result<&C, CoordinatesOutOfBoundsError>;
23}
24
25/// Trait to handle mutable matrix operations.
26pub trait MatrixMut<C> {
27    /// Set a certain cell's value.
28    fn set<T: Into<MatrixCoordinates>>(
29        &mut self,
30        coordinates: T,
31        value: C,
32    ) -> Result<C, CoordinatesOutOfBoundsError>;
33}
34
35/// Domain in two directions.
36pub trait Domain2D {
37    /// Range of rows included in this object's domain.
38    fn row_range(&self) -> Range<usize>;
39    /// Range of columns included in this object's domain.
40    fn col_range(&self) -> Range<usize>;
41    /// The area spanned by this domain.
42    fn range(&self) -> MatrixRange {
43        MatrixRange::from((self.row_range(), self.col_range()))
44    }
45    /// Starting coordinate of this domain (inclusive).
46    fn start(&self) -> MatrixCoordinates {
47        MatrixCoordinates::new(self.row_range().start, self.col_range().start)
48    }
49    /// Ending coordinate of this domain (exclusive).
50    fn end(&self) -> MatrixCoordinates {
51        MatrixCoordinates::new(self.row_range().end, self.col_range().end)
52    }
53    /// The middle point of this cluster as a tuple of (rows, columns), fractional.
54    fn middle(&self) -> (f64, f64) {
55        let start = self.start();
56        let end = self.end();
57        let rows = start.row as f64 + 0.5 * (end.row - start.row) as f64;
58        let cols = start.col as f64 + 0.5 * (end.col - start.col) as f64;
59        (rows, cols)
60    }
61}
62impl Domain2D for ((usize, usize), (usize, usize)) {
63    fn row_range(&self) -> Range<usize> {
64        self.0 .0..self.1 .0
65    }
66    fn col_range(&self) -> Range<usize> {
67        self.0 .1..self.1 .1
68    }
69}
70impl Domain2D for (Range<usize>, Range<usize>) {
71    fn row_range(&self) -> Range<usize> {
72        self.0.clone()
73    }
74    fn col_range(&self) -> Range<usize> {
75        self.1.clone()
76    }
77}
78impl<D: Domain2D, T: Domain2D> Contains2D<D> for T {
79    fn contains(&self, other: &D) -> bool {
80        let plus_one = MatrixRange {
81            start: self.start(),
82            end: self.end() + MatrixCoordinates { row: 1, col: 1 },
83        };
84
85        self.contains(&other.start()) && plus_one.contains(&other.end())
86    }
87}
88impl<T: Domain2D> Contains2D<MatrixCoordinates> for T {
89    fn contains(&self, other: &MatrixCoordinates) -> bool {
90        self.row_range().contains(&other.row) && self.col_range().contains(&other.col)
91    }
92}
93
94/// Dimensions in two directions.
95pub trait Dimensions2D {
96    /// Number of rows in the output.
97    fn n_rows(&self) -> usize;
98    /// Number of columns in the output.
99    fn n_cols(&self) -> usize;
100    /// Dimensions of the output as a dimensions object.
101    fn dimensions(&self) -> MatrixDimensions {
102        MatrixDimensions::new(self.n_rows(), self.n_cols())
103    }
104    /// Length of these dimensions if it were a 1D vector.
105    fn len(&self) -> usize {
106        self.n_rows() * self.n_cols()
107    }
108    /// Whether these dimensions are (0,0).
109    fn is_empty(&self) -> bool {
110        self.n_rows() == 0 && self.n_cols() == 0
111    }
112}
113impl<D: Domain2D> Dimensions2D for D {
114    fn n_rows(&self) -> usize {
115        self.row_range().len()
116    }
117    fn n_cols(&self) -> usize {
118        self.col_range().len()
119    }
120}
121impl Dimensions2D for (usize, usize) {
122    fn n_rows(&self) -> usize {
123        self.0
124    }
125    fn n_cols(&self) -> usize {
126        self.1
127    }
128}
129
130/// Whether this instance contains or covers something else.
131pub trait Contains2D<T> {
132    /// Whether this instance contains or covers the other.
133    fn contains(&self, other: &T) -> bool;
134}