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}