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}