diff --git a/examples/drawing.rs b/examples/drawing.rs index 47a7ea22..c5677357 100644 --- a/examples/drawing.rs +++ b/examples/drawing.rs @@ -44,18 +44,14 @@ fn main() { draw_line_segment_mut(&mut image, (20f32, 180f32), (20f32, 220f32), white); // Draw a hollow rect within bounds - draw_hollow_rect_mut(&mut image, Rect::at(60, 10).of_size(20, 20), white); + draw_hollow_rect_mut(&mut image, Rect{x: 60, y:10, width:20, height:20}, white); // Outside bounds - draw_hollow_rect_mut(&mut image, Rect::at(300, 10).of_size(20, 20), white); - // Partially outside bounds - draw_hollow_rect_mut(&mut image, Rect::at(90, -10).of_size(30, 20), white); + draw_hollow_rect_mut(&mut image, Rect{x: 300, y:10, width:20, height:20}, white); // Draw a filled rect within bounds - draw_filled_rect_mut(&mut image, Rect::at(130, 10).of_size(20, 20), white); + draw_filled_rect_mut(&mut image, Rect{x: 130, y:10, width:20, height:20}, white); // Outside bounds - draw_filled_rect_mut(&mut image, Rect::at(300, 10).of_size(20, 20), white); - // Partially outside bounds - draw_filled_rect_mut(&mut image, Rect::at(180, -10).of_size(30, 20), white); + draw_filled_rect_mut(&mut image, Rect{x: 300, y:10, width:20, height:20}, white); // Draw a hollow circle within bounds draw_hollow_circle_mut(&mut image, (100, 100), 15, white); diff --git a/examples/template_matching.rs b/examples/template_matching.rs index 320743a4..eed230ba 100644 --- a/examples/template_matching.rs +++ b/examples/template_matching.rs @@ -125,8 +125,12 @@ fn run_match_template( .unwrap(); // Show location the template was extracted from - let roi = Rect::at(args.template_x as i32, args.template_y as i32) - .of_size(args.template_w, args.template_h); + let roi = Rect { + x: args.template_x, + y: args.template_y, + width: args.template_w, + height: args.template_h, + }; draw_green_rect(&result_padded, roi) } @@ -174,8 +178,12 @@ fn main() { ); // Show location the template was extracted from - let roi = Rect::at(args.template_x as i32, args.template_y as i32) - .of_size(args.template_w, args.template_h); + let roi = Rect { + x: args.template_x, + y: args.template_y, + width: args.template_w, + height: args.template_h, + }; let image_with_roi = draw_green_rect(&image, roi); diff --git a/src/drawing/rect.rs b/src/drawing/rect.rs index ee2c99ea..d4121dfe 100644 --- a/src/drawing/rect.rs +++ b/src/drawing/rect.rs @@ -1,7 +1,7 @@ use crate::definitions::Image; use crate::drawing::line::draw_line_segment_mut; use crate::drawing::Canvas; -use crate::rect::Rect; +use crate::rect::{Rect, RectExt}; use image::GenericImage; use std::f32; @@ -23,15 +23,15 @@ pub fn draw_hollow_rect_mut(canvas: &mut C, rect: Rect, color: C::Pixel) where C: Canvas, { - let left = rect.left() as f32; - let right = rect.right() as f32; - let top = rect.top() as f32; - let bottom = rect.bottom() as f32; - - draw_line_segment_mut(canvas, (left, top), (right, top), color); - draw_line_segment_mut(canvas, (left, bottom), (right, bottom), color); - draw_line_segment_mut(canvas, (left, top), (left, bottom), color); - draw_line_segment_mut(canvas, (right, top), (right, bottom), color); + let left_x = rect.left_x() as f32; + let right_x = rect.right_x() as f32; + let top_y = rect.top_y() as f32; + let bottom_y = rect.bottom_y() as f32; + + draw_line_segment_mut(canvas, (left_x, top_y), (right_x, top_y), color); + draw_line_segment_mut(canvas, (left_x, bottom_y), (right_x, bottom_y), color); + draw_line_segment_mut(canvas, (left_x, top_y), (left_x, bottom_y), color); + draw_line_segment_mut(canvas, (right_x, top_y), (right_x, bottom_y), color); } /// Draws a rectangle and its contents on an image. @@ -52,12 +52,17 @@ pub fn draw_filled_rect_mut(canvas: &mut C, rect: Rect, color: C::Pixel) where C: Canvas, { - let canvas_bounds = Rect::at(0, 0).of_size(canvas.width(), canvas.height()); - if let Some(intersection) = canvas_bounds.intersect(rect) { - for dy in 0..intersection.height() { - for dx in 0..intersection.width() { - let x = intersection.left() as u32 + dx; - let y = intersection.top() as u32 + dy; + let canvas_bounds = Rect { + x: 0, + y: 0, + width: canvas.width(), + height: canvas.height(), + }; + if let Some(intersection) = canvas_bounds.intersect(&rect) { + for dy in 0..intersection.height { + for dx in 0..intersection.width { + let x = intersection.left_x() + dx; + let y = intersection.top_y() + dy; canvas.draw_pixel(x, y, color); } } @@ -82,7 +87,16 @@ mod tests { 1, 1, 4, 1, 4; 1, 1, 4, 4, 4); - let actual = draw_hollow_rect(&image, Rect::at(2, 2).of_size(3, 3), Luma([4u8])); + let actual = draw_hollow_rect( + &image, + Rect { + x: 2, + y: 2, + width: 3, + height: 3, + }, + Luma([4u8]), + ); assert_pixels_eq!(actual, expected); } @@ -97,7 +111,16 @@ mod tests { 1, 4, 4, 4, 1; 1, 1, 1, 1, 1); - let actual = draw_filled_rect(&image, Rect::at(1, 1).of_size(3, 3), Luma([4u8])); + let actual = draw_filled_rect( + &image, + Rect { + x: 1, + y: 1, + width: 3, + height: 3, + }, + Luma([4u8]), + ); assert_pixels_eq!(actual, expected); } @@ -111,10 +134,24 @@ mod tests { let mut image = Blend(RgbaImage::from_pixel(5, 5, white)); - draw_filled_rect_mut(&mut image, Rect::at(1, 1).of_size(3, 3), blue); draw_filled_rect_mut( &mut image, - Rect::at(2, 2).of_size(1, 1), + Rect { + x: 1, + y: 1, + width: 3, + height: 3, + }, + blue, + ); + draw_filled_rect_mut( + &mut image, + Rect { + x: 2, + y: 2, + width: 1, + height: 1, + }, semi_transparent_red, ); @@ -134,7 +171,16 @@ mod tests { // Draw an opaque rectangle over the central pixel as a sanity check that // we're blending in the correct direction only. - draw_filled_rect_mut(&mut image, Rect::at(2, 2).of_size(1, 1), blue); + draw_filled_rect_mut( + &mut image, + Rect { + x: 2, + y: 2, + width: 1, + height: 1, + }, + blue, + ); assert_eq!(*image.0.get_pixel(2, 2), blue); } } @@ -151,7 +197,12 @@ mod benches { fn bench_draw_filled_rect_mut_rgb(b: &mut Bencher) { let mut image = RgbImage::new(200, 200); let color = Rgb([120u8, 60u8, 47u8]); - let rect = Rect::at(50, 50).of_size(80, 90); + let rect = Rect { + x: 50, + y: 50, + width: 80, + height: 90, + }; b.iter(|| { draw_filled_rect_mut(&mut image, rect, color); black_box(&image); diff --git a/src/edges.rs b/src/edges.rs index 38ce3d9e..1b3975b5 100644 --- a/src/edges.rs +++ b/src/edges.rs @@ -163,9 +163,18 @@ mod benches { fn edge_detect_bench_image(width: u32, height: u32) -> GrayImage { let mut image = GrayImage::new(width, height); - let (w, h) = (width as i32, height as i32); - let large = Rect::at(w / 4, h / 4).of_size(width / 2, height / 2); - let small = Rect::at(9, 9).of_size(3, 3); + let large = Rect { + x: width / 4, + y: height / 4, + width: width / 2, + height: height / 2, + }; + let small = Rect { + x: 9, + y: 9, + width: 3, + height: 3, + }; draw_filled_rect_mut(&mut image, large, Luma([255])); draw_filled_rect_mut(&mut image, small, Luma([255])); diff --git a/src/rect.rs b/src/rect.rs index 5ac47732..90f937f2 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -1,191 +1,101 @@ -//! Basic manipulation of rectangles. +//! Extension trait for `image::Rect` use std::cmp; -/// A rectangular region of non-zero width and height. -/// # Examples -/// ``` -/// use imageproc::rect::Rect; -/// use imageproc::rect::Region; -/// -/// // Construct a rectangle with top-left corner at (4, 5), width 6 and height 7. -/// let rect = Rect::at(4, 5).of_size(6, 7); -/// -/// // Contains top-left point: -/// assert_eq!(rect.left(), 4); -/// assert_eq!(rect.top(), 5); -/// assert!(rect.contains(rect.left(), rect.top())); -/// -/// // Contains bottom-right point, at (left + width - 1, top + height - 1): -/// assert_eq!(rect.right(), 9); -/// assert_eq!(rect.bottom(), 11); -/// assert!(rect.contains(rect.right(), rect.bottom())); -/// ``` -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Rect { - left: i32, - top: i32, - width: u32, - height: u32, -} - -/// A geometrical representation of a set of 2D points with coordinate type T. -pub trait Region { - /// Whether this region contains the given point. - fn contains(&self, x: T, y: T) -> bool; -} - -impl Rect { - /// Reduces possibility of confusing coordinates and dimensions - /// when specifying rects. - /// - /// See the [struct-level documentation](struct.Rect.html) for examples. - pub fn at(x: i32, y: i32) -> RectPosition { - RectPosition { left: x, top: y } - } +pub use image::math::Rect; +/// Extension methods for [`Rect`] used by `imageproc`. +pub trait RectExt { /// Smallest y-coordinate reached by rect. - /// - /// See the [struct-level documentation](struct.Rect.html) for examples. - pub fn top(&self) -> i32 { - self.top - } - + fn top_y(&self) -> u32; /// Smallest x-coordinate reached by rect. - /// - /// See the [struct-level documentation](struct.Rect.html) for examples. - pub fn left(&self) -> i32 { - self.left - } - + fn bottom_y(&self) -> u32; /// Greatest y-coordinate reached by rect. - /// - /// See the [struct-level documentation](struct.Rect.html) for examples. - pub fn bottom(&self) -> i32 { - self.top + (self.height as i32) - 1 - } - + fn left_x(&self) -> u32; /// Greatest x-coordinate reached by rect. - /// - /// See the [struct-level documentation](struct.Rect.html) for examples. - pub fn right(&self) -> i32 { - self.left + (self.width as i32) - 1 - } - - /// Width of rect. - pub fn width(&self) -> u32 { - self.width - } - - /// Height of rect. - pub fn height(&self) -> u32 { - self.height - } - + fn right_x(&self) -> u32; /// Returns the intersection of self and other, or none if they are are disjoint. /// /// # Examples /// ``` - /// use imageproc::rect::Rect; - /// use imageproc::rect::Region; + /// use imageproc::rect::{Rect, RectExt}; /// /// // Intersecting a rectangle with itself - /// let r = Rect::at(4, 5).of_size(6, 7); - /// assert_eq!(r.intersect(r), Some(r)); + /// let r = Rect{x: 4, y: 5, width: 6, height: 7}; + /// assert_eq!(r.intersect(&r), Some(r)); /// /// // Intersecting overlapping but non-equal rectangles - /// let r = Rect::at(0, 0).of_size(5, 5); - /// let s = Rect::at(1, 4).of_size(10, 12); - /// let i = Rect::at(1, 4).of_size(4, 1); - /// assert_eq!(r.intersect(s), Some(i)); + /// let r = Rect{x: 0, y: 0, width:5, height:5}; + /// let s = Rect{x: 1, y: 4, width:10, height: 2}; + /// let i = Rect{x: 1, y: 4, width:4, height:1}; + /// assert_eq!(r.intersect(&s), Some(i)); /// /// // Intersecting disjoint rectangles - /// let r = Rect::at(0, 0).of_size(5, 5); - /// let s = Rect::at(10, 10).of_size(100, 12); - /// assert_eq!(r.intersect(s), None); + /// let r = Rect{x: 0, y: 0, width:5, height:5}; + /// let s = Rect{x: 10, y: 10, width: 100, height: 12}; + /// assert_eq!(r.intersect(&s), None); /// ``` - pub fn intersect(&self, other: Rect) -> Option { - let left = cmp::max(self.left, other.left); - let top = cmp::max(self.top, other.top); - let right = cmp::min(self.right(), other.right()); - let bottom = cmp::min(self.bottom(), other.bottom()); + fn intersect(&self, other: &Self) -> Option + where + Self: std::marker::Sized; + /// Does `self` contain the given coordinates? + fn contains(&self, x: u32, y: u32) -> bool; +} - if right < left || bottom < top { +impl RectExt for Rect { + fn top_y(&self) -> u32 { + self.y + } + fn left_x(&self) -> u32 { + self.x + } + fn bottom_y(&self) -> u32 { + self.y + self.height - 1 + } + fn right_x(&self) -> u32 { + self.x + self.width - 1 + } + + fn intersect(&self, other: &Rect) -> Option { + let left_x = cmp::max(self.x, other.x); + let top_y = cmp::max(self.y, other.y); + let right_x = cmp::min(self.right_x(), other.right_x()); + let bottom_y = cmp::min(self.bottom_y(), other.bottom_y()); + + if right_x < left_x || bottom_y < top_y { return None; } Some(Rect { - left, - top, - width: (right - left) as u32 + 1, - height: (bottom - top) as u32 + 1, + x: left_x, + y: top_y, + width: right_x - left_x + 1, + height: bottom_y - top_y + 1, }) } -} - -impl Region for Rect { - fn contains(&self, x: i32, y: i32) -> bool { - self.left <= x && x <= self.right() && self.top <= y && y <= self.bottom() - } -} - -impl Region for Rect { - fn contains(&self, x: f32, y: f32) -> bool { - self.left as f32 <= x - && x <= self.right() as f32 - && self.top as f32 <= y - && y <= self.bottom() as f32 - } -} - -/// Position of the top left of a rectangle. -/// Only used when building a [`Rect`](struct.Rect.html). -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct RectPosition { - left: i32, - top: i32, -} -impl RectPosition { - /// Construct a rectangle from a position and size. Width and height - /// are required to be strictly positive. - /// - /// See the [`Rect`](struct.Rect.html) documentation for examples. - pub fn of_size(self, width: u32, height: u32) -> Rect { - assert!(width > 0, "width must be strictly positive"); - assert!(height > 0, "height must be strictly positive"); - Rect { - left: self.left, - top: self.top, - width, - height, - } + fn contains(&self, x: u32, y: u32) -> bool { + self.x <= x && x <= self.right_x() && self.y <= y && y <= self.bottom_y() } } #[cfg(test)] mod tests { - use super::{Rect, Region}; + use crate::rect::RectExt; - #[test] - #[should_panic] - fn test_rejects_empty_rectangle() { - Rect::at(1, 2).of_size(0, 1); - } + use super::Rect; #[test] fn test_contains_i32() { - let r = Rect::at(5, 5).of_size(6, 6); + let r = Rect { + x: 5, + y: 5, + width: 6, + height: 6, + }; assert!(r.contains(5, 5)); assert!(r.contains(10, 10)); assert!(!r.contains(10, 11)); assert!(!r.contains(11, 10)); } - - #[test] - fn test_contains_f32() { - let r = Rect::at(5, 5).of_size(6, 6); - assert!(r.contains(5f32, 5f32)); - assert!(!r.contains(10.1f32, 10f32)); - } }