geometry/
side.rs

1//! The sides of an axis-aligned rectangle.
2
3use crate::dir::Dir;
4use crate::sign::Sign;
5use array_map::{ArrayMap, Indexable};
6use serde::{Deserialize, Serialize};
7
8/// An enumeration of the sides of an axis-aligned rectangle.
9#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash, PartialEq, Eq)]
10#[repr(u8)]
11#[derive(Indexable)]
12pub enum Side {
13    /// The left side.
14    Left,
15    /// The bottom side.
16    Bot,
17    /// The right side.
18    Right,
19    /// The top side.
20    Top,
21}
22
23impl Side {
24    /// Gets the direction of the coordinate corresponding to this side.
25    ///
26    /// Top and bottom edges are y-coordinates, so they are on the **vertical** axis.
27    /// Left and right edges are x-coordinates, so they are on the **horizontal** axis.
28    ///
29    /// Also see [`Side::edge_dir`].
30    pub fn coord_dir(&self) -> Dir {
31        use Dir::*;
32        use Side::*;
33        match self {
34            Top | Bot => Vert,
35            Left | Right => Horiz,
36        }
37    }
38
39    /// Gets the direction of the edge corresponding to this side.
40    ///
41    /// Top and bottom edges are **horizontal** line segments;
42    /// left and right edges are **vertical** line segments.
43    ///
44    /// Also see [`Side::coord_dir`].
45    pub fn edge_dir(&self) -> Dir {
46        use Dir::*;
47        use Side::*;
48        match self {
49            Top | Bot => Horiz,
50            Left | Right => Vert,
51        }
52    }
53
54    /// Returns the opposite direction.
55    pub fn other(&self) -> Self {
56        match self {
57            Side::Top => Side::Bot,
58            Side::Right => Side::Left,
59            Side::Bot => Side::Top,
60            Side::Left => Side::Right,
61        }
62    }
63
64    /// Returns the sign corresponding to moving towards this side.
65    pub fn sign(&self) -> Sign {
66        use Side::*;
67        use Sign::*;
68        match self {
69            Top | Right => Pos,
70            Bot | Left => Neg,
71        }
72    }
73
74    /// Returns the side corresponding with the given [`Dir`] and [`Sign`].
75    ///
76    /// # Example
77    ///
78    /// ```
79    /// # use geometry::prelude::*;
80    /// assert_eq!(Side::with_dir_and_sign(Dir::Horiz, Sign::Neg), Side::Left);
81    /// assert_eq!(Side::with_dir_and_sign(Dir::Vert, Sign::Neg), Side::Bot);
82    /// assert_eq!(Side::with_dir_and_sign(Dir::Horiz, Sign::Pos), Side::Right);
83    /// assert_eq!(Side::with_dir_and_sign(Dir::Vert, Sign::Pos), Side::Top);
84    /// ```
85    pub fn with_dir_and_sign(dir: Dir, sign: Sign) -> Side {
86        match dir {
87            Dir::Horiz => match sign {
88                Sign::Pos => Side::Right,
89                Sign::Neg => Side::Left,
90            },
91            Dir::Vert => match sign {
92                Sign::Pos => Side::Top,
93                Sign::Neg => Side::Bot,
94            },
95        }
96    }
97
98    /// Returns sides that bound the given direction.
99    ///
100    /// Users should not rely upon the order of the sides returned.
101    ///
102    /// # Example
103    ///
104    /// ```
105    /// # use geometry::prelude::*;
106    /// assert_eq!(Side::with_dir(Dir::Horiz), [Side::Left, Side::Right]);
107    /// assert_eq!(Side::with_dir(Dir::Vert), [Side::Bot, Side::Top]);
108    /// ```
109    pub fn with_dir(dir: Dir) -> [Side; 2] {
110        match dir {
111            Dir::Horiz => [Side::Left, Side::Right],
112            Dir::Vert => [Side::Bot, Side::Top],
113        }
114    }
115}
116
117impl std::ops::Not for Side {
118    type Output = Self;
119    /// Exclamation Operator returns the opposite direction
120    fn not(self) -> Self::Output {
121        self.other()
122    }
123}
124/// An association of a value with type `T` to each of the four [`Side`]s.
125#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
126pub struct Sides<T> {
127    inner: ArrayMap<Side, T, 4>,
128}
129
130impl<T> Sides<T>
131where
132    T: Clone,
133{
134    /// Creates a new [`Sides`] with `value` associated with all sides.
135    ///
136    /// The value will be cloned for each [`Side`].
137    ///
138    /// If your value is [`Copy`], consider using [`Sides::uniform`] instead.
139    pub fn uniform_cloned(value: T) -> Self {
140        Self {
141            inner: ArrayMap::from_value(value),
142        }
143    }
144}
145
146impl<T> Sides<T>
147where
148    T: Copy,
149{
150    /// Creates a new [`Sides`] with `value` associated with all sides.
151    pub const fn uniform(value: T) -> Self {
152        Self {
153            inner: ArrayMap::new([value; 4]),
154        }
155    }
156}
157
158impl<T> Sides<T> {
159    /// Creates a new [`Sides`] with with the provided values for each side.
160    pub const fn new(left: T, bot: T, right: T, top: T) -> Self {
161        // IMPORTANT: the ordering of array elements here must match
162        // the ordering of variants in the [`Side`] enum.
163        Self {
164            inner: ArrayMap::new([left, bot, right, top]),
165        }
166    }
167
168    /// Maps a function over the provided [`Sides`], returning a new [`Sides`].
169    pub fn map<B>(self, f: impl FnMut(&Side, T) -> B) -> Sides<B> {
170        Sides {
171            inner: self.inner.map(f),
172        }
173    }
174}
175
176impl<T> std::ops::Index<Side> for Sides<T> {
177    type Output = T;
178    fn index(&self, index: Side) -> &Self::Output {
179        self.inner.index(index)
180    }
181}
182
183impl<T> std::ops::IndexMut<Side> for Sides<T> {
184    fn index_mut(&mut self, index: Side) -> &mut Self::Output {
185        self.inner.index_mut(index)
186    }
187}