1use std::fmt;
2use std::fmt::Display;
3use std::num::TryFromIntError;
4
5use crate::utilities::{index_x, index_y, saturating_index_1d};
6use crate::{Direction, DirectionsSet, DirectionsSetIter, Size2D, Tessellation, TileShape};
7
8#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
68pub struct Point2D {
69 pub x: usize,
70 pub y: usize,
71}
72
73impl Point2D {
74 pub fn new(x: usize, y: usize) -> Self { Self { x, y } }
75
76 pub fn new_from_idx(idx: usize, size: &Size2D) -> Self {
78 Self {
79 x: index_x(idx, size.width),
80 y: index_y(idx, size.width),
81 }
82 }
83
84 pub fn origin() -> Self { Self { x: 0, y: 0 } }
86
87 pub fn is_on_board(&self, size: Size2D) -> bool { self.x < size.width && self.y < size.height }
89
90 pub fn idx(&self, size: Size2D) -> usize {
94 saturating_index_1d(self.x, self.y, size.width)
97 }
98
99 pub fn neighbor(
101 &self, tessellation: Tessellation, to: Direction, size: Size2D,
102 ) -> Option<Self> {
103 tessellation
104 .neighbor(self.x, self.y, to, size.width, size.height)
105 .map(|(new_x, new_y)| Self { x: new_x, y: new_y })
106 }
107
108 pub fn neighbors(&self, tessellation: Tessellation, size: Size2D) -> Option<IterNeighbors> {
114 IterNeighbors::new(tessellation, *self, size)
115 }
116
117 pub fn neighbor_directions(
119 &self, tessellation: Tessellation, size: Size2D,
120 ) -> Option<DirectionsSet> {
121 tessellation.neighbor_directions(self.x, self.y, size.width, size.height)
122 }
123
124 pub fn tile_shape(&self, tessellation: Tessellation) -> TileShape {
126 tessellation.tile_shape_from_pos(self.x, self.y)
127 }
128}
129
130impl Display for Point2D {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 write!(f, "({}, {})", self.x, self.y)
133 }
134}
135
136macro_rules! impl_point2d_from_2D {
141 (usize) => {
142 impl From<(usize, usize)> for Point2D {
144 fn from(value: (usize, usize)) -> Self {
145 Self {
146 x: value.0,
147 y: value.1,
148 }
149 }
150 }
151
152 impl From<[usize; 2]> for Point2D {
154 fn from(value: [usize; 2]) -> Self { Self::from((value[0], value[1])) }
155 }
156 };
157
158 (u8) => {
159 impl_point2d_from_2D!(@smaller u8);
160 };
161 (u16) => {
162 impl_point2d_from_2D!(@smaller u16);
163 };
164
165 (@smaller $t:ty) => {
166 impl From<($t, $t)> for Point2D {
168 fn from(value: ($t, $t)) -> Self {
169 Self {
170 x: value.0.into(),
171 y: value.1.into(),
172 }
173 }
174 }
175
176 impl From<[$t; 2]> for Point2D {
178 fn from(value: [$t; 2]) -> Self { Self::from((value[0], value[1])) }
179 }
180 };
181
182 (u32) => {
183 impl_point2d_from_2D!(@others u32);
184 };
185 (u64) => {
186 impl_point2d_from_2D!(@others u64);
187 };
188 (isize) => {
189 impl_point2d_from_2D!(@others isize);
190 };
191 (i64) => {
192 impl_point2d_from_2D!(@others i64);
193 };
194 (i32) => {
195 impl_point2d_from_2D!(@others i32);
196 };
197 (i16) => {
198 impl_point2d_from_2D!(@others i16);
199 };
200 (i8) => {
201 impl_point2d_from_2D!(@others i8);
202 };
203
204
205 (@others $t:ty) => {
206 impl TryFrom<($t, $t)> for Point2D {
207 type Error = TryFromIntError;
208
209 fn try_from(value: ($t, $t)) -> Result<Self, Self::Error> {
211 Ok(Point2D {
212 x: value.0.try_into()?,
213 y: value.1.try_into()?,
214 })
215 }
216 }
217
218 impl TryFrom<[$t; 2]> for Point2D {
219 type Error = TryFromIntError;
220
221 fn try_from(value: [$t; 2]) -> Result<Self, Self::Error> {
223 Self::try_from((value[0], value[1]))
224 }
225 }
226 };
227}
228
229impl_point2d_from_2D!(usize);
230impl_point2d_from_2D!(u64);
231impl_point2d_from_2D!(u32);
232impl_point2d_from_2D!(u16);
233impl_point2d_from_2D!(u8);
234impl_point2d_from_2D!(isize);
235impl_point2d_from_2D!(i64);
236impl_point2d_from_2D!(i32);
237impl_point2d_from_2D!(i16);
238impl_point2d_from_2D!(i8);
239
240#[derive(Debug, Clone, Copy, PartialEq)]
246pub struct IterNeighbors {
247 tessellation: Tessellation,
248 src: Point2D,
249 size: Size2D,
250 only_neighbor_directions: DirectionsSetIter,
251}
252
253impl IterNeighbors {
254 pub(self) fn new(tessellation: Tessellation, at: Point2D, size: Size2D) -> Option<Self> {
255 tessellation
256 .neighbor_directions(at.x, at.y, size.width, size.height)
257 .map(|ndd| {
258 Self {
259 tessellation,
260 src: at,
261 size,
262 only_neighbor_directions: ndd.iter(),
263 }
264 })
265 }
266}
267
268impl Iterator for IterNeighbors {
269 type Item = (Direction, Point2D);
270
271 fn next(&mut self) -> Option<(Direction, Point2D)> {
272 self.only_neighbor_directions.next().map(|d| {
273 self.src
274 .neighbor(self.tessellation, d, self.size)
275 .map(|p| (d, p))
276 })?
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 const DATA: [(usize, usize, usize); 45] = [
285 (0, 0, 0),
286 (1, 0, 1),
287 (2, 0, 2),
288 (3, 0, 3),
289 (4, 0, 4),
290 (0, 1, 5),
291 (1, 1, 6),
292 (2, 1, 7),
293 (3, 1, 8),
294 (4, 1, 9),
295 (0, 2, 10),
296 (1, 2, 11),
297 (2, 2, 12),
298 (3, 2, 13),
299 (4, 2, 14),
300 (0, 3, 15),
301 (1, 3, 16),
302 (2, 3, 17),
303 (3, 3, 18),
304 (4, 3, 19),
305 (0, 4, 20),
306 (1, 4, 21),
307 (2, 4, 22),
308 (3, 4, 23),
309 (4, 4, 24),
310 (0, 5, 25),
311 (1, 5, 26),
312 (2, 5, 27),
313 (3, 5, 28),
314 (4, 5, 29),
315 (0, 6, 30),
316 (1, 6, 31),
317 (2, 6, 32),
318 (3, 6, 33),
319 (4, 6, 34),
320 (0, 7, 35),
321 (1, 7, 36),
322 (2, 7, 37),
323 (3, 7, 38),
324 (4, 7, 39),
325 (0, 8, 40),
326 (1, 8, 41),
327 (2, 8, 42),
328 (3, 8, 43),
329 (4, 8, 44),
330 ];
331
332 #[test]
333 fn it_can_be_constructed_from_2d_coordinates() {
334 let size: Size2D = (5_usize, 9_usize).into();
335
336 for (x, y, idx) in DATA {
337 let pos = Point2D::new(x, y);
338 assert_eq!(pos.x, x);
339 assert_eq!(pos.y, y);
340 assert_eq!(pos.idx(size), idx);
341 }
342 }
343
344 #[test]
345 fn it_can_be_constructed_from_1d_index() {
346 let size: Size2D = (5_usize, 9_usize).into();
347
348 for (x, y, idx) in DATA {
349 let pos = Point2D::new_from_idx(idx, &size);
350 assert_eq!(pos.x, x);
351 assert_eq!(pos.y, y);
352 assert_eq!(pos.idx(size), idx);
353 }
354 }
355}