use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Rect {
pub x: u16,
pub y: u16,
pub w: u16,
pub h: u16,
}
impl Rect {
#[must_use]
pub const fn new(x: u16, y: u16, w: u16, h: u16) -> Self {
Self { x, y, w, h }
}
#[must_use]
pub const fn sized(w: u16, h: u16) -> Self {
Self { x: 0, y: 0, w, h }
}
#[must_use]
pub const fn right(self) -> u32 {
self.x as u32 + self.w as u32
}
#[must_use]
pub const fn bottom(self) -> u32 {
self.y as u32 + self.h as u32
}
#[must_use]
pub const fn area(self) -> u32 {
self.w as u32 * self.h as u32
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.w == 0 || self.h == 0
}
#[must_use]
pub fn contains(self, px: u16, py: u16) -> bool {
let (px, py) = (u32::from(px), u32::from(py));
u32::from(self.x) <= px && px < self.right() && u32::from(self.y) <= py && py < self.bottom()
}
#[must_use]
pub(crate) fn span_overlap(a0: u32, a1: u32, b0: u32, b1: u32) -> u32 {
let lo = a0.max(b0);
let hi = a1.min(b1);
hi.saturating_sub(lo)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn right_and_bottom_are_exclusive_edges() {
let r = Rect::new(2, 3, 10, 5);
assert_eq!(r.right(), 12);
assert_eq!(r.bottom(), 8);
assert!(r.contains(2, 3)); assert!(!r.contains(12, 3)); assert!(!r.contains(2, 8)); assert!(r.contains(11, 7)); }
#[test]
fn empty_when_either_dim_zero() {
assert!(Rect::sized(0, 9).is_empty());
assert!(Rect::sized(9, 0).is_empty());
assert!(!Rect::sized(1, 1).is_empty());
assert_eq!(Rect::new(0, 0, 4, 5).area(), 20);
}
#[test]
fn span_overlap_is_intersection_length() {
assert_eq!(Rect::span_overlap(0, 10, 5, 15), 5);
assert_eq!(Rect::span_overlap(0, 5, 5, 10), 0); assert_eq!(Rect::span_overlap(0, 5, 10, 15), 0); assert_eq!(Rect::span_overlap(2, 8, 0, 20), 6); }
#[test]
fn high_coordinate_right_edge_does_not_wrap() {
let r = Rect::new(u16::MAX - 3, 0, 3, 1);
assert_eq!(r.right(), u32::from(u16::MAX));
}
}