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 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
|
//! Rectangles on the 2D character grid.
use crate::direction::{Absolute, Orientation};
use crate::Vec2;
use std::ops::Add;
/// A non-empty rectangle on the 2D grid.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rect {
/// Top-left corner, inclusive
top_left: Vec2,
/// Bottom-right corner, inclusive
bottom_right: Vec2,
}
impl<T> Add<T> for Rect
where
T: Into<Vec2>,
{
type Output = Rect;
fn add(mut self, rhs: T) -> Self {
self.offset(rhs);
self
}
}
impl Rect {
/// Creates a new `Rect` around a single point.
///
/// The size will be `(1, 1)`.
#[must_use]
pub fn from_point<T>(point: T) -> Self
where
T: Into<Vec2>,
{
Self::from_size(point, (1, 1))
}
/// Creates a new `Rect` with the given position and size.
///
/// The minimum size will `(1, 1)`.
#[must_use]
pub fn from_size<U, V>(top_left: U, size: V) -> Self
where
U: Into<Vec2>,
V: Into<Vec2>,
{
let size = size.into();
let top_left = top_left.into();
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.
#[must_use]
pub fn from_corners<U, V>(a: U, b: V) -> Self
where
U: Into<Vec2>,
V: Into<Vec2>,
{
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<R>(&mut self, other: R)
where
R: Into<Rect>,
{
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`.
#[must_use]
pub fn expanded_to<R>(mut self, other: R) -> Self
where
R: Into<Rect>,
{
self.expand_to(other);
self
}
/// Returns the start and end coordinate of one side of this rectangle.
///
/// Both start and end are inclusive.
pub fn side(self, orientation: Orientation) -> (usize, usize) {
match orientation {
Orientation::Vertical => (self.top(), self.bottom()),
Orientation::Horizontal => (self.left(), self.right()),
}
}
/// Returns the coordinate of the given edge.
///
/// All edges are inclusive.
pub fn edge(self, side: Absolute) -> usize {
match side {
Absolute::Left => self.left(),
Absolute::Right => self.right(),
Absolute::Up => self.top(),
Absolute::Down => self.bottom(),
// TODO: Remove `None` from `Absolute` enum
Absolute::None => panic!("None is not a valid edge."),
}
}
/// Adds the given offset to this rectangle.
pub fn offset<V>(&mut self, offset: V)
where
V: Into<Vec2>,
{
let offset = offset.into();
self.top_left = self.top_left + offset;
self.bottom_right = self.bottom_right + offset;
}
/// 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.
///
/// This is inclusive.
pub fn top_left(self) -> Vec2 {
self.top_left
}
/// Returns the bottom-right corner.
///
/// This is inclusive.
pub fn bottom_right(self) -> Vec2 {
self.bottom_right
}
/// Returns the top-right corner.
///
/// This is inclusive.
pub fn top_right(self) -> Vec2 {
Vec2::new(self.right(), self.top())
}
/// Returns the bottom-left corner.
///
/// This is inclusive.
pub fn bottom_left(self) -> Vec2 {
Vec2::new(self.left(), self.bottom())
}
/// Returns the Y value of the top edge of the rectangle.
///
/// This is inclusive.
pub fn top(self) -> usize {
self.top_left.y
}
/// Returns the X value of the left edge of the rectangle.
///
/// This is inclusive.
pub fn left(self) -> usize {
self.top_left.x
}
/// Returns the X value of the right edge of the rectangle.
///
/// This is inclusive.
pub fn right(self) -> usize {
self.bottom_right.x
}
/// Returns the Y value of the bottom edge of the rectangle.
///
/// This is inclusive.
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()
}
/// Checks if a point is in `self`.
pub fn contains(self, point: Vec2) -> bool {
point.fits(self.top_left) && point.fits_in(self.bottom_right)
}
}
|