# HG changeset patch # User alfadur # Date 1541447038 -10800 # Node ID 7f5a591e1c43ed018a39348623f95a9b27b40d5a # Parent 09f62bb046efb5cf8b2e9cec89b255c00729b14e separate rectangle types based on right/bottom edge inclusivity 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)); } } diff -r 09f62bb046ef -r 7f5a591e1c43 rust/land2d/src/lib.rs --- a/rust/land2d/src/lib.rs Mon Nov 05 21:21:53 2018 +0300 +++ b/rust/land2d/src/lib.rs Mon Nov 05 22:43:58 2018 +0300 @@ -3,12 +3,11 @@ use std::cmp; -use integral_geometry::{ArcPoints, EquidistantPoints, Line, Point, Rect, Size, SizeMask}; +use integral_geometry::{ArcPoints, EquidistantPoints, Line, Point, RectInclusive, Size, SizeMask}; pub struct Land2D { pixels: vec2d::Vec2D, - play_size: Size, - play_box: Rect, + play_box: RectInclusive, mask: SizeMask, } @@ -20,9 +19,8 @@ ((real_size.width - play_size.width) / 2) as i32, (real_size.height - play_size.height) as i32, ); - let play_box = Rect::from_size(top_left, play_size); + let play_box = RectInclusive::from_size(top_left, play_size); Self { - play_size, play_box, pixels: vec2d::Vec2D::new(real_size, fill_value), mask: real_size.to_mask(), @@ -50,21 +48,21 @@ #[inline] pub fn play_width(&self) -> usize { - self.play_size.width + self.play_box.width() } #[inline] pub fn play_height(&self) -> usize { - self.play_size.height + self.play_box.height() } #[inline] pub fn play_size(&self) -> Size { - self.play_size + self.play_box.size() } #[inline] - pub fn play_box(&self) -> Rect { + pub fn play_box(&self) -> RectInclusive { self.play_box } diff -r 09f62bb046ef -r 7f5a591e1c43 rust/landgen/src/outline.rs --- a/rust/landgen/src/outline.rs Mon Nov 05 21:21:53 2018 +0300 +++ b/rust/landgen/src/outline.rs Mon Nov 05 22:43:58 2018 +0300 @@ -1,7 +1,7 @@ use itertools::Itertools; use std::cmp::min; -use integral_geometry::{Line, Ray, Point, Polygon, Rect, Size}; +use integral_geometry::{Line, Ray, Point, Polygon, Rect, RectInclusive, Size}; use land2d::Land2D; use outline_template::OutlineTemplate; @@ -10,14 +10,14 @@ pub islands: Vec, pub fill_points: Vec, pub size: Size, - pub play_box: Rect, - intersections_box: Rect, + pub play_box: RectInclusive, + intersections_box: RectInclusive, } impl OutlinePoints { pub fn from_outline_template>( outline_template: &OutlineTemplate, - play_box: Rect, + play_box: RectInclusive, size: Size, random_numbers: &mut I, ) -> Self { @@ -38,7 +38,7 @@ }) .collect(), fill_points: outline_template.fill_points.clone(), - intersections_box: Rect::at_origin(size) + intersections_box: RectInclusive::at_origin(size) .with_margin(size.to_square().width as i32 * -2), } } @@ -73,7 +73,11 @@ } #[inline] - fn solve_intersection(intersections_box: &Rect, ray: &Ray, edge: &Line) -> Option<(i32, u32)> + fn solve_intersection( + intersections_box: &RectInclusive, + ray: &Ray, + edge: &Line + ) -> Option<(i32, u32)> { let edge_dir = edge.scaled_direction(); let aqpb = ray.direction.cross(edge_dir) as i64; @@ -302,15 +306,16 @@ #[test()] fn points_test() { + let size = Size::square(100); let mut points = OutlinePoints { islands: vec![ Polygon::new(&[Point::new(0, 0), Point::new(20, 0), Point::new(30, 30)]), Polygon::new(&[Point::new(10, 15), Point::new(15, 20), Point::new(20, 15)]), ], fill_points: vec![Point::new(1, 1)], - play_box: Rect::from_box(0, 100, 0, 100).with_margin(10), + play_box: RectInclusive::at_origin(size).with_margin(10), size: Size::square(100), - intersections_box: Rect::from_box(0, 0, 100, 100), + intersections_box: RectInclusive::at_origin(size), }; let segments: Vec = points.segments_iter().collect();