| 1 | //! The rectangle primitive. |
| 2 | |
| 3 | mod points; |
| 4 | |
| 5 | use crate::{ |
| 6 | geometry::{AnchorPoint, AnchorX, AnchorY, Dimensions, Point, Size}, |
| 7 | primitives::PointsIter, |
| 8 | }; |
| 9 | use az::SaturatingAs; |
| 10 | use core::{ |
| 11 | cmp::min, |
| 12 | ops::{Range, RangeInclusive}, |
| 13 | }; |
| 14 | pub use points::Points; |
| 15 | |
| 16 | /// Rectangle primitive |
| 17 | /// |
| 18 | /// # Examples |
| 19 | /// |
| 20 | /// ## Create some rectangles with different styles |
| 21 | /// |
| 22 | /// ```rust |
| 23 | /// use embedded_graphics::{ |
| 24 | /// pixelcolor::Rgb565, prelude::*, primitives::{Rectangle, PrimitiveStyleBuilder}, |
| 25 | /// }; |
| 26 | /// # use embedded_graphics::mock_display::MockDisplay; |
| 27 | /// # let mut display = MockDisplay::default(); |
| 28 | /// |
| 29 | /// // Rectangle with red 3 pixel wide stroke and green fill with the top left corner at (30, 20) and |
| 30 | /// // a size of (10, 15) |
| 31 | /// let style = PrimitiveStyleBuilder::new() |
| 32 | /// .stroke_color(Rgb565::RED) |
| 33 | /// .stroke_width(3) |
| 34 | /// .fill_color(Rgb565::GREEN) |
| 35 | /// .build(); |
| 36 | /// |
| 37 | /// Rectangle::new(Point::new(30, 20), Size::new(10, 15)) |
| 38 | /// .into_styled(style) |
| 39 | /// .draw(&mut display)?; |
| 40 | /// |
| 41 | /// // Rectangle with translation applied |
| 42 | /// Rectangle::new(Point::new(30, 20), Size::new(10, 15)) |
| 43 | /// .translate(Point::new(-20, -10)) |
| 44 | /// .into_styled(style) |
| 45 | /// .draw(&mut display)?; |
| 46 | /// # Ok::<(), core::convert::Infallible>(()) |
| 47 | /// ``` |
| 48 | #[derive (Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)] |
| 49 | #[cfg_attr (feature = "defmt" , derive(::defmt::Format))] |
| 50 | pub struct Rectangle { |
| 51 | /// Top left point of the rectangle. |
| 52 | pub top_left: Point, |
| 53 | |
| 54 | /// Size of the rectangle. |
| 55 | pub size: Size, |
| 56 | } |
| 57 | |
| 58 | impl Dimensions for Rectangle { |
| 59 | fn bounding_box(&self) -> Rectangle { |
| 60 | *self |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | impl PointsIter for Rectangle { |
| 65 | type Iter = Points; |
| 66 | |
| 67 | fn points(&self) -> Self::Iter { |
| 68 | self::Points::new(self) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | /// Returns the center offset. |
| 73 | /// |
| 74 | /// The center offset is defined as the offset between the top left corner and |
| 75 | /// the center point of a rectangle with the given size. |
| 76 | const fn center_offset(size: Size) -> Size { |
| 77 | size.saturating_sub(Size::new_equal(1)).div_u32(2) |
| 78 | } |
| 79 | |
| 80 | impl Rectangle { |
| 81 | /// Creates a new rectangle from the top left point and the size. |
| 82 | pub const fn new(top_left: Point, size: Size) -> Self { |
| 83 | Rectangle { top_left, size } |
| 84 | } |
| 85 | |
| 86 | /// Creates a new rectangle from two corners. |
| 87 | pub fn with_corners(corner_1: Point, corner_2: Point) -> Self { |
| 88 | let left = min(corner_1.x, corner_2.x); |
| 89 | let top = min(corner_1.y, corner_2.y); |
| 90 | |
| 91 | Rectangle { |
| 92 | top_left: Point::new(left, top), |
| 93 | size: Size::from_bounding_box(corner_1, corner_2), |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | /// Creates a new rectangle from the center point and the size. |
| 98 | /// |
| 99 | /// For rectangles with even width and/or height the top left corner doesn't |
| 100 | /// align with the pixel grid. Because of this the coordinates of the top left |
| 101 | /// corner will be rounded up to the nearest integer coordinate. |
| 102 | pub const fn with_center(center: Point, size: Size) -> Self { |
| 103 | Rectangle { |
| 104 | top_left: center.sub_size(center_offset(size)), |
| 105 | size, |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | /// Returns a zero sized rectangle. |
| 110 | pub const fn zero() -> Rectangle { |
| 111 | Rectangle::new(Point::zero(), Size::zero()) |
| 112 | } |
| 113 | |
| 114 | /// Returns the center of this rectangle. |
| 115 | /// |
| 116 | /// For rectangles with even width and/or height the returned value is rounded down |
| 117 | /// to the nearest integer coordinate. |
| 118 | pub fn center(&self) -> Point { |
| 119 | self.top_left + center_offset(self.size) |
| 120 | } |
| 121 | |
| 122 | /// Returns the bottom right corner of this rectangle. |
| 123 | /// |
| 124 | /// Because the smallest rectangle that can be represented by its corners |
| 125 | /// has a size of 1 x 1 pixels, this function returns `None` if the width or |
| 126 | /// height of the rectangle is zero. |
| 127 | pub fn bottom_right(&self) -> Option<Point> { |
| 128 | if self.size.width > 0 && self.size.height > 0 { |
| 129 | Some(self.top_left + self.size - Point::new(1, 1)) |
| 130 | } else { |
| 131 | None |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | /// Return whether the rectangle contains a given point. |
| 136 | pub fn contains(&self, point: Point) -> bool { |
| 137 | if point.x >= self.top_left.x && point.y >= self.top_left.y { |
| 138 | self.bottom_right().map_or(false, |bottom_right| { |
| 139 | point.x <= bottom_right.x && point.y <= bottom_right.y |
| 140 | }) |
| 141 | } else { |
| 142 | false |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | /// Returns a new `Rectangle` containing the intersection of `self` and `other`. |
| 147 | /// |
| 148 | /// If no intersection is present, this method will return a zero sized rectangle. |
| 149 | /// |
| 150 | /// # Examples |
| 151 | /// |
| 152 | /// ## Intersection |
| 153 | /// |
| 154 | /// This example draws two rectangles to a mock display using the `.` character, along with |
| 155 | /// their intersection shown with `#` characters. |
| 156 | /// |
| 157 | /// ```rust |
| 158 | /// use embedded_graphics::{ |
| 159 | /// mock_display::MockDisplay, pixelcolor::BinaryColor, prelude::*, |
| 160 | /// primitives::{Rectangle, PrimitiveStyle}, |
| 161 | /// }; |
| 162 | /// |
| 163 | /// let mut display = MockDisplay::new(); |
| 164 | /// # display.set_allow_overdraw(true); |
| 165 | /// |
| 166 | /// let rect1 = Rectangle::new(Point::zero(), Size::new(7, 8)); |
| 167 | /// let rect2 = Rectangle::new(Point::new(2, 3), Size::new(10, 7)); |
| 168 | /// |
| 169 | /// let intersection = rect1.intersection(&rect2); |
| 170 | /// |
| 171 | /// rect1 |
| 172 | /// .into_styled(PrimitiveStyle::with_stroke(BinaryColor::Off, 1)) |
| 173 | /// .draw(&mut display)?; |
| 174 | /// |
| 175 | /// rect2 |
| 176 | /// .into_styled(PrimitiveStyle::with_stroke(BinaryColor::Off, 1)) |
| 177 | /// .draw(&mut display)?; |
| 178 | /// |
| 179 | /// intersection |
| 180 | /// .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) |
| 181 | /// .draw(&mut display)?; |
| 182 | /// |
| 183 | /// display.assert_pattern(&[ |
| 184 | /// "....... " , |
| 185 | /// ". . " , |
| 186 | /// ". . " , |
| 187 | /// ". #####....." , |
| 188 | /// ". # # ." , |
| 189 | /// ". # # ." , |
| 190 | /// ". # # ." , |
| 191 | /// "..##### ." , |
| 192 | /// " . ." , |
| 193 | /// " .........." , |
| 194 | /// ]); |
| 195 | /// # Ok::<(), core::convert::Infallible>(()) |
| 196 | /// ``` |
| 197 | /// |
| 198 | /// ## No intersection |
| 199 | /// |
| 200 | /// This example creates two rectangles with no intersection between them. In this case, |
| 201 | /// `intersection` returns a zero-sized rectangle. |
| 202 | /// |
| 203 | /// ```rust |
| 204 | /// use embedded_graphics::{prelude::*, primitives::{Rectangle, PrimitiveStyle}}; |
| 205 | /// |
| 206 | /// let rect1 = Rectangle::new(Point::zero(), Size::new(7, 8)); |
| 207 | /// let rect2 = Rectangle::new(Point::new(10, 15), Size::new(10, 7)); |
| 208 | /// |
| 209 | /// let intersection = rect1.intersection(&rect2); |
| 210 | /// |
| 211 | /// assert!(intersection.is_zero_sized()); |
| 212 | /// # Ok::<(), core::convert::Infallible>(()) |
| 213 | /// ``` |
| 214 | pub fn intersection(&self, other: &Rectangle) -> Rectangle { |
| 215 | match (other.bottom_right(), self.bottom_right()) { |
| 216 | (Some(other_bottom_right), Some(self_bottom_right)) => { |
| 217 | if overlaps( |
| 218 | self.top_left.x..=self_bottom_right.x, |
| 219 | other.top_left.x..=other_bottom_right.x, |
| 220 | ) && overlaps( |
| 221 | self.top_left.y..=self_bottom_right.y, |
| 222 | other.top_left.y..=other_bottom_right.y, |
| 223 | ) { |
| 224 | return Rectangle::with_corners( |
| 225 | self.top_left.component_max(other.top_left), |
| 226 | self_bottom_right.component_min(other_bottom_right), |
| 227 | ); |
| 228 | } |
| 229 | } |
| 230 | (Some(_other_bottom_right), None) => { |
| 231 | // Check if zero sized self is inside other |
| 232 | if other.contains(self.top_left) { |
| 233 | return *self; |
| 234 | } |
| 235 | } |
| 236 | (None, Some(_self_bottom_right)) => { |
| 237 | // Check if zero sized other is inside self |
| 238 | if self.contains(other.top_left) { |
| 239 | return *other; |
| 240 | } |
| 241 | } |
| 242 | (None, None) => (), |
| 243 | }; |
| 244 | |
| 245 | // No overlap present |
| 246 | Rectangle::zero() |
| 247 | } |
| 248 | |
| 249 | /// Returns a resized copy of this rectangle. |
| 250 | /// |
| 251 | /// The rectangle is resized relative to the given anchor point. |
| 252 | /// |
| 253 | /// # Examples |
| 254 | /// |
| 255 | /// ``` |
| 256 | /// use embedded_graphics::{ |
| 257 | /// prelude::*, |
| 258 | /// primitives::rectangle::Rectangle, |
| 259 | /// geometry::AnchorPoint, |
| 260 | /// }; |
| 261 | /// |
| 262 | /// let rect = Rectangle::new(Point::new(20, 20), Size::new(10, 20)); |
| 263 | /// let resized = rect.resized(Size::new(20, 10), AnchorPoint::Center); |
| 264 | /// |
| 265 | /// assert_eq!( |
| 266 | /// resized, |
| 267 | /// Rectangle::new(Point::new(15, 25), Size::new(20, 10)) |
| 268 | /// ); |
| 269 | /// ``` |
| 270 | pub fn resized(&self, size: Size, anchor_point: AnchorPoint) -> Self { |
| 271 | let mut resized = self.clone(); |
| 272 | resized.resize_width_mut(size.width, anchor_point.x()); |
| 273 | resized.resize_height_mut(size.height, anchor_point.y()); |
| 274 | |
| 275 | resized |
| 276 | } |
| 277 | |
| 278 | /// Returns a new rectangle with the given width, resized relative to the given anchor edge. |
| 279 | /// |
| 280 | /// # Examples |
| 281 | /// |
| 282 | /// ``` |
| 283 | /// use embedded_graphics::{ |
| 284 | /// prelude::*, |
| 285 | /// primitives::rectangle::Rectangle, |
| 286 | /// geometry::AnchorX, |
| 287 | /// }; |
| 288 | /// |
| 289 | /// let rect = Rectangle::new(Point::new(20, 20), Size::new(10, 20)); |
| 290 | /// let resized = rect.resized_width(20, AnchorX::Center); |
| 291 | /// |
| 292 | /// assert_eq!( |
| 293 | /// resized, |
| 294 | /// Rectangle::new(Point::new(15, 20), Size::new(20, 20)) |
| 295 | /// ); |
| 296 | /// ``` |
| 297 | pub fn resized_width(&self, width: u32, anchor_x: AnchorX) -> Self { |
| 298 | let mut resized = self.clone(); |
| 299 | resized.resize_width_mut(width, anchor_x); |
| 300 | |
| 301 | resized |
| 302 | } |
| 303 | |
| 304 | /// Returns a new rectangle with the given height, resized relative to the given anchor edge. |
| 305 | /// |
| 306 | /// # Examples |
| 307 | /// |
| 308 | /// ``` |
| 309 | /// use embedded_graphics::{ |
| 310 | /// prelude::*, |
| 311 | /// primitives::rectangle::Rectangle, |
| 312 | /// geometry::AnchorY, |
| 313 | /// }; |
| 314 | /// |
| 315 | /// let rect = Rectangle::new(Point::new(20, 20), Size::new(10, 20)); |
| 316 | /// let resized = rect.resized_height(10, AnchorY::Center); |
| 317 | /// |
| 318 | /// assert_eq!( |
| 319 | /// resized, |
| 320 | /// Rectangle::new(Point::new(20, 25), Size::new(10, 10)) |
| 321 | /// ); |
| 322 | /// ``` |
| 323 | pub fn resized_height(&self, height: u32, anchor_y: AnchorY) -> Self { |
| 324 | let mut resized = self.clone(); |
| 325 | resized.resize_height_mut(height, anchor_y); |
| 326 | |
| 327 | resized |
| 328 | } |
| 329 | |
| 330 | fn resize_width_mut(&mut self, width: u32, anchor_x: AnchorX) { |
| 331 | // Assume size = 1 for zero sized dimensions. |
| 332 | let delta = |
| 333 | self.size.width.saturating_as::<i32>().max(1) - width.saturating_as::<i32>().max(1); |
| 334 | |
| 335 | self.top_left.x += match anchor_x { |
| 336 | AnchorX::Left => 0, |
| 337 | AnchorX::Center => delta / 2, |
| 338 | AnchorX::Right => delta, |
| 339 | }; |
| 340 | self.size.width = width; |
| 341 | } |
| 342 | |
| 343 | fn resize_height_mut(&mut self, height: u32, anchor_y: AnchorY) { |
| 344 | // Assume size = 1 for zero sized dimensions. |
| 345 | let delta = |
| 346 | self.size.height.saturating_as::<i32>().max(1) - height.saturating_as::<i32>().max(1); |
| 347 | |
| 348 | self.top_left.y += match anchor_y { |
| 349 | AnchorY::Top => 0, |
| 350 | AnchorY::Center => delta / 2, |
| 351 | AnchorY::Bottom => delta, |
| 352 | }; |
| 353 | self.size.height = height; |
| 354 | } |
| 355 | |
| 356 | /// Offset the rectangle by a given value. |
| 357 | /// |
| 358 | /// Negative values will shrink the rectangle. |
| 359 | pub fn offset(&self, offset: i32) -> Self { |
| 360 | let size = if offset >= 0 { |
| 361 | self.size.saturating_add(Size::new_equal(offset as u32 * 2)) |
| 362 | } else { |
| 363 | self.size |
| 364 | .saturating_sub(Size::new_equal((-offset) as u32 * 2)) |
| 365 | }; |
| 366 | |
| 367 | Self::with_center(self.center(), size) |
| 368 | } |
| 369 | |
| 370 | /// Returns an anchor point. |
| 371 | /// |
| 372 | /// # Examples |
| 373 | /// ``` |
| 374 | /// use embedded_graphics::{ |
| 375 | /// prelude::*, |
| 376 | /// primitives::rectangle::Rectangle, |
| 377 | /// geometry::AnchorPoint, |
| 378 | /// }; |
| 379 | /// |
| 380 | /// let mut rect = Rectangle::new(Point::new(20, 20), Size::new(11, 21)); |
| 381 | /// |
| 382 | /// assert_eq!(rect.anchor_point(AnchorPoint::TopLeft), Point::new(20, 20)); |
| 383 | /// assert_eq!( |
| 384 | /// rect.anchor_point(AnchorPoint::BottomCenter), |
| 385 | /// Point::new(25, 40) |
| 386 | /// ); |
| 387 | /// ``` |
| 388 | pub fn anchor_point(&self, anchor_point: AnchorPoint) -> Point { |
| 389 | Point::new( |
| 390 | self.anchor_x(anchor_point.x()), |
| 391 | self.anchor_y(anchor_point.y()), |
| 392 | ) |
| 393 | } |
| 394 | |
| 395 | /// Returns the X coordinate of a given anchor edge of the rectangle. |
| 396 | /// |
| 397 | /// # Examples |
| 398 | /// |
| 399 | /// ``` |
| 400 | /// use embedded_graphics::{ |
| 401 | /// prelude::*, |
| 402 | /// primitives::rectangle::Rectangle, |
| 403 | /// geometry::AnchorX, |
| 404 | /// }; |
| 405 | /// |
| 406 | /// let mut rect = Rectangle::new(Point::new(20, 20), Size::new(11, 21)); |
| 407 | /// |
| 408 | /// assert_eq!(rect.anchor_x(AnchorX::Left), 20); |
| 409 | /// assert_eq!(rect.anchor_x(AnchorX::Center), 25); |
| 410 | /// ``` |
| 411 | pub fn anchor_x(&self, anchor_x: AnchorX) -> i32 { |
| 412 | // Assume size = 1 for zero sized dimensions. |
| 413 | let delta = self.size.width.saturating_as::<i32>().max(1) - 1; |
| 414 | |
| 415 | self.top_left.x |
| 416 | + match anchor_x { |
| 417 | AnchorX::Left => 0, |
| 418 | AnchorX::Center => delta / 2, |
| 419 | AnchorX::Right => delta, |
| 420 | } |
| 421 | } |
| 422 | |
| 423 | /// Returns the Y coordinate of a given anchor edge of the rectangle. |
| 424 | /// |
| 425 | /// # Examples |
| 426 | /// |
| 427 | /// ``` |
| 428 | /// use embedded_graphics::{ |
| 429 | /// prelude::*, |
| 430 | /// primitives::rectangle::Rectangle, |
| 431 | /// geometry::AnchorY, |
| 432 | /// }; |
| 433 | /// |
| 434 | /// let mut rect = Rectangle::new(Point::new(20, 20), Size::new(11, 21)); |
| 435 | /// |
| 436 | /// assert_eq!(rect.anchor_y(AnchorY::Top), 20); |
| 437 | /// assert_eq!(rect.anchor_y(AnchorY::Bottom), 40); |
| 438 | /// ``` |
| 439 | pub fn anchor_y(&self, anchor_y: AnchorY) -> i32 { |
| 440 | // Assume size = 1 for zero sized dimensions. |
| 441 | let delta = self.size.height.saturating_as::<i32>().max(1) - 1; |
| 442 | |
| 443 | self.top_left.y |
| 444 | + match anchor_y { |
| 445 | AnchorY::Top => 0, |
| 446 | AnchorY::Center => delta / 2, |
| 447 | AnchorY::Bottom => delta, |
| 448 | } |
| 449 | } |
| 450 | |
| 451 | /// Returns the range of Y coordinates in this rectangle. |
| 452 | /// |
| 453 | /// # Examples |
| 454 | /// |
| 455 | /// ``` |
| 456 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
| 457 | /// |
| 458 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(3, 4)); |
| 459 | /// assert_eq!(rect.rows(), 20..24); |
| 460 | /// ``` |
| 461 | /// |
| 462 | /// By combining this method with [`columns`] it is possible to iterate over all pixels inside |
| 463 | /// the rectangle. This can be more flexible than using the [`points`] iterator, for example, |
| 464 | /// if a different iteration order is required or some operations should be called once per row. |
| 465 | /// |
| 466 | /// ``` |
| 467 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
| 468 | /// |
| 469 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(3, 4)); |
| 470 | /// |
| 471 | /// // Iterate over the y coordinates of the rows in reverse order. |
| 472 | /// for y in rect.rows().rev() { |
| 473 | /// for x in rect.columns() { |
| 474 | /// // use x, y coordinates |
| 475 | /// } |
| 476 | /// } |
| 477 | /// ``` |
| 478 | /// |
| 479 | /// [`columns`]: Rectangle::columns() |
| 480 | /// [`points`]: super::PointsIter::points |
| 481 | pub fn rows(&self) -> Range<i32> { |
| 482 | self.top_left.y |
| 483 | ..self |
| 484 | .top_left |
| 485 | .y |
| 486 | .saturating_add(self.size.height.saturating_as()) |
| 487 | } |
| 488 | |
| 489 | /// Returns the range of X coordinates in this rectangle. |
| 490 | /// |
| 491 | /// # Examples |
| 492 | /// |
| 493 | /// ``` |
| 494 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
| 495 | /// |
| 496 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(3, 4)); |
| 497 | /// |
| 498 | /// assert_eq!(rect.columns(), 10..13); |
| 499 | /// ``` |
| 500 | /// |
| 501 | /// By combining this method with [`rows`] it is possible to iterator over all pixels inside |
| 502 | /// the rectangle. This can be more flexible than using the [`points`] iterator, for example, |
| 503 | /// if a different iteration order is required or some operations should be called once per row. |
| 504 | /// |
| 505 | /// ``` |
| 506 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
| 507 | /// |
| 508 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(3, 4)); |
| 509 | /// |
| 510 | /// // Iterate over all points starting from the top right corner and advancing downwards. |
| 511 | /// for x in rect.columns().rev() { |
| 512 | /// for y in rect.rows() { |
| 513 | /// // use x, y coordinates |
| 514 | /// } |
| 515 | /// } |
| 516 | /// ``` |
| 517 | /// |
| 518 | /// [`rows`]: Rectangle::rows() |
| 519 | /// [`points`]: super::PointsIter::points |
| 520 | pub fn columns(&self) -> Range<i32> { |
| 521 | self.top_left.x |
| 522 | ..self |
| 523 | .top_left |
| 524 | .x |
| 525 | .saturating_add(self.size.width.saturating_as()) |
| 526 | } |
| 527 | |
| 528 | /// Returns `true` is the rectangle is zero sized. |
| 529 | /// |
| 530 | /// A rectangle is zero sized if the width or height are zero. |
| 531 | /// |
| 532 | /// # Examples |
| 533 | /// ``` |
| 534 | /// use embedded_graphics::{prelude::*, primitives::Rectangle}; |
| 535 | /// |
| 536 | /// let rect = Rectangle::new(Point::new(10, 20), Size::new(10, 20)); |
| 537 | /// assert_eq!(rect.is_zero_sized(), false); |
| 538 | /// |
| 539 | /// let rect = Rectangle::new(Point::new(10, 20), Size::zero()); |
| 540 | /// assert_eq!(rect.is_zero_sized(), true); |
| 541 | /// ``` |
| 542 | pub const fn is_zero_sized(&self) -> bool { |
| 543 | self.size.height == 0 || self.size.width == 0 |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | /// Checks if the two ranges overlap. |
| 548 | fn overlaps(first: RangeInclusive<i32>, second: RangeInclusive<i32>) -> bool { |
| 549 | second.contains(item:first.start()) |
| 550 | || second.contains(item:first.end()) |
| 551 | || first.start() < second.start() && first.end() > second.end() |
| 552 | } |
| 553 | |
| 554 | #[cfg (test)] |
| 555 | mod tests { |
| 556 | use super::*; |
| 557 | use crate::geometry::{Dimensions, Point, Size}; |
| 558 | |
| 559 | #[test ] |
| 560 | fn dimensions() { |
| 561 | let rect = Rectangle::new(Point::new(5, 10), Size::new(10, 20)); |
| 562 | |
| 563 | assert_eq!( |
| 564 | rect.bounding_box(), |
| 565 | Rectangle::new(Point::new(5, 10), Size::new(10, 20)) |
| 566 | ); |
| 567 | } |
| 568 | |
| 569 | #[test ] |
| 570 | fn center() { |
| 571 | let odd = Rectangle::new(Point::new(10, 20), Size::new(5, 7)); |
| 572 | assert_eq!(odd.center(), Point::new(12, 23)); |
| 573 | |
| 574 | let even = Rectangle::new(Point::new(20, 30), Size::new(4, 8)); |
| 575 | assert_eq!(even.center(), Point::new(21, 33)); |
| 576 | } |
| 577 | |
| 578 | #[test ] |
| 579 | fn bottom_right() { |
| 580 | let zero = Rectangle::new(Point::new(10, 20), Size::zero()); |
| 581 | assert_eq!(zero.bottom_right(), None); |
| 582 | |
| 583 | let odd = Rectangle::new(Point::new(10, 20), Size::new(5, 7)); |
| 584 | assert_eq!(odd.bottom_right(), Some(Point::new(14, 26))); |
| 585 | |
| 586 | let even = Rectangle::new(Point::new(20, 30), Size::new(4, 8)); |
| 587 | assert_eq!(even.bottom_right(), Some(Point::new(23, 37))); |
| 588 | } |
| 589 | |
| 590 | #[test ] |
| 591 | fn rectangle_intersection() { |
| 592 | let rect1 = Rectangle::new(Point::new_equal(10), Size::new(20, 30)); |
| 593 | let rect2 = Rectangle::new(Point::new_equal(25), Size::new(30, 40)); |
| 594 | |
| 595 | assert_eq!( |
| 596 | rect1.intersection(&rect2), |
| 597 | Rectangle::new(Point::new_equal(25), Size::new(5, 15)) |
| 598 | ); |
| 599 | } |
| 600 | |
| 601 | #[test ] |
| 602 | fn rectangle_no_intersection() { |
| 603 | let rect1 = Rectangle::new(Point::new_equal(10), Size::new(20, 30)); |
| 604 | let rect2 = Rectangle::new(Point::new_equal(35), Size::new(30, 40)); |
| 605 | |
| 606 | assert_eq!( |
| 607 | rect1.intersection(&rect2), |
| 608 | Rectangle::new(Point::zero(), Size::zero()) |
| 609 | ); |
| 610 | } |
| 611 | |
| 612 | #[test ] |
| 613 | fn rectangle_complete_intersection() { |
| 614 | let rect1 = Rectangle::new(Point::new_equal(10), Size::new(20, 30)); |
| 615 | let rect2 = rect1; |
| 616 | |
| 617 | assert_eq!(rect1.intersection(&rect2), rect1); |
| 618 | } |
| 619 | |
| 620 | #[test ] |
| 621 | fn rectangle_contained_intersection() { |
| 622 | let rect1 = Rectangle::with_corners(Point::new_equal(10), Point::new(20, 30)); |
| 623 | let rect2 = Rectangle::with_corners(Point::new_equal(5), Point::new(30, 40)); |
| 624 | |
| 625 | assert_eq!(rect1.intersection(&rect2), rect1); |
| 626 | } |
| 627 | |
| 628 | #[test ] |
| 629 | fn zero_sized_intersection() { |
| 630 | let rect1 = Rectangle::new(Point::new(1, 2), Size::new(0, 0)); |
| 631 | let rect2 = Rectangle::new(Point::new(-10, -10), Size::new(20, 20)); |
| 632 | |
| 633 | assert_eq!(rect1.intersection(&rect2), rect1); |
| 634 | |
| 635 | let rect1 = Rectangle::new(Point::new(-10, -10), Size::new(20, 20)); |
| 636 | let rect2 = Rectangle::new(Point::new(2, 3), Size::new(0, 0)); |
| 637 | |
| 638 | assert_eq!(rect1.intersection(&rect2), rect2); |
| 639 | } |
| 640 | |
| 641 | /// Test for issue #452 |
| 642 | /// |
| 643 | /// Rectangles can intersect even if no corner of any rectangle is contained inside the other |
| 644 | /// rectangle. |
| 645 | /// |
| 646 | /// Example: |
| 647 | /// |
| 648 | /// **** |
| 649 | /// * * |
| 650 | /// ############ |
| 651 | /// # * * # |
| 652 | /// # * * # |
| 653 | /// ############ |
| 654 | /// * * |
| 655 | /// **** |
| 656 | #[test ] |
| 657 | fn issue_452_broken_intersection_check() { |
| 658 | let rect1 = Rectangle::new(Point::new(50, 0), Size::new(75, 200)); |
| 659 | let rect2 = Rectangle::new(Point::new(0, 75), Size::new(200, 50)); |
| 660 | |
| 661 | let expected = Rectangle::new(Point::new(50, 75), Size::new(75, 50)); |
| 662 | |
| 663 | assert_eq!(rect1.intersection(&rect2), expected); |
| 664 | assert_eq!(rect2.intersection(&rect1), expected); |
| 665 | } |
| 666 | |
| 667 | #[test ] |
| 668 | fn offset() { |
| 669 | let center = Point::new(10, 20); |
| 670 | let rect = Rectangle::with_center(center, Size::new(3, 4)); |
| 671 | |
| 672 | assert_eq!(rect.offset(0), rect); |
| 673 | |
| 674 | assert_eq!( |
| 675 | rect.offset(1), |
| 676 | Rectangle::with_center(center, Size::new(5, 6)) |
| 677 | ); |
| 678 | assert_eq!( |
| 679 | rect.offset(2), |
| 680 | Rectangle::with_center(center, Size::new(7, 8)) |
| 681 | ); |
| 682 | |
| 683 | assert_eq!( |
| 684 | rect.offset(-1), |
| 685 | Rectangle::with_center(center, Size::new(1, 2)) |
| 686 | ); |
| 687 | assert_eq!( |
| 688 | rect.offset(-2), |
| 689 | Rectangle::with_center(center, Size::new(0, 0)) |
| 690 | ); |
| 691 | assert_eq!( |
| 692 | rect.offset(-3), |
| 693 | Rectangle::with_center(center, Size::new(0, 0)) |
| 694 | ); |
| 695 | } |
| 696 | |
| 697 | fn test_resized(rect: Rectangle, target_size: Size, tests: &[(AnchorPoint, Point)]) { |
| 698 | for &(anchor_point, expected_top_left) in tests { |
| 699 | let resized = rect.resized(target_size, anchor_point); |
| 700 | let expected = Rectangle::new(expected_top_left, target_size); |
| 701 | |
| 702 | assert_eq!(resized, expected, "{:?}" , anchor_point); |
| 703 | |
| 704 | let resized_x = rect.resized_width(target_size.width, anchor_point.x()); |
| 705 | assert_eq!( |
| 706 | resized_x.top_left, |
| 707 | Point::new(resized.top_left.x, rect.top_left.y) |
| 708 | ); |
| 709 | assert_eq!( |
| 710 | resized_x.size, |
| 711 | Size::new(resized.size.width, rect.size.height) |
| 712 | ); |
| 713 | |
| 714 | let resized_y = rect.resized_height(target_size.height, anchor_point.y()); |
| 715 | assert_eq!( |
| 716 | resized_y.top_left, |
| 717 | Point::new(rect.top_left.x, resized.top_left.y) |
| 718 | ); |
| 719 | assert_eq!( |
| 720 | resized_y.size, |
| 721 | Size::new(rect.size.width, resized.size.height) |
| 722 | ); |
| 723 | } |
| 724 | } |
| 725 | |
| 726 | #[test ] |
| 727 | fn resized_smaller() { |
| 728 | test_resized( |
| 729 | Rectangle::new(Point::new(10, 20), Size::new(30, 40)), |
| 730 | Size::new(10, 20), |
| 731 | &[ |
| 732 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
| 733 | (AnchorPoint::TopCenter, Point::new(20, 20)), |
| 734 | (AnchorPoint::TopRight, Point::new(30, 20)), |
| 735 | (AnchorPoint::CenterLeft, Point::new(10, 30)), |
| 736 | (AnchorPoint::Center, Point::new(20, 30)), |
| 737 | (AnchorPoint::CenterRight, Point::new(30, 30)), |
| 738 | (AnchorPoint::BottomLeft, Point::new(10, 40)), |
| 739 | (AnchorPoint::BottomCenter, Point::new(20, 40)), |
| 740 | (AnchorPoint::BottomRight, Point::new(30, 40)), |
| 741 | ], |
| 742 | ); |
| 743 | } |
| 744 | |
| 745 | #[test ] |
| 746 | fn resized_larger() { |
| 747 | test_resized( |
| 748 | Rectangle::new(Point::new(10, 20), Size::new(30, 40)), |
| 749 | Size::new(40, 50), |
| 750 | &[ |
| 751 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
| 752 | (AnchorPoint::TopCenter, Point::new(5, 20)), |
| 753 | (AnchorPoint::TopRight, Point::new(0, 20)), |
| 754 | (AnchorPoint::CenterLeft, Point::new(10, 15)), |
| 755 | (AnchorPoint::Center, Point::new(5, 15)), |
| 756 | (AnchorPoint::CenterRight, Point::new(0, 15)), |
| 757 | (AnchorPoint::BottomLeft, Point::new(10, 10)), |
| 758 | (AnchorPoint::BottomCenter, Point::new(5, 10)), |
| 759 | (AnchorPoint::BottomRight, Point::new(0, 10)), |
| 760 | ], |
| 761 | ); |
| 762 | } |
| 763 | |
| 764 | #[test ] |
| 765 | fn resized_zero_sized() { |
| 766 | test_resized( |
| 767 | Rectangle::new(Point::new(10, 20), Size::zero()), |
| 768 | Size::new(5, 7), |
| 769 | &[ |
| 770 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
| 771 | (AnchorPoint::TopCenter, Point::new(8, 20)), |
| 772 | (AnchorPoint::TopRight, Point::new(6, 20)), |
| 773 | (AnchorPoint::CenterLeft, Point::new(10, 17)), |
| 774 | (AnchorPoint::Center, Point::new(8, 17)), |
| 775 | (AnchorPoint::CenterRight, Point::new(6, 17)), |
| 776 | (AnchorPoint::BottomLeft, Point::new(10, 14)), |
| 777 | (AnchorPoint::BottomCenter, Point::new(8, 14)), |
| 778 | (AnchorPoint::BottomRight, Point::new(6, 14)), |
| 779 | ], |
| 780 | ); |
| 781 | } |
| 782 | |
| 783 | #[test ] |
| 784 | fn resized_to_zero_sized() { |
| 785 | test_resized( |
| 786 | Rectangle::new(Point::new(10, 20), Size::new(21, 31)), |
| 787 | Size::zero(), |
| 788 | &[ |
| 789 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
| 790 | (AnchorPoint::TopCenter, Point::new(20, 20)), |
| 791 | (AnchorPoint::TopRight, Point::new(30, 20)), |
| 792 | (AnchorPoint::CenterLeft, Point::new(10, 35)), |
| 793 | (AnchorPoint::Center, Point::new(20, 35)), |
| 794 | (AnchorPoint::CenterRight, Point::new(30, 35)), |
| 795 | (AnchorPoint::BottomLeft, Point::new(10, 50)), |
| 796 | (AnchorPoint::BottomCenter, Point::new(20, 50)), |
| 797 | (AnchorPoint::BottomRight, Point::new(30, 50)), |
| 798 | ], |
| 799 | ); |
| 800 | } |
| 801 | |
| 802 | #[test ] |
| 803 | fn anchor_point() { |
| 804 | let rect = Rectangle::new(Point::new(10, 20), Size::new(21, 31)); |
| 805 | |
| 806 | for &(anchor_point, expected) in &[ |
| 807 | (AnchorPoint::TopLeft, Point::new(10, 20)), |
| 808 | (AnchorPoint::TopCenter, Point::new(20, 20)), |
| 809 | (AnchorPoint::TopRight, Point::new(30, 20)), |
| 810 | (AnchorPoint::CenterLeft, Point::new(10, 35)), |
| 811 | (AnchorPoint::Center, Point::new(20, 35)), |
| 812 | (AnchorPoint::CenterRight, Point::new(30, 35)), |
| 813 | (AnchorPoint::BottomLeft, Point::new(10, 50)), |
| 814 | (AnchorPoint::BottomCenter, Point::new(20, 50)), |
| 815 | (AnchorPoint::BottomRight, Point::new(30, 50)), |
| 816 | ] { |
| 817 | assert_eq!( |
| 818 | rect.anchor_point(anchor_point), |
| 819 | expected, |
| 820 | "{:?}" , |
| 821 | anchor_point, |
| 822 | ); |
| 823 | |
| 824 | assert_eq!( |
| 825 | rect.anchor_x(anchor_point.x()), |
| 826 | expected.x, |
| 827 | "{:?}.x()" , |
| 828 | anchor_point |
| 829 | ); |
| 830 | |
| 831 | assert_eq!( |
| 832 | rect.anchor_y(anchor_point.y()), |
| 833 | expected.y, |
| 834 | "{:?}.y()" , |
| 835 | anchor_point |
| 836 | ); |
| 837 | } |
| 838 | } |
| 839 | |
| 840 | #[test ] |
| 841 | fn rows_and_columns_zero_sized() { |
| 842 | let rect = Rectangle::zero(); |
| 843 | |
| 844 | assert_eq!( |
| 845 | rect.rows().next(), |
| 846 | None, |
| 847 | "the rows iterator for a zero sized rectangle shouldn't return any items" |
| 848 | ); |
| 849 | |
| 850 | assert_eq!( |
| 851 | rect.columns().next(), |
| 852 | None, |
| 853 | "the columns iterator for a zero sized rectangle shouldn't return any items" |
| 854 | ); |
| 855 | } |
| 856 | } |
| 857 | |