rust/integral-geometry/src/lib.rs
changeset 14135 7f5a591e1c43
parent 14134 09f62bb046ef
child 14137 3119d665d3c6
--- 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<i32> {
+        self.x..self.x + self.width as i32
     }
 
     #[inline]
-    pub fn x_range(&self) -> RangeInclusive<i32> {
-        self.x..=self.x + self.width as i32
-    }
-
-    #[inline]
-    pub fn y_range(&self) -> RangeInclusive<i32> {
-        self.y..=self.y + self.height as i32
+    pub fn y_range(&self) -> Range<i32> {
+        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<i32> {
+        self.left()..=self.right()
+    }
+
+    #[inline]
+    pub fn y_range(&self) -> RangeInclusive<i32> {
+        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<RectInclusive> for Rect {
+    fn from(r: RectInclusive) -> Self {
+        Self::from_size(r.top_left, r.size())
+    }
+}
+
+impl From<Rect> for RectInclusive {
+    fn from(r: Rect) -> Self {
+        Self::new(r.top_left(), r.bottom_right())
+    }
+}
+
 trait RangeContains<T> {
     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));
     }
 }