diff --git a/src/lib.rs b/src/lib.rs index 442c2fc..76c9816 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,7 @@ pub mod view; pub mod views; pub mod vec; +pub mod rect; pub mod theme; pub mod align; pub mod menu; diff --git a/src/rect.rs b/src/rect.rs new file mode 100644 index 0000000..1e3e650 --- /dev/null +++ b/src/rect.rs @@ -0,0 +1,141 @@ +//! Rectangles on the 2D character grid. + +use vec::Vec2; + +/// A non-empty rectangle on the 2D grid. +/// +/// +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rect { + top_left: Vec2, + bottom_right: Vec2, +} + +impl From for Rect +where + T: Into, +{ + fn from(other: T) -> Self { + // From a point, we can create a 1-by-1 rectangle. + Self::from_size(other, (1, 1)) + } +} + +impl Rect { + /// Creates a new `Rect` with the given position and size. + /// + /// `size` must be non-zero in each axis. + pub fn from_size(top_left: U, size: V) -> Self + where + U: Into, + V: Into, + { + let size = size.into(); + let top_left = top_left.into(); + assert!(size > Vec2::zero()); + + let bottom_right = top_left + size.saturating_sub((1, 1)); + + Self::from_corners(top_left, bottom_right) + } + + /// Creates a new `Rect` from two corners. + /// + /// It can be any two opposite corners. + pub fn from_corners(a: U, b: V) -> Self + where + U: Into, + V: Into, + { + let a = a.into(); + let b = b.into(); + + let top_left = Vec2::min(a, b); + let bottom_right = Vec2::max(a, b); + + Rect { + top_left, + bottom_right, + } + } + + /// Grow this rectangle if necessary to include `other`. + pub fn expand_to(&mut self, other: R) + where + R: Into, + { + let other = other.into(); + + self.top_left = self.top_left.or_min(other.top_left); + self.bottom_right = self.bottom_right.or_max(other.bottom_right); + } + + /// Returns a new rectangle that includes both `self` and `other`. + pub fn expanded_to(mut self, other: R) -> Self + where + R: Into, + { + self.expand_to(other); + self + } + + /// Returns the size of the rectangle. + pub fn size(self) -> Vec2 { + self.bottom_right - self.top_left + (1, 1) + } + + /// Returns the width of the rectangle. + pub fn width(self) -> usize { + self.size().x + } + + /// Returns the height of the rectangle. + pub fn height(self) -> usize { + self.size().y + } + + /// Returns the top-left corner. + pub fn top_left(self) -> Vec2 { + self.top_left + } + + /// Returns the bottom-right corner. + pub fn bottom_right(self) -> Vec2 { + self.bottom_right + } + + /// Returns the top-right corner. + pub fn top_right(self) -> Vec2 { + Vec2::new(self.right(), self.top()) + } + + /// Returns the bottom-left corner. + pub fn bottom_left(self) -> Vec2 { + Vec2::new(self.left(), self.bottom()) + } + + /// Returns the Y value of the top edge of the rectangle. + pub fn top(self) -> usize { + self.top_left.y + } + + /// Returns the X value of the left edge of the rectangle. + pub fn left(self) -> usize { + self.top_left.x + } + + /// Returns the X value of the right edge of the rectangle. + pub fn right(self) -> usize { + self.bottom_right.x + } + + /// Returns the Y value of the botton edge of the rectangle. + pub fn bottom(self) -> usize { + self.bottom_right.y + } + + /// Returns the surface (number of cells) covered by the rectangle. + pub fn surface(self) -> usize { + self.width() * self.height() + } +}