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