//! The names of the directions and the sides of the logical plane. //! //! An [`Axis`] is one of the two directions. A [`Side`] is one of the four //! sides of a rectangle. Each side is perpendicular to one axis. use crate::space::{Rect, center_x, center_y}; /// One of the two directions of the logical plane. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Axis { /// The direction from the left to the right. Horizontal, /// The direction from the top to the bottom. Vertical, } impl Axis { /// Give the two axes. #[must_use] pub const fn all() -> [Self; 2] { [Self::Horizontal, Self::Vertical] } /// Give the other axis. #[must_use] pub const fn transverse(self) -> Self { match self { Self::Horizontal => Self::Vertical, Self::Vertical => Self::Horizontal, } } /// Give the two sides that are perpendicular to the axis. /// /// The first side is the side with the smaller coordinate. #[must_use] pub const fn sides(self) -> [Side; 2] { match self { Self::Horizontal => [Side::Left, Side::Right], Self::Vertical => [Side::Top, Side::Bottom], } } /// Give the center of the rectangle on the axis. #[must_use] pub fn center_of(self, rect: Rect) -> i32 { match self { Self::Horizontal => center_x(rect), Self::Vertical => center_y(rect), } } } /// One of the four sides of a rectangle. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Side { /// The side with the smallest horizontal coordinate. Left, /// The side with the largest horizontal coordinate. Right, /// The side with the smallest vertical coordinate. Top, /// The side with the largest vertical coordinate. Bottom, } impl Side { /// Give the four sides. #[must_use] pub const fn all() -> [Self; 4] { [Self::Left, Self::Right, Self::Top, Self::Bottom] } /// Give the axis that is perpendicular to the side. #[must_use] pub const fn axis(self) -> Axis { match self { Self::Left | Self::Right => Axis::Horizontal, Self::Top | Self::Bottom => Axis::Vertical, } } /// Give the side that is opposite to this side. #[must_use] pub const fn opposite(self) -> Self { match self { Self::Left => Self::Right, Self::Right => Self::Left, Self::Top => Self::Bottom, Self::Bottom => Self::Top, } } /// Tell if the side has the smaller coordinate on its axis. #[must_use] pub const fn is_leading(self) -> bool { matches!(self, Self::Left | Self::Top) } /// Give the coordinate of the side of the rectangle. #[must_use] pub fn coordinate_of(self, rect: Rect) -> i32 { match self { Self::Left => rect.min.x, Self::Right => rect.max.x, Self::Top => rect.min.y, Self::Bottom => rect.max.y, } } } #[cfg(test)] mod tests { use super::*; use crate::space::{Point, Size, rect_at}; fn rect(x: i32, y: i32, w: i32, h: i32) -> Rect { rect_at(Point::new(x, y), Size::new(w, h)) } #[test] fn the_opposite_of_the_opposite_is_the_same_side() { for side in Side::all() { assert_eq!(side.opposite().opposite(), side); } } #[test] fn a_side_and_its_opposite_share_an_axis() { for side in Side::all() { assert_eq!(side.axis(), side.opposite().axis()); } } #[test] fn the_transverse_of_the_transverse_is_the_same_axis() { for axis in Axis::all() { assert_eq!(axis.transverse().transverse(), axis); } } #[test] fn the_sides_of_an_axis_have_that_axis() { for axis in Axis::all() { for side in axis.sides() { assert_eq!(side.axis(), axis); } } } #[test] fn exactly_one_side_of_an_axis_is_leading() { for axis in Axis::all() { let leading = axis.sides().iter().filter(|s| s.is_leading()).count(); assert_eq!(leading, 1); } } #[test] fn the_coordinates_of_a_rectangle_match_its_corners() { let r = rect(10, 20, 100, 50); assert_eq!(Side::Left.coordinate_of(r), 10); assert_eq!(Side::Right.coordinate_of(r), 110); assert_eq!(Side::Top.coordinate_of(r), 20); assert_eq!(Side::Bottom.coordinate_of(r), 70); } }