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 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
use crate::{Point, Size, Vector};
/// A rectangle.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Rectangle<T = f32> {
/// X coordinate of the top-left corner.
pub x: T,
/// Y coordinate of the top-left corner.
pub y: T,
/// Width of the rectangle.
pub width: T,
/// Height of the rectangle.
pub height: T,
}
impl Rectangle<f32> {
/// Creates a new [`Rectangle`] with its top-left corner in the given
/// [`Point`] and with the provided [`Size`].
pub fn new(top_left: Point, size: Size) -> Self {
Self {
x: top_left.x,
y: top_left.y,
width: size.width,
height: size.height,
}
}
/// Creates a new [`Rectangle`] with its top-left corner at the origin
/// and with the provided [`Size`].
pub fn with_size(size: Size) -> Self {
Self {
x: 0.0,
y: 0.0,
width: size.width,
height: size.height,
}
}
/// Returns the [`Point`] at the center of the [`Rectangle`].
pub fn center(&self) -> Point {
Point::new(self.center_x(), self.center_y())
}
/// Returns the X coordinate of the [`Point`] at the center of the
/// [`Rectangle`].
pub fn center_x(&self) -> f32 {
self.x + self.width / 2.0
}
/// Returns the Y coordinate of the [`Point`] at the center of the
/// [`Rectangle`].
pub fn center_y(&self) -> f32 {
self.y + self.height / 2.0
}
/// Returns the position of the top left corner of the [`Rectangle`].
pub fn position(&self) -> Point {
Point::new(self.x, self.y)
}
/// Returns the [`Size`] of the [`Rectangle`].
pub fn size(&self) -> Size {
Size::new(self.width, self.height)
}
/// Returns the area of the [`Rectangle`].
pub fn area(&self) -> f32 {
self.width * self.height
}
/// Returns true if the given [`Point`] is contained in the [`Rectangle`].
pub fn contains(&self, point: Point) -> bool {
self.x <= point.x
&& point.x < self.x + self.width
&& self.y <= point.y
&& point.y < self.y + self.height
}
/// Returns true if the given [`Point`] is contained in the [`Rectangle`].
/// The [`Point`] must be strictly contained, i.e. it must not be on the
/// border.
pub fn contains_strict(&self, point: Point) -> bool {
self.x < point.x
&& point.x < self.x + self.width
&& self.y < point.y
&& point.y < self.y + self.height
}
/// Returns true if the current [`Rectangle`] is completely within the given
/// `container`.
pub fn is_within(&self, container: &Rectangle) -> bool {
container.contains(self.position())
&& container.contains(
self.position() + Vector::new(self.width, self.height),
)
}
/// Returns true if the current [`Rectangle`] is completely within the given
/// `container`. The [`Rectangle`] must be strictly contained, i.e. it must
/// not be on the border.
pub fn is_within_strict(&self, container: &Rectangle) -> bool {
container.contains_strict(self.position())
&& container.contains_strict(
self.position() + Vector::new(self.width, self.height),
)
}
/// Computes the intersection with the given [`Rectangle`].
pub fn intersection(
&self,
other: &Rectangle<f32>,
) -> Option<Rectangle<f32>> {
let x = self.x.max(other.x);
let y = self.y.max(other.y);
let lower_right_x = (self.x + self.width).min(other.x + other.width);
let lower_right_y = (self.y + self.height).min(other.y + other.height);
let width = lower_right_x - x;
let height = lower_right_y - y;
if width > 0.0 && height > 0.0 {
Some(Rectangle {
x,
y,
width,
height,
})
} else {
None
}
}
/// Returns whether the [`Rectangle`] intersects with the given one.
pub fn intersects(&self, other: &Self) -> bool {
self.intersection(other).is_some()
}
/// Computes the union with the given [`Rectangle`].
pub fn union(&self, other: &Self) -> Self {
let x = self.x.min(other.x);
let y = self.y.min(other.y);
let lower_right_x = (self.x + self.width).max(other.x + other.width);
let lower_right_y = (self.y + self.height).max(other.y + other.height);
let width = lower_right_x - x;
let height = lower_right_y - y;
Rectangle {
x,
y,
width,
height,
}
}
/// Snaps the [`Rectangle`] to __unsigned__ integer coordinates.
pub fn snap(self) -> Rectangle<u32> {
Rectangle {
x: self.x as u32,
y: self.y as u32,
width: self.width as u32,
height: self.height as u32,
}
}
/// Expands the [`Rectangle`] a given amount.
pub fn expand(self, amount: f32) -> Self {
Self {
x: self.x - amount,
y: self.y - amount,
width: self.width + amount * 2.0,
height: self.height + amount * 2.0,
}
}
}
impl std::ops::Mul<f32> for Rectangle<f32> {
type Output = Self;
fn mul(self, scale: f32) -> Self {
Self {
x: self.x * scale,
y: self.y * scale,
width: self.width * scale,
height: self.height * scale,
}
}
}
impl From<Rectangle<u32>> for Rectangle<f32> {
fn from(rectangle: Rectangle<u32>) -> Rectangle<f32> {
Rectangle {
x: rectangle.x as f32,
y: rectangle.y as f32,
width: rectangle.width as f32,
height: rectangle.height as f32,
}
}
}
impl<T> std::ops::Add<Vector<T>> for Rectangle<T>
where
T: std::ops::Add<Output = T>,
{
type Output = Rectangle<T>;
fn add(self, translation: Vector<T>) -> Self {
Rectangle {
x: self.x + translation.x,
y: self.y + translation.y,
..self
}
}
}
impl<T> std::ops::Sub<Vector<T>> for Rectangle<T>
where
T: std::ops::Sub<Output = T>,
{
type Output = Rectangle<T>;
fn sub(self, translation: Vector<T>) -> Self {
Rectangle {
x: self.x - translation.x,
y: self.y - translation.y,
..self
}
}
}