ratio_matrix/
traits.rs

1//! # Common traits for working with 2D (matrix) data.
2//!
3//! ## License
4//!
5//! This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
6//! If a copy of the MPL was not distributed with this file,
7//! You can obtain one at <https://mozilla.org/MPL/2.0/>.
8//!
9//! **Code examples both in the docstrings and rendered documentation are free to use.**
10
11use std::ops::Range;
12
13use crate::{
14    CoordinatesOutOfBoundsError, MatrixCoordinates, MatrixDimensionError, MatrixDimensions,
15    MatrixRange,
16};
17
18/// 2D matrix behavior.
19pub trait Matrix2D<C>: Dimensions2D + Domain2D + MatrixRef<C> + MatrixMut<C> + Sized {
20    /// Create a new matrix from this value slice with the given number of columns in a row.
21    fn new(values: Vec<C>, n_cols: usize) -> Result<Self, MatrixDimensionError>;
22}
23
24/// Trait to handle matrix data by reference.
25pub trait MatrixRef<C> {
26    /// Get a matrix value at this coordinate.
27    fn get<T: Into<MatrixCoordinates>>(
28        &self,
29        coordinates: T,
30    ) -> Result<&C, CoordinatesOutOfBoundsError>;
31}
32
33/// Trait to handle mutable matrix operations.
34pub trait MatrixMut<C> {
35    /// Set a certain cell's value.
36    fn set<T: Into<MatrixCoordinates>>(
37        &mut self,
38        coordinates: T,
39        value: C,
40    ) -> Result<C, CoordinatesOutOfBoundsError>;
41}
42
43/// Domain in two directions.
44pub trait Domain2D {
45    /// Range of rows included in this object's domain.
46    fn row_range(&self) -> Range<isize>;
47
48    /// Range of columns included in this object's domain.
49    fn col_range(&self) -> Range<isize>;
50
51    /// The area spanned by this domain.
52    fn range(&self) -> MatrixRange {
53        MatrixRange::from((self.row_range(), self.col_range()))
54    }
55
56    /// Starting coordinate of this domain (inclusive).
57    fn start(&self) -> MatrixCoordinates {
58        MatrixCoordinates::new(self.row_range().start, self.col_range().start)
59    }
60
61    /// Ending coordinate of this domain (exclusive).
62    fn end(&self) -> MatrixCoordinates {
63        MatrixCoordinates::new(self.row_range().end, self.col_range().end)
64    }
65
66    /// The middle point of this cluster as a tuple of (rows, columns), fractional.
67    fn middle(&self) -> (f64, f64) {
68        let start = self.start();
69        let end = self.end();
70        let rows = start.row as f64 + 0.5 * (end.row - start.row) as f64;
71        let cols = start.col as f64 + 0.5 * (end.col - start.col) as f64;
72        (rows, cols)
73    }
74}
75impl Domain2D for ((usize, usize), (usize, usize)) {
76    fn row_range(&self) -> Range<isize> {
77        self.0.0 as isize..self.1.0 as isize
78    }
79    fn col_range(&self) -> Range<isize> {
80        self.0.1 as isize..self.1.1 as isize
81    }
82}
83impl Domain2D for (Range<usize>, Range<usize>) {
84    fn row_range(&self) -> Range<isize> {
85        self.0.start as isize..self.0.end as isize
86    }
87    fn col_range(&self) -> Range<isize> {
88        self.1.start as isize..self.1.end as isize
89    }
90}
91impl Domain2D for ((isize, isize), (isize, isize)) {
92    fn row_range(&self) -> Range<isize> {
93        self.0.0..self.1.0
94    }
95    fn col_range(&self) -> Range<isize> {
96        self.0.1..self.1.1
97    }
98}
99impl Domain2D for (Range<isize>, Range<isize>) {
100    fn row_range(&self) -> Range<isize> {
101        self.0.clone()
102    }
103    fn col_range(&self) -> Range<isize> {
104        self.1.clone()
105    }
106}
107impl<D: Domain2D, T: Domain2D> Contains2D<D> for T {
108    fn contains(&self, other: &D) -> bool {
109        let plus_one = MatrixRange {
110            start: self.start(),
111            end: self.end() + MatrixCoordinates { row: 1, col: 1 },
112        };
113
114        self.contains(&other.start()) && plus_one.contains(&other.end())
115    }
116
117    fn overlaps(&self, other: &D) -> bool {
118        self.contains(&other.start())
119            || self.contains(&(other.end() - MatrixCoordinates::new(1, 1)))
120            || other.contains(self)
121    }
122}
123impl<T: Domain2D> Contains2D<MatrixCoordinates> for T {
124    fn contains(&self, other: &MatrixCoordinates) -> bool {
125        self.row_range().contains(&other.row) && self.col_range().contains(&other.col)
126    }
127}
128
129/// Dimensions in two directions.
130pub trait Dimensions2D {
131    /// Number of rows in the output.
132    fn n_rows(&self) -> usize;
133
134    /// Number of columns in the output.
135    fn n_cols(&self) -> usize;
136
137    /// Dimensions of the output as a dimensions object.
138    fn dimensions(&self) -> MatrixDimensions {
139        MatrixDimensions::new(self.n_rows(), self.n_cols())
140    }
141
142    /// Length of these dimensions if it were a 1D vector.
143    fn len(&self) -> usize {
144        self.n_rows() * self.n_cols()
145    }
146
147    /// Whether these dimensions are (0,0).
148    fn is_empty(&self) -> bool {
149        self.n_rows() == 0 && self.n_cols() == 0
150    }
151}
152impl<D: Domain2D> Dimensions2D for D {
153    fn n_rows(&self) -> usize {
154        self.row_range().len()
155    }
156    fn n_cols(&self) -> usize {
157        self.col_range().len()
158    }
159}
160impl Dimensions2D for (usize, usize) {
161    fn n_rows(&self) -> usize {
162        self.0
163    }
164    fn n_cols(&self) -> usize {
165        self.1
166    }
167}
168impl Dimensions2D for (isize, isize) {
169    fn n_rows(&self) -> usize {
170        self.0 as usize
171    }
172    fn n_cols(&self) -> usize {
173        self.1 as usize
174    }
175}
176
177/// Whether this instance contains or covers something else.
178pub trait Contains2D<T> {
179    /// Whether this instance contains or covers the other.
180    fn contains(&self, other: &T) -> bool;
181
182    /// Whether this instance overlaps the other.
183    fn overlaps(&self, other: &T) -> bool {
184        self.contains(other)
185    }
186}