geometry/
dir.rs

1//! Axis-aligned directions: horizontal or vertical.
2
3use std::fmt::Display;
4
5use array_map::{ArrayMap, Indexable};
6use serde::{Deserialize, Serialize};
7
8/// An enumeration of axis-aligned directions.
9#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash, PartialEq, Eq)]
10#[repr(u8)]
11#[derive(Indexable)]
12pub enum Dir {
13    /// The horizontal, or x-aligned, direction.
14    Horiz,
15    /// The vertical, or y-aligned, direction.
16    Vert,
17}
18
19impl Dir {
20    /// Returns the other direction.
21    ///
22    /// # Example
23    ///
24    /// ```
25    /// # use geometry::prelude::*;
26    /// assert_eq!(Dir::Vert.other(), Dir::Horiz);
27    /// assert_eq!(Dir::Horiz.other(), Dir::Vert);
28    /// ```
29    pub const fn other(&self) -> Self {
30        match *self {
31            Self::Horiz => Self::Vert,
32            Self::Vert => Self::Horiz,
33        }
34    }
35}
36
37impl Display for Dir {
38    /// Displays the direction in a human-readable format.
39    ///
40    /// Currently, [`Dir::Horiz`] becomes `horizontal`;
41    /// [`Dir::Vert`] becomes `vertical`.
42    /// However, users should not rely on these particular strings.
43    ///
44    /// # Example
45    ///
46    /// ```
47    /// # use geometry::prelude::*;
48    /// assert_eq!(format!("{}", Dir::Horiz), "horizontal");
49    /// assert_eq!(format!("{}", Dir::Vert), "vertical");
50    /// ```
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        match *self {
53            Self::Horiz => write!(f, "horizontal"),
54            Self::Vert => write!(f, "vertical"),
55        }
56    }
57}
58
59impl std::ops::Not for Dir {
60    type Output = Self;
61    /// Returns the other direction.
62    ///
63    /// # Example
64    ///
65    /// ```
66    /// # use geometry::prelude::*;
67    /// assert_eq!(!Dir::Vert, Dir::Horiz);
68    /// assert_eq!(!Dir::Horiz, Dir::Vert);
69    /// ```
70    fn not(self) -> Self::Output {
71        self.other()
72    }
73}
74
75/// An association of a value with type `T` to each of the two [`Dir`]s.
76#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
77pub struct Dirs<T> {
78    inner: ArrayMap<Dir, T, 2>,
79}
80
81impl<T> Dirs<T>
82where
83    T: Clone,
84{
85    /// Creates a new [`Dirs`] with `value` associated with all directions.
86    ///
87    /// The value will be cloned for each [`Dir`].
88    ///
89    /// If your value is [`Copy`], consider using [`Dirs::uniform`] instead.
90    pub fn uniform_cloned(value: T) -> Self {
91        Self {
92            inner: ArrayMap::from_value(value),
93        }
94    }
95}
96
97impl<T> Dirs<T>
98where
99    T: Copy,
100{
101    /// Creates a new [`Dir`] with `value` associated with all directions.
102    pub const fn uniform(value: T) -> Self {
103        Self {
104            inner: ArrayMap::new([value; 2]),
105        }
106    }
107}
108
109impl<T> Dirs<T> {
110    /// Creates a new [`Dir`] with with the provided values for each direction.
111    pub const fn new(horiz: T, vert: T) -> Self {
112        // IMPORTANT: the ordering of array elements here must match
113        // the ordering of variants in the [`Dir`] enum.
114        Self {
115            inner: ArrayMap::new([horiz, vert]),
116        }
117    }
118
119    /// Maps a function over the provided [`Dir`], returning a new [`Dir`].
120    pub fn map<B>(self, f: impl FnMut(&Dir, T) -> B) -> Dirs<B> {
121        Dirs {
122            inner: self.inner.map(f),
123        }
124    }
125}
126
127impl<T> std::ops::Index<Dir> for Dirs<T> {
128    type Output = T;
129    fn index(&self, index: Dir) -> &Self::Output {
130        self.inner.index(index)
131    }
132}
133
134impl<T> std::ops::IndexMut<Dir> for Dirs<T> {
135    fn index_mut(&mut self, index: Dir) -> &mut Self::Output {
136        self.inner.index_mut(index)
137    }
138}