simple_grid/index.rs
1/// A struct used for indexing into a grid.
2#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)]
3pub struct GridIndex(usize, usize);
4
5impl GridIndex {
6 /// Construct a new GridIndex.
7 pub fn new(column: usize, row: usize) -> Self {
8 Self(column, row)
9 }
10
11 /// Get the column (x) index.
12 pub fn column(&self) -> usize {
13 self.0
14 }
15
16 /// Get the row (y) index.
17 pub fn row(&self) -> usize {
18 self.1
19 }
20
21 /// Returns an iterator over the cardinal and ordinal neighbors of `self`.
22 ///
23 /// Returns the neighbors in clockwise order: `[up, up_right, right, down_right, down, down_left, left, up_left]`.
24 ///
25 /// ## Example
26 /// ```rust
27 /// # use simple_grid::GridIndex;
28 /// let idx = GridIndex::new(0, 1);
29 /// let neighbors: Vec<_> = idx.neighbors().collect();
30 /// assert_eq!(neighbors, vec![
31 /// (0, 0).into(), // up
32 /// (1, 0).into(), // up_right
33 /// (1, 1).into(), // right
34 /// (1, 2).into(), // down_right
35 /// (0, 2).into(), // down
36 /// // nothing to the left since `idx` has column=0
37 /// ]);
38 /// ```
39 pub fn neighbors(self) -> impl Iterator<Item = Self> {
40 use std::iter::once;
41 once::<fn(&Self) -> Option<Self>>(Self::up as _)
42 .chain(once(Self::up_right as _))
43 .chain(once(Self::right as _))
44 .chain(once(Self::down_right as _))
45 .chain(once(Self::down as _))
46 .chain(once(Self::down_left as _))
47 .chain(once(Self::left as _))
48 .chain(once(Self::up_left as _))
49 .filter_map(move |f| f(&self))
50 }
51
52 /// Returns an iterator over the cardinal neighbors of `self`.
53 ///
54 /// Returns the neighbors in clockwise order: `[up, right, down, left]`.
55 ///
56 /// ## Example
57 /// ```rust
58 /// # use simple_grid::GridIndex;
59 /// let idx = GridIndex::new(0, 1);
60 /// let neighbors: Vec<_> = idx.cardinal_neighbors().collect();
61 /// assert_eq!(neighbors, vec![
62 /// (0, 0).into(), // up
63 /// (1, 1).into(), // right
64 /// (0, 2).into(), // down
65 /// // nothing to the left since `idx` has column=0
66 /// ]);
67 /// ```
68 pub fn cardinal_neighbors(self) -> impl Iterator<Item = Self> {
69 use std::iter::once;
70 once::<fn(&Self) -> Option<Self>>(Self::up as _)
71 .chain(once(Self::right as _))
72 .chain(once(Self::down as _))
73 .chain(once(Self::left as _))
74 .filter_map(move |f| f(&self))
75 }
76
77 /// Get the `GridIndex` above, if it exists.
78 ///
79 /// ## Example
80 /// ```rust
81 /// # use simple_grid::GridIndex;
82 /// let row_5 = GridIndex::new(17, 5);
83 /// assert_eq!(row_5.up(), Some(GridIndex::new(17, 4)));
84 /// let row_0 = GridIndex::new(38, 0);
85 /// assert_eq!(row_0.up(), None);
86 /// ```
87 pub fn up(&self) -> Option<Self> {
88 if self.row() > 0 {
89 Some(Self::new(self.column(), self.row() - 1))
90 } else {
91 None
92 }
93 }
94
95 /// Get the `GridIndex` to the right, if it exists.
96 ///
97 /// ## Example
98 /// ```rust
99 /// # use simple_grid::GridIndex;
100 /// let column_17 = GridIndex::new(17, 11);
101 /// assert_eq!(column_17.right(), Some(GridIndex::new(18, 11)));
102 /// ```
103 pub fn right(&self) -> Option<Self> {
104 self.column()
105 .checked_add(1)
106 .map(|right| Self::new(right, self.row()))
107 }
108
109 /// Get the `GridIndex` below, if it exists.
110 ///
111 /// ## Example
112 /// ```rust
113 /// # use simple_grid::GridIndex;
114 /// let row_15 = GridIndex::new(3, 15);
115 /// assert_eq!(row_15.down(), Some(GridIndex::new(3, 16)));
116 /// ```
117 pub fn down(&self) -> Option<Self> {
118 self.row()
119 .checked_add(1)
120 .map(|down| Self::new(self.column(), down))
121 }
122
123 /// Get the `GridIndex` to the left, if it exists.
124 /// 0).
125 ///
126 /// ## Example
127 /// ```rust
128 /// # use simple_grid::GridIndex;
129 /// let column_9 = GridIndex::new(9, 10);
130 /// assert_eq!(column_9.left(), Some(GridIndex::new(8, 10)));
131 /// let column_0 = GridIndex::new(0, 10);
132 /// assert_eq!(column_0.left(), None);
133 /// ```
134 pub fn left(&self) -> Option<Self> {
135 if self.column() > 0 {
136 Some(Self::new(self.column() - 1, self.row()))
137 } else {
138 None
139 }
140 }
141
142 /// Get the `GridIndex` above and to the left, if it exists.
143 ///
144 /// ## Example
145 /// ```rust
146 /// # use simple_grid::{Grid, GridIndex};
147 /// let column_5_row_4 = GridIndex::new(5, 4);
148 /// assert_eq!(column_5_row_4.up_left(), Some(GridIndex::new(4, 3)));
149 /// let column_0_row_4 = GridIndex::new(0, 4);
150 /// assert_eq!(column_0_row_4.up_left(), None);
151 /// ```
152 pub fn up_left(&self) -> Option<Self> {
153 self.up().and_then(|up| up.left())
154 }
155
156 /// Get the `GridIndex` above and to the right, if it exists.
157 ///
158 /// ## Example
159 /// ```rust
160 /// # use simple_grid::{Grid, GridIndex};
161 /// let column_5_row_4 = GridIndex::new(5, 4);
162 /// assert_eq!(column_5_row_4.up_right(), Some(GridIndex::new(6, 3)));
163 /// let column_5_row_0 = GridIndex::new(5, 0);
164 /// assert_eq!(column_5_row_0.up_right(), None);
165 /// ```
166 pub fn up_right(&self) -> Option<Self> {
167 if self.row() > 0 {
168 Some(Self::new(self.column() + 1, self.row() - 1))
169 } else {
170 None
171 }
172 }
173
174 /// Get the `GridIndex` below and to the right, if it exists.
175 ///
176 /// ## Example
177 /// ```rust
178 /// # use simple_grid::{Grid, GridIndex};
179 /// let column_5_row_4 = GridIndex::new(5, 4);
180 /// assert_eq!(column_5_row_4.down_right(), Some(GridIndex::new(6, 5)));
181 /// ```
182 pub fn down_right(&self) -> Option<Self> {
183 if let (Some(right), Some(down)) = (self.column().checked_add(1), self.row().checked_add(1))
184 {
185 Some(Self::new(right, down))
186 } else {
187 None
188 }
189 }
190
191 /// Get the `GridIndex` below and to the left, if it exists.
192 ///
193 /// ## Example
194 /// ```rust
195 /// # use simple_grid::{Grid, GridIndex};
196 /// let column_5_row_4 = GridIndex::new(5, 4);
197 /// assert_eq!(column_5_row_4.down_left(), Some(GridIndex::new(4, 5)));
198 /// let column_0_row_0 = GridIndex::new(0, 0);
199 /// assert_eq!(column_0_row_0.down_left(), None);
200 /// ```
201 pub fn down_left(&self) -> Option<Self> {
202 if self.column() > 0 {
203 Some(Self::new(self.column() - 1, self.row() + 1))
204 } else {
205 None
206 }
207 }
208
209 /// Convert this GridIndex into a linear index in a Grid of the given width.
210 ///
211 /// ## Panics
212 /// * If `self.column() >= width`
213 pub(crate) fn to_linear_idx_in(self, width: usize) -> usize {
214 if self.column() >= width {
215 panic!(
216 "can't convert {:?} to a linear index in a Grid of width {}",
217 self, width
218 );
219 }
220 self.row() * width + self.column()
221 }
222}
223
224impl From<(usize, usize)> for GridIndex {
225 fn from((column, row): (usize, usize)) -> Self {
226 GridIndex::new(column, row)
227 }
228}
229
230impl std::fmt::Display for GridIndex {
231 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232 write!(f, "({},{})", self.column(), self.row())
233 }
234}
235
236#[derive(Debug, Copy, Clone)]
237pub(crate) enum LinearIndexError {
238 RowTooHigh,
239 ColumnTooHigh,
240}
241
242impl std::fmt::Display for LinearIndexError {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 let output = match self {
245 LinearIndexError::RowTooHigh => "row index is too high",
246 LinearIndexError::ColumnTooHigh => "column index is too high",
247 };
248
249 write!(f, "{}", output)
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn to_linear_idx_in_test() {
259 let index = GridIndex::new(2, 3);
260 let linear = index.to_linear_idx_in(7);
261 assert_eq!(linear, 23);
262 }
263}