chickenwire/coordinate/
axial.rs

1//! Axial Coordinates
2
3use std::ops::{Add, Div, Mul, Sub};
4
5use super::*;
6
7//////////////////////////////////////////////////////////////////////////////
8// Primary Structure
9//////////////////////////////////////////////////////////////////////////////
10
11/// Axial coordinates use the same system as cube coordinates, but only store
12/// two of the coordinate values. This is possible since, for cube coordinate
13/// (x, y, z), the third value can always be calculated when the other two are
14/// known due to the constraint x + y + z == 0.
15#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
16pub struct Axial {
17    pub q: i32,
18    pub r: i32,
19}
20
21//////////////////////////////////////////////////////////////////////////////
22// Traits: Arithmetic
23//////////////////////////////////////////////////////////////////////////////
24
25/// Adds two `Axial` coordinates in the same manner as vectors.
26///
27/// # Examples
28///
29/// ```
30/// use chickenwire::coordinate::axial::Axial;
31///
32/// let coord_1 = Axial::from_coords(1, -3);
33/// let coord_2 = Axial::from_coords(-5, 12);
34///
35/// assert_eq!(coord_1 + coord_2, Axial::from_coords(-4, 9));
36/// assert_eq!(coord_2 + coord_1, Axial::from_coords(-4, 9));
37/// ```
38impl Add for Axial {
39    type Output = Self;
40
41    fn add(self, other: Self) -> Self {
42        Self {
43            q: self.q + other.q,
44            r: self.r + other.r,
45        }
46    }
47}
48
49/// Subtracts two `Axial` coordinates in the same manner as vectors.
50///
51/// # Examples
52///
53/// ```
54/// use chickenwire::coordinate::axial::Axial;
55///
56/// let coord_1 = Axial::from_coords(1, -3);
57/// let coord_2 = Axial::from_coords(5, -12);
58///
59/// assert_eq!(coord_1 - coord_2, Axial::from_coords(-4, 9));
60/// assert_eq!(coord_2 - coord_1, Axial::from_coords(4, -9));
61/// ```
62impl Sub for Axial {
63    type Output = Self;
64
65    fn sub(self, other: Self) -> Self {
66        Self {
67            q: self.q - other.q,
68            r: self.r - other.r,
69        }
70    }
71}
72
73/// Multiplies an `Axial` coordinate by an `i32` scalar, like a vector.
74///
75/// # Examples
76///
77/// ```
78/// use chickenwire::coordinate::axial::Axial;
79///
80/// let coord = Axial::from_coords(1, -3);
81///
82/// assert_eq!(-1 * coord, Axial::from_coords(-1, 3));
83/// assert_eq!(0 * coord, Axial::ORIGIN);
84/// assert_eq!(coord * 2, Axial::from_coords(2, -6));
85/// ```
86impl Mul<i32> for Axial {
87    type Output = Self;
88
89    fn mul(self, n: i32) -> Self {
90        Self {
91            q: self.q * n,
92            r: self.r * n,
93        }
94    }
95}
96
97impl Mul<Axial> for i32 {
98    type Output = Axial;
99
100    fn mul(self, coord: Axial) -> Axial {
101        coord * self
102    }
103}
104
105/// Divides an `Axial` coordinate by an `i32` scalar, like a vector. Values
106/// are truncated (i.e. rounded toward zero).
107///
108/// # Panics
109///
110/// Panics when trying to divide by zero.
111///
112/// # Examples
113///
114/// ```
115/// use chickenwire::coordinate::axial::Axial;
116///
117/// let coord = Axial::from_coords(12, -36);
118///
119/// assert_eq!(coord / -1, Axial::from_coords(-12, 36));
120/// assert_eq!(coord / 2, Axial::from_coords(6, -18));
121/// assert_eq!(coord / 3, Axial::from_coords(4, -12));
122/// ```
123impl Div<i32> for Axial {
124    type Output = Self;
125
126    fn div(self, n: i32) -> Self {
127        Self {
128            q: self.q / n,
129            r: self.r / n,
130        }
131    }
132}
133
134//////////////////////////////////////////////////////////////////////////////
135// Traits: From & Into
136//////////////////////////////////////////////////////////////////////////////
137
138/// Creates an `Axial` from an `(i32, i32)`.
139///
140/// # Examples
141///
142/// ```
143/// use chickenwire::coordinate::axial::Axial;
144///
145/// assert_eq!(
146///     Axial::from((1, 2)),
147///     Axial { q: 1, r: 2 }
148/// );
149/// ```
150impl From<(i32, i32)> for Axial {
151    fn from((q, r): (i32, i32)) -> Self {
152        Self {
153            q: q,
154            r: r,
155        }
156    }
157}
158
159/// Creates an `Axial` from a `Cube`.
160///
161/// # Examples
162///
163/// ```
164/// use chickenwire::coordinate::axial::Axial;
165/// use chickenwire::coordinate::cube::Cube;
166///
167/// let cube = Cube::force_from_coords(1, 2, -3);
168///
169/// assert_eq!(
170///     Axial::from(cube),
171///     Axial { q: 1, r: -3 }
172/// );
173/// assert_eq!(
174///     Axial::from(Cube::ORIGIN),
175///     Axial::ORIGIN
176/// );
177/// ```
178impl From<Cube> for Axial {
179    fn from(coord: Cube) -> Self {
180        let (x, _, z) = coord.to_tuple();
181
182        Self { q: x, r: z }
183    }
184}
185
186/// Creates an `Axial` from a `MultiCoord`.
187///
188/// Conversion from both axial and cube `MultiCoord`s is supported.
189///
190/// # Panics
191///
192/// Panics when parsing a double or offset `MultiCoord`.
193impl From<MultiCoord> for Axial {
194    fn from(coord: MultiCoord) -> Self {
195        match coord.sys {
196            CoordSys::Axial => Axial { q: coord.a, r: coord.b },
197            CoordSys::Cube => Axial { q: coord.a, r: coord.c.unwrap() },
198            _ => panic!("{:?} is not an Axial or Cube coordinate", coord),
199        }
200    }
201}
202
203//////////////////////////////////////////////////////////////////////////////
204// Methods
205//////////////////////////////////////////////////////////////////////////////
206
207impl Axial {
208    //////////////////////////////////
209    // Constants
210    //////////////////////////////////
211
212    /// `Axial` coordinate origin of (0, 0).
213    pub const ORIGIN: Axial = Axial { q: 0, r: 0 };
214
215    //////////////////////////////////
216    // Initialization
217    //////////////////////////////////
218
219    /// For two unsigned 32-bit integers x and y, the corresponding axial
220    /// coordinate is (x, y).
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// use chickenwire::coordinate::axial::Axial;
226    ///
227    /// assert_eq!(Axial::from((1, 2)), Axial::from_coords(1, 2));
228    /// ```
229    pub fn from_coords(q: i32, r: i32) -> Self {
230        Self::from((q, r))
231    }
232
233    //////////////////////////////////
234    // Neighbors
235    //////////////////////////////////
236
237    /// [ docs missing ]
238    pub fn neighbors(self) -> Vec<Self> {
239        let cube_neighbors = Cube::from(self).neighbors();
240
241        cube_neighbors.into_iter().map(|coord| Self::from(coord)).collect()
242    }
243
244    /// [ docs missing ]
245    pub fn diagonals(self) -> Vec<Self> {
246        let cube_diagonals = Cube::from(self).diagonals();
247
248        cube_diagonals.into_iter().map(|coord| Self::from(coord)).collect()
249    }
250
251    //////////////////////////////////
252    // Distances
253    //////////////////////////////////
254
255    /// Determines the distance between two axial coordinates.
256    ///
257    /// # Examples
258    ///
259    /// ```
260    /// use chickenwire::coordinate::axial::Axial;
261    ///
262    /// let origin = Axial::ORIGIN;
263    /// let coord_1 = Axial { q: 1, r: -3 };
264    /// let coord_2 = Axial { q: -8, r: 2 };
265    ///
266    /// assert_eq!(origin.dist(coord_1), 3);
267    /// assert_eq!(coord_1.dist(origin), 3);
268    /// assert_eq!(coord_1.dist(coord_1), 0);
269    /// assert_eq!(coord_2.dist(coord_1), 9);
270    /// ```
271    pub fn dist(self, other: Self) -> i32 {
272        Cube::from(self).dist(Cube::from(other))
273    }
274}