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<usize>;
47    /// Range of columns included in this object's domain.
48    fn col_range(&self) -> Range<usize>;
49    /// The area spanned by this domain.
50    fn range(&self) -> MatrixRange {
51        MatrixRange::from((self.row_range(), self.col_range()))
52    }
53    /// Starting coordinate of this domain (inclusive).
54    fn start(&self) -> MatrixCoordinates {
55        MatrixCoordinates::new(self.row_range().start, self.col_range().start)
56    }
57    /// Ending coordinate of this domain (exclusive).
58    fn end(&self) -> MatrixCoordinates {
59        MatrixCoordinates::new(self.row_range().end, self.col_range().end)
60    }
61    /// The middle point of this cluster as a tuple of (rows, columns), fractional.
62    fn middle(&self) -> (f64, f64) {
63        let start = self.start();
64        let end = self.end();
65        let rows = start.row as f64 + 0.5 * (end.row - start.row) as f64;
66        let cols = start.col as f64 + 0.5 * (end.col - start.col) as f64;
67        (rows, cols)
68    }
69}
70impl Domain2D for ((usize, usize), (usize, usize)) {
71    fn row_range(&self) -> Range<usize> {
72        self.0.0..self.1.0
73    }
74    fn col_range(&self) -> Range<usize> {
75        self.0.1..self.1.1
76    }
77}
78impl Domain2D for (Range<usize>, Range<usize>) {
79    fn row_range(&self) -> Range<usize> {
80        self.0.clone()
81    }
82    fn col_range(&self) -> Range<usize> {
83        self.1.clone()
84    }
85}
86impl<D: Domain2D, T: Domain2D> Contains2D<D> for T {
87    fn contains(&self, other: &D) -> bool {
88        let plus_one = MatrixRange {
89            start: self.start(),
90            end: self.end() + MatrixCoordinates { row: 1, col: 1 },
91        };
92
93        self.contains(&other.start()) && plus_one.contains(&other.end())
94    }
95}
96impl<T: Domain2D> Contains2D<MatrixCoordinates> for T {
97    fn contains(&self, other: &MatrixCoordinates) -> bool {
98        self.row_range().contains(&other.row) && self.col_range().contains(&other.col)
99    }
100}
101
102/// Dimensions in two directions.
103pub trait Dimensions2D {
104    /// Number of rows in the output.
105    fn n_rows(&self) -> usize;
106    /// Number of columns in the output.
107    fn n_cols(&self) -> usize;
108    /// Dimensions of the output as a dimensions object.
109    fn dimensions(&self) -> MatrixDimensions {
110        MatrixDimensions::new(self.n_rows(), self.n_cols())
111    }
112    /// Length of these dimensions if it were a 1D vector.
113    fn len(&self) -> usize {
114        self.n_rows() * self.n_cols()
115    }
116    /// Whether these dimensions are (0,0).
117    fn is_empty(&self) -> bool {
118        self.n_rows() == 0 && self.n_cols() == 0
119    }
120}
121impl<D: Domain2D> Dimensions2D for D {
122    fn n_rows(&self) -> usize {
123        self.row_range().len()
124    }
125    fn n_cols(&self) -> usize {
126        self.col_range().len()
127    }
128}
129impl Dimensions2D for (usize, usize) {
130    fn n_rows(&self) -> usize {
131        self.0
132    }
133    fn n_cols(&self) -> usize {
134        self.1
135    }
136}
137
138/// Whether this instance contains or covers something else.
139pub trait Contains2D<T> {
140    /// Whether this instance contains or covers the other.
141    fn contains(&self, other: &T) -> bool;
142}