npc_engine_utils/
coord2d.rs

1/*
2 *  SPDX-License-Identifier: Apache-2.0 OR MIT
3 *  © 2020-2022 ETH Zurich and other contributors, see AUTHORS.txt for details
4 */
5
6use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
7
8use rand::Rng;
9
10/// A 2-D integer coordinate type.
11#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
12pub struct Coord2D {
13    pub x: i32,
14    pub y: i32,
15}
16impl Coord2D {
17    pub const fn new(x: i32, y: i32) -> Self {
18        Coord2D { x, y }
19    }
20    /// Returns the absolute value, component by component.
21    pub const fn abs(&self) -> Self {
22        Self::new(self.x.abs(), self.y.abs())
23    }
24    /// Returns the absolute value of the difference to other, component by component.
25    pub fn abs_diff(&self, other: &Coord2D) -> Self {
26        (*self - *other).abs()
27    }
28    /// Returns the value of the smallest dimension of the absolute value of the difference to other.
29    pub fn shortest_dim_dist(&self, other: &Coord2D) -> i32 {
30        let diff = self.abs_diff(other);
31        diff.x.min(diff.y)
32    }
33    /// Returns the value of the largest dimension of the absolute value of the difference to other.
34    pub fn largest_dim_dist(&self, other: &Coord2D) -> i32 {
35        let diff = self.abs_diff(other);
36        diff.x.max(diff.y)
37    }
38    /// Returns the maximum value of self and other, component by component.
39    pub fn max_per_comp(&self, other: Coord2D) -> Self {
40        Self::new(self.x.max(other.x), self.y.max(other.y))
41    }
42    /// Returns the minimum value of self and other, component by component.
43    pub fn min_per_comp(&self, other: Coord2D) -> Self {
44        Self::new(self.x.min(other.x), self.y.min(other.y))
45    }
46    /// Generates a random value between 0 and range.
47    pub fn rand_uniform(range: Coord2D) -> Self {
48        let mut rng = rand::thread_rng();
49        let x = rng.gen_range(0..range.x);
50        let y = rng.gen_range(0..range.y);
51        Self::new(x, y)
52    }
53    /// Manhattan distance between self and other.
54    pub fn manhattan_dist(&self, other: Coord2D) -> i32 {
55        let diff = self.abs_diff(&other);
56        diff.x + diff.y
57    }
58}
59
60impl std::fmt::Display for Coord2D {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        write!(f, "[{}, {}]", self.x, self.y)
63    }
64}
65
66impl Ord for Coord2D {
67    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
68        self.y.cmp(&other.y).then(self.x.cmp(&other.x))
69    }
70}
71impl PartialOrd for Coord2D {
72    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
73        Some(self.cmp(other))
74    }
75}
76
77impl Add for Coord2D {
78    type Output = Coord2D;
79
80    fn add(self, rhs: Self) -> Self::Output {
81        Coord2D::new(self.x + rhs.x, self.y + rhs.y)
82    }
83}
84
85impl Sub for Coord2D {
86    type Output = Coord2D;
87
88    fn sub(self, rhs: Self) -> Self::Output {
89        Coord2D::new(self.x - rhs.x, self.y - rhs.y)
90    }
91}
92
93impl AddAssign for Coord2D {
94    fn add_assign(&mut self, rhs: Self) {
95        self.x += rhs.x;
96        self.y += rhs.y;
97    }
98}
99
100impl SubAssign for Coord2D {
101    fn sub_assign(&mut self, rhs: Self) {
102        self.x -= rhs.x;
103        self.y -= rhs.y;
104    }
105}
106
107impl MulAssign<i32> for Coord2D {
108    fn mul_assign(&mut self, rhs: i32) {
109        self.x *= rhs;
110        self.y *= rhs;
111    }
112}
113
114impl DivAssign<i32> for Coord2D {
115    fn div_assign(&mut self, rhs: i32) {
116        self.x /= rhs;
117        self.y /= rhs;
118    }
119}
120
121impl Mul<i32> for Coord2D {
122    type Output = Coord2D;
123
124    fn mul(self, rhs: i32) -> Self::Output {
125        Coord2D::new(self.x * rhs, self.y * rhs)
126    }
127}
128
129impl Div<i32> for Coord2D {
130    type Output = Coord2D;
131
132    fn div(self, rhs: i32) -> Self::Output {
133        Coord2D::new(self.x / rhs, self.y / rhs)
134    }
135}
136
137impl Neg for Coord2D {
138    type Output = Coord2D;
139
140    fn neg(self) -> Self::Output {
141        Coord2D::new(-self.x, -self.y)
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use crate::*;
148
149    #[test]
150    fn cmp() {
151        assert_eq!(Coord2D::new(0, 0), Coord2D::new(0, 0));
152        assert!(Coord2D::new(0, 0) < Coord2D::new(0, 1));
153        assert!(Coord2D::new(0, 0) < Coord2D::new(1, 0));
154        assert!(Coord2D::new(1, 0) < Coord2D::new(0, 1));
155    }
156
157    #[test]
158    fn abs_diff() {
159        assert_eq!(
160            Coord2D::new(3, 2).abs_diff(&Coord2D::new(0, 3)),
161            Coord2D::new(3, 1)
162        );
163    }
164
165    #[test]
166    fn dim_dist() {
167        assert_eq!(Coord2D::new(3, 2).shortest_dim_dist(&Coord2D::new(0, 1)), 1);
168        assert_eq!(Coord2D::new(3, 2).largest_dim_dist(&Coord2D::new(0, 1)), 3);
169    }
170
171    #[test]
172    fn manhattan_dist() {
173        assert_eq!(Coord2D::new(0, 0).manhattan_dist(Coord2D::new(0, 2)), 2);
174        assert_eq!(Coord2D::new(0, 0).manhattan_dist(Coord2D::new(2, 0)), 2);
175        assert_eq!(Coord2D::new(0, 0).manhattan_dist(Coord2D::new(0, -2)), 2);
176        assert_eq!(Coord2D::new(0, 0).manhattan_dist(Coord2D::new(-2, 0)), 2);
177        assert_eq!(Coord2D::new(3, 2).manhattan_dist(Coord2D::new(0, 1)), 4);
178    }
179}