#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Point {
pub x: u16,
pub y: u16,
}
impl Point {
pub fn zero() -> Point {
Point { x: 0, y: 0 }
}
pub fn offset(self, offset_x: u16, offset_y: u16) -> Point {
Point {
x: self.x + offset_x,
y: self.y + offset_y,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rect {
pub point: Point,
pub size: Size,
}
impl Rect {
pub fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
Self {
point: Point { x, y },
size: Size { width, height },
}
}
#[inline]
pub fn left(&self) -> u16 {
self.point.x
}
#[inline]
pub fn top(&self) -> u16 {
self.point.y
}
#[inline]
pub fn right(&self) -> u16 {
self.left() + self.size.width
}
#[inline]
pub fn bottom(&self) -> u16 {
self.top() + self.size.height
}
pub fn inset_by(
self,
inset_left: u16,
inset_right: u16,
inset_top: u16,
inset_bottom: u16,
) -> Rect {
Rect {
point: Point {
x: self.point.x.saturating_add(inset_left),
y: self.point.y.saturating_add(inset_top),
},
size: self
.size
.inset_by(inset_left, inset_right, inset_top, inset_bottom),
}
}
pub fn outset_by(
self,
outset_left: u16,
outset_right: u16,
outset_top: u16,
outset_bottom: u16,
) -> Rect {
Rect {
point: Point {
x: self.point.x.saturating_sub(outset_left),
y: self.point.y.saturating_sub(outset_top),
},
size: self
.size
.outset_by(outset_left, outset_right, outset_top, outset_bottom),
}
}
pub(crate) fn offset(self, offset_x: u16, offset_y: u16) -> Rect {
Rect {
point: self.point.offset(offset_x, offset_y),
size: self.size,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Size {
pub width: u16,
pub height: u16,
}
impl Size {
pub fn new(width: u16, height: u16) -> Self {
Self { width, height }
}
pub fn zero() -> Size {
Size {
width: 0,
height: 0,
}
}
pub fn max() -> Size {
Size {
width: u16::MAX,
height: u16::MAX,
}
}
pub const MAX: Size = Size {
width: u16::MAX,
height: u16::MAX,
};
pub fn inset_by(self, left: u16, right: u16, top: u16, bottom: u16) -> Size {
Size {
width: self.width.saturating_sub(left + right),
height: self.height.saturating_sub(top + bottom),
}
}
pub fn outset_by(self, left: u16, right: u16, top: u16, bottom: u16) -> Size {
Size {
width: self.width + left + right,
height: self.height + top + bottom,
}
}
pub fn min(&self, proposed: Size) -> Size {
Size {
width: self.width.min(proposed.width),
height: self.height.min(proposed.height),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rect_inset_by() {
let rect = Rect::new(10, 10, 30, 20);
let inset_rect = rect.inset_by(5, 5, 5, 5);
assert_eq!(inset_rect.point.x, 15);
assert_eq!(inset_rect.point.y, 15);
assert_eq!(inset_rect.size.width, 20);
assert_eq!(inset_rect.size.height, 10);
}
#[test]
fn test_rect_outset_by() {
let rect = Rect::new(10, 10, 30, 20);
let outset_rect = rect.outset_by(5, 5, 5, 5);
assert_eq!(outset_rect.point.x, 5);
assert_eq!(outset_rect.point.y, 5);
assert_eq!(outset_rect.size.width, 40);
assert_eq!(outset_rect.size.height, 30);
}
use proptest::prelude::*;
proptest! {
#[test]
fn test_inset_outset_identity(width in 50..100u16, height in 50..100u16, left in 0..25u16, right in 0..25u16, top in 0..25u16, bottom in 0..25u16) {
let size = Size { width, height };
let inset_size = size.inset_by(left, right, top, bottom);
let outset_size = inset_size.outset_by(left, right, top, bottom);
assert_eq!(size, outset_size);
}
#[test]
fn test_outset_inset_identity(width in 50..100u16, height in 50..100u16, left in 0..25u16, right in 0..25u16, top in 0..25u16, bottom in 0..25u16) {
let size = Size { width, height };
let outset_size = size.outset_by(left, right, top, bottom);
let inset_size = outset_size.inset_by(left, right, top, bottom);
assert_eq!(size, inset_size);
}
}
}