diff -r 09f62bb046ef -r 7f5a591e1c43 rust/integral-geometry/src/lib.rs --- a/rust/integral-geometry/src/lib.rs Mon Nov 05 21:21:53 2018 +0300 +++ b/rust/integral-geometry/src/lib.rs Mon Nov 05 22:43:58 2018 +0300 @@ -73,7 +73,7 @@ } #[inline] - pub fn clamp(self, rect: &Rect) -> Point { + pub fn clamp(self, rect: &RectInclusive) -> Point { Point::new( rect.x_range().clamp(self.x), rect.y_range().clamp(self.y) @@ -260,28 +260,16 @@ #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct Rect { - pub x: i32, - pub y: i32, - pub width: u32, - pub height: u32, + x: i32, + y: i32, + width: u32, + height: u32 } impl Rect { #[inline] pub fn new(x: i32, y: i32, width: u32, height: u32) -> Self { - Self { - x, - y, - width, - height, - } - } - - pub fn from_box(left: i32, right: i32, top: i32, bottom: i32) -> Self { - assert!(left <= right); - assert!(top <= bottom); - - Rect::new(left, top, (right - left) as u32, (bottom - top) as u32) + Self { x, y, width, height } } pub fn from_size(top_left: Point, size: Size) -> Self { @@ -298,8 +286,18 @@ } #[inline] + pub fn width(&self) -> usize { + self.width as usize + } + + #[inline] + pub fn height(&self) -> usize { + self.height as usize + } + + #[inline] pub fn size(&self) -> Size { - Size::new(self.width as usize, self.height as usize) + Size::new(self.width(), self.height()) } #[inline] @@ -319,12 +317,12 @@ #[inline] pub fn right(&self) -> i32 { - self.x + self.width as i32 + self.x + self.width as i32 - 1 } #[inline] pub fn bottom(&self) -> i32 { - self.y + self.height as i32 + self.y + self.height as i32 - 1 } #[inline] @@ -343,23 +341,13 @@ } #[inline] - pub fn with_margin(&self, margin: i32) -> Self { - Rect::from_box( - self.left() + margin, - self.right() - margin, - self.top() + margin, - self.bottom() - margin, - ) + pub fn x_range(&self) -> Range { + self.x..self.x + self.width as i32 } #[inline] - pub fn x_range(&self) -> RangeInclusive { - self.x..=self.x + self.width as i32 - } - - #[inline] - pub fn y_range(&self) -> RangeInclusive { - self.y..=self.y + self.height as i32 + pub fn y_range(&self) -> Range { + self.y..self.y + self.height as i32 } #[inline] @@ -384,14 +372,8 @@ } #[inline] - pub fn split_at(&self, point: Point) -> [Rect; 4] { - assert!(self.contains_inside(point)); - [ - Rect::from_box(self.left(), point.x, self.top(), point.y), - Rect::from_box(point.x, self.right(), self.top(), point.y), - Rect::from_box(point.x, self.right(), point.y, self.bottom()), - Rect::from_box(self.left(), point.x, point.y, self.bottom()), - ] + pub fn split_at(&self, point: Point) -> [RectInclusive; 4] { + RectInclusive::from(self.clone()).split_at(point) } #[inline] @@ -404,6 +386,153 @@ } } +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct RectInclusive { + top_left: Point, + bottom_right: Point, +} + +impl RectInclusive { + #[inline] + pub fn new(top_left: Point, bottom_right: Point) -> Self { + assert!(top_left.x <= bottom_right.x); + assert!(top_left.y <= bottom_right.y); + Self { top_left, bottom_right } + } + + pub fn from_box(left: i32, right: i32, top: i32, bottom: i32) -> Self { + RectInclusive::new(Point::new(left, top), Point::new(right, bottom)) + } + pub fn from_size(top_left: Point, size: Size) -> Self { + RectInclusive::new( + top_left, + top_left + Point::new(size.width as i32 - 1, size.height as i32 - 1) + ) + } + + pub fn at_origin(size: Size) -> Self { + RectInclusive::from_size(Point::zero(), size) + } + + #[inline] + pub fn width(&self) -> usize { + (self.right() - self.left() + 1) as usize + } + + #[inline] + pub fn height(&self) -> usize { + (self.right() - self.left() + 1) as usize + } + + #[inline] + pub fn size(&self) -> Size { + Size::new(self.width(), self.height()) + } + + #[inline] + pub fn area(&self) -> usize { + self.size().area() + } + + #[inline] + pub fn left(&self) -> i32 { + self.top_left.x + } + + #[inline] + pub fn top(&self) -> i32 { + self.top_left.y + } + + #[inline] + pub fn right(&self) -> i32 { + self.bottom_right.x + } + + #[inline] + pub fn bottom(&self) -> i32 { + self.bottom_right.y + } + + #[inline] + pub fn top_left(&self) -> Point { + self.top_left + } + + #[inline] + pub fn bottom_right(&self) -> Point { + self.bottom_right + } + + #[inline] + pub fn center(&self) -> Point { + (self.top_left() + self.bottom_right()) / 2 + } + + #[inline] + pub fn with_margin(&self, margin: i32) -> Self { + let offset = Point::diag(margin); + RectInclusive::new( + self.top_left() + offset, + self.bottom_right() - offset + ) + } + + #[inline] + pub fn x_range(&self) -> RangeInclusive { + self.left()..=self.right() + } + + #[inline] + pub fn y_range(&self) -> RangeInclusive { + self.top()..=self.bottom() + } + + #[inline] + pub fn contains(&self, point: Point) -> bool { + self.x_range().contains(point.x) && self.y_range().contains(point.y) + } + + #[inline] + pub fn contains_inside(&self, point: Point) -> bool { + point.x > self.left() + && point.x < self.right() + && point.y > self.top() + && point.y < self.bottom() + } + + #[inline] + pub fn intersects(&self, other: &RectInclusive) -> bool { + self.left() <= self.right() + && self.right() >= other.left() + && self.top() <= other.bottom() + && self.bottom() >= other.top() + } + + #[inline] + pub fn split_at(&self, point: Point) -> [RectInclusive; 4] { + assert!(self.contains_inside(point)); + [ + RectInclusive::from_box(self.left(), point.x, self.top(), point.y), + RectInclusive::from_box(point.x, self.right(), self.top(), point.y), + RectInclusive::from_box(point.x, self.right(), point.y, self.bottom()), + RectInclusive::from_box(self.left(), point.x, point.y, self.bottom()), + ] + } +} + +impl From for Rect { + fn from(r: RectInclusive) -> Self { + Self::from_size(r.top_left, r.size()) + } +} + +impl From for RectInclusive { + fn from(r: Rect) -> Self { + Self::new(r.top_left(), r.bottom_right()) + } +} + trait RangeContains { fn contains(&self, value: T) -> bool; } @@ -799,20 +928,20 @@ #[test] fn rect() { - let r = Rect::from_box(10, 100, 0, 70); + let r = RectInclusive::from_box(10, 100, 0, 70); assert!(r.contains_inside(Point::new(99, 69))); assert!(!r.contains_inside(Point::new(100, 70))); assert_eq!(r.top_left(), Point::new(10, 0)); - assert_eq!(r.with_margin(12), Rect::from_box(22, 88, 12, 58)); + assert_eq!(r.with_margin(12), RectInclusive::from_box(22, 88, 12, 58)); } #[test] fn fit() { - let r = Rect::from_box(10, 100, 0, 70); + let r = RectInclusive::from_box(10, 100, 0, 70); - assert_eq!(Point::new(0, -10).fit(&r), Point::new(10, 0)); - assert_eq!(Point::new(1000, 1000).fit(&r), Point::new(100, 70)); + assert_eq!(Point::new(0, -10).clamp(&r), Point::new(10, 0)); + assert_eq!(Point::new(1000, 1000).clamp(&r), Point::new(100, 70)); } }