use std::ops::{Add, Sub};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Pos {
pub col: u16,
pub row: u16,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Dim {
pub width: u16,
pub height: u16,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct BBox {
pos: Pos,
dim: Dim,
}
impl Add for Pos {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let col = self.col + rhs.col;
let row = self.row + rhs.row;
Pos::new(col, row)
}
}
impl Sub for Pos {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
let col = self.col - rhs.col;
let row = self.row - rhs.row;
Pos::new(col, row)
}
}
impl Pos {
pub fn new(col: u16, row: u16) -> Self {
Self { col, row }
}
}
impl Dim {
pub fn new(width: u16, height: u16) -> Self {
Self { width, height }
}
pub fn is_empty(self) -> bool {
self.width == 0 || self.height == 0
}
}
impl BBox {
pub fn new(col: u16, row: u16, width: u16, height: u16) -> Self {
let pos = Pos::new(col, row);
let dim = Dim::new(width, height);
Self { pos, dim }
}
pub fn left(self) -> u16 {
self.pos.col
}
pub fn width(self) -> u16 {
self.dim.width
}
pub fn right(self) -> u16 {
self.left() + self.width()
}
pub fn top(self) -> u16 {
self.pos.row
}
pub fn height(self) -> u16 {
self.dim.height
}
pub fn bottom(self) -> u16 {
self.top() + self.height()
}
pub fn dim(self) -> Dim {
self.dim
}
pub fn contains(self, pos: Pos) -> bool {
pos.col >= self.left()
&& pos.col < self.right()
&& pos.row >= self.top()
&& pos.row < self.bottom()
}
pub fn within(self, pos: Pos) -> Option<Pos> {
if self.contains(pos) {
Some(pos - self.pos)
} else {
None
}
}
pub fn clip(self, rhs: Self) -> Self {
let col = self.left().max(rhs.left());
let row = self.top().max(rhs.top());
let right = self.right().min(rhs.right());
let bottom = self.bottom().min(rhs.bottom());
let width = if right > col { right - col } else { 0 };
let height = if bottom > row { bottom - row } else { 0 };
BBox::new(col, row, width, height)
}
pub fn trim_left(mut self, trim: u16) -> Self {
let trim = self.width().min(trim);
self.pos.col += trim;
self.dim.width -= trim;
self
}
pub fn trim_right(mut self, trim: u16) -> Self {
let trim = self.width().min(trim);
self.dim.width -= trim;
self
}
pub fn trim_top(mut self, trim: u16) -> Self {
let trim = self.height().min(trim);
self.pos.row += trim;
self.dim.height -= trim;
self
}
pub fn trim_bottom(mut self, trim: u16) -> Self {
let trim = self.height().min(trim);
self.dim.height -= trim;
self
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn bbox_trim() {
let bbox = BBox::new(0, 0, 5, 7);
assert_eq!(bbox.trim_left(1), BBox::new(1, 0, 4, 7));
assert_eq!(bbox.trim_right(1), BBox::new(0, 0, 4, 7));
assert_eq!(bbox.trim_top(1), BBox::new(0, 1, 5, 6));
assert_eq!(bbox.trim_bottom(1), BBox::new(0, 0, 5, 6));
}
}