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 these coordinates.
27    /// Coordinates should be row and column indices with respect to the source matrix.
28    fn get<T: Into<MatrixCoordinates>>(
29        &self,
30        coordinates: T,
31    ) -> Result<&C, CoordinatesOutOfBoundsError>;
32}
33
34/// Trait to handle mutable matrix operations.
35pub trait MatrixMut<C> {
36    /// Set a certain cell's value.
37    /// Coordinates should be row and column indices with respect to the source matrix.
38    fn set<T: Into<MatrixCoordinates>>(
39        &mut self,
40        coordinates: T,
41        value: C,
42    ) -> Result<C, CoordinatesOutOfBoundsError>;
43}
44
45/// Domain in two directions.
46pub trait Domain2D {
47    /// Range of rows included in this object's domain.
48    fn row_range(&self) -> Range<usize>;
49
50    /// Range of columns included in this object's domain.
51    fn col_range(&self) -> Range<usize>;
52
53    /// The area spanned by this domain.
54    fn range(&self) -> MatrixRange {
55        MatrixRange::from((self.row_range(), self.col_range()))
56    }
57
58    /// Starting coordinate of this domain (inclusive).
59    fn start(&self) -> MatrixCoordinates {
60        MatrixCoordinates::new(self.row_range().start, self.col_range().start)
61    }
62
63    /// Ending coordinate of this domain (exclusive).
64    fn end(&self) -> MatrixCoordinates {
65        MatrixCoordinates::new(self.row_range().end, self.col_range().end)
66    }
67
68    /// The middle point of this cluster as a tuple of (rows, columns), fractional.
69    fn middle(&self) -> (f64, f64) {
70        let start = self.start();
71        let end = self.end();
72        let rows = start.row as f64 + 0.5 * (end.row - start.row) as f64;
73        let cols = start.col as f64 + 0.5 * (end.col - start.col) as f64;
74        (rows, cols)
75    }
76}
77impl Domain2D for ((usize, usize), (usize, usize)) {
78    fn row_range(&self) -> Range<usize> {
79        self.0.0..self.1.0
80    }
81    fn col_range(&self) -> Range<usize> {
82        self.0.1..self.1.1
83    }
84}
85impl Domain2D for (Range<usize>, Range<usize>) {
86    fn row_range(&self) -> Range<usize> {
87        self.0.clone()
88    }
89    fn col_range(&self) -> Range<usize> {
90        self.1.clone()
91    }
92}
93impl<D: Domain2D, T: Domain2D> Contains2D<D> for T {
94    fn contains(&self, other: &D) -> bool {
95        let plus_one = MatrixRange {
96            start: self.start(),
97            end: self.end() + MatrixCoordinates::new(1, 1),
98        };
99
100        self.contains(&other.start()) && plus_one.contains(&other.end())
101    }
102
103    fn overlaps(&self, other: &D) -> bool {
104        let plus_one = MatrixRange {
105            start: self.start(),
106            end: self.end() + MatrixCoordinates::new(1, 1),
107        };
108
109        self.contains(&other.start()) || plus_one.contains(&other.end()) || other.contains(self)
110    }
111}
112impl<T: Domain2D> Contains2D<MatrixCoordinates> for T {
113    fn contains(&self, other: &MatrixCoordinates) -> bool {
114        self.row_range().contains(&other.row) && self.col_range().contains(&other.col)
115    }
116}
117
118/// Dimensions in two directions.
119pub trait Dimensions2D {
120    /// Number of rows in the output.
121    fn n_rows(&self) -> usize;
122
123    /// Number of columns in the output.
124    fn n_cols(&self) -> usize;
125
126    /// Dimensions of the output as a dimensions object.
127    fn dimensions(&self) -> MatrixDimensions {
128        MatrixDimensions::new(self.n_rows(), self.n_cols())
129    }
130
131    /// Length of these dimensions if it were a 1D vector.
132    fn len(&self) -> usize {
133        self.n_rows() * self.n_cols()
134    }
135
136    /// Whether these dimensions are (0,0).
137    fn is_empty(&self) -> bool {
138        self.n_rows() == 0 && self.n_cols() == 0
139    }
140}
141impl<D: Domain2D> Dimensions2D for D {
142    fn n_rows(&self) -> usize {
143        self.row_range().len()
144    }
145    fn n_cols(&self) -> usize {
146        self.col_range().len()
147    }
148}
149impl Dimensions2D for (usize, usize) {
150    fn n_rows(&self) -> usize {
151        self.0
152    }
153    fn n_cols(&self) -> usize {
154        self.1
155    }
156}
157impl Dimensions2D for (isize, isize) {
158    fn n_rows(&self) -> usize {
159        self.0 as usize
160    }
161    fn n_cols(&self) -> usize {
162        self.1 as usize
163    }
164}
165
166/// Whether this instance contains or covers something else.
167pub trait Contains2D<T> {
168    /// Whether this instance contains or covers the other.
169    fn contains(&self, other: &T) -> bool;
170
171    /// Whether this instance overlaps the other.
172    fn overlaps(&self, other: &T) -> bool {
173        self.contains(other)
174    }
175}