geometry.rs (17076B)
1 use crate::hints::SizeHints; 2 use crate::window::Window; 3 4 use std::ops::Add; 5 use std::ops::AddAssign; 6 use std::ops::Sub; 7 use std::ops::SubAssign; 8 9 pub type Extents = Padding; 10 11 #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)] 12 pub enum Edge { 13 Left, 14 Right, 15 Top, 16 Bottom, 17 } 18 19 #[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] 20 pub enum Corner { 21 TopLeft, 22 TopRight, 23 BottomLeft, 24 BottomRight, 25 } 26 27 #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] 28 pub struct Pos { 29 pub x: i32, 30 pub y: i32, 31 } 32 33 impl Default for Pos { 34 fn default() -> Self { 35 Self { 36 x: 0, 37 y: 0, 38 } 39 } 40 } 41 42 impl Pos { 43 pub fn from_center_of_region(region: Region) -> Self { 44 Self { 45 x: region.pos.x + (region.dim.w as f32 / 2f32) as i32, 46 y: region.pos.y + (region.dim.h as f32 / 2f32) as i32, 47 } 48 } 49 50 pub fn from_center_of_dim(dim: Dim) -> Self { 51 Self { 52 x: dim.w / 2, 53 y: dim.h / 2, 54 } 55 } 56 57 pub fn values(&self) -> (i32, i32) { 58 (self.x, self.y) 59 } 60 61 pub fn dist( 62 &self, 63 pos: Self, 64 ) -> Distance { 65 Distance { 66 dx: (pos.x - self.x), 67 dy: (pos.y - self.y), 68 } 69 } 70 71 pub fn relative_to( 72 &self, 73 pos: Self, 74 ) -> Self { 75 Pos { 76 x: self.x - pos.x, 77 y: self.y - pos.y, 78 } 79 } 80 81 pub fn is_origin(&self) -> bool { 82 *self 83 == Pos { 84 x: 0, 85 y: 0, 86 } 87 } 88 } 89 90 impl Add<Pos> for Pos { 91 type Output = Self; 92 93 fn add( 94 self, 95 other: Pos, 96 ) -> Self::Output { 97 Self::Output { 98 x: self.x + other.x, 99 y: self.y + other.y, 100 } 101 } 102 } 103 104 #[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] 105 pub struct Dim { 106 pub w: i32, 107 pub h: i32, 108 } 109 110 impl Default for Dim { 111 fn default() -> Self { 112 Self { 113 w: 0, 114 h: 0, 115 } 116 } 117 } 118 119 impl Dim { 120 pub fn values(&self) -> (i32, i32) { 121 (self.w, self.h) 122 } 123 124 pub fn center(&self) -> Pos { 125 Pos { 126 x: (self.w as f32 / 2f32) as i32, 127 y: (self.h as f32 / 2f32) as i32, 128 } 129 } 130 131 pub fn nearest_corner( 132 &self, 133 pos: Pos, 134 ) -> Corner { 135 let center = self.center(); 136 137 if pos.x >= center.x { 138 if pos.y >= center.y { 139 Corner::BottomRight 140 } else { 141 Corner::TopRight 142 } 143 } else { 144 if pos.y >= center.y { 145 Corner::BottomLeft 146 } else { 147 Corner::TopLeft 148 } 149 } 150 } 151 } 152 153 impl Add<Dim> for Pos { 154 type Output = Self; 155 156 fn add( 157 self, 158 other: Dim, 159 ) -> Self::Output { 160 Self::Output { 161 x: self.x + other.w, 162 y: self.y + other.h, 163 } 164 } 165 } 166 167 impl Sub<Dim> for Pos { 168 type Output = Self; 169 170 fn sub( 171 self, 172 other: Dim, 173 ) -> Self::Output { 174 Self::Output { 175 x: self.x - other.w, 176 y: self.y - other.h, 177 } 178 } 179 } 180 181 impl Sub for Pos { 182 type Output = Dim; 183 184 fn sub( 185 self, 186 other: Self, 187 ) -> Self::Output { 188 Self::Output { 189 w: self.x - other.x, 190 h: self.y - other.y, 191 } 192 } 193 } 194 195 #[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] 196 pub struct Region { 197 pub pos: Pos, 198 pub dim: Dim, 199 } 200 201 impl Default for Region { 202 fn default() -> Self { 203 Self { 204 pos: Default::default(), 205 dim: Default::default(), 206 } 207 } 208 } 209 210 impl Region { 211 pub fn new( 212 x: i32, 213 y: i32, 214 w: i32, 215 h: i32, 216 ) -> Self { 217 Self { 218 pos: Pos { 219 x, 220 y, 221 }, 222 dim: Dim { 223 w, 224 h, 225 }, 226 } 227 } 228 229 pub fn values(&self) -> (Pos, Dim) { 230 (self.pos, self.dim) 231 } 232 233 pub fn with_size_hints( 234 self, 235 size_hints: &Option<SizeHints>, 236 ) -> Self { 237 let mut geometry = self; 238 239 if let Some(size_hints) = size_hints { 240 size_hints.apply(&mut geometry.dim); 241 } 242 243 geometry 244 } 245 246 pub fn encompasses( 247 &self, 248 pos: Pos, 249 ) -> bool { 250 pos.x >= self.pos.x 251 && pos.y >= self.pos.y 252 && pos.x <= self.pos.x + self.dim.w 253 && pos.y <= self.pos.y + self.dim.h 254 } 255 256 pub fn contains( 257 &self, 258 region: Region, 259 ) -> bool { 260 self.encompasses(region.pos) && self.encompasses(region.bottom_right()) 261 } 262 263 pub fn occludes( 264 &self, 265 region: Region, 266 ) -> bool { 267 self.encompasses(region.pos) || region.encompasses(self.pos) 268 } 269 270 pub fn nearest_corner( 271 &self, 272 mut pos: Pos, 273 ) -> Corner { 274 pos += self.pos.dist(Pos { 275 x: 0, 276 y: 0, 277 }); 278 self.dim.nearest_corner(pos) 279 } 280 281 pub fn quadrant_center_from_pos( 282 &self, 283 pos: Pos, 284 ) -> Option<Pos> { 285 if self.encompasses(pos) { 286 return None; 287 } 288 289 let mut dists = vec![ 290 (Corner::TopLeft, self.pos.dist(pos).pythagorean()), 291 (Corner::TopRight, self.top_right().dist(pos).pythagorean()), 292 ( 293 Corner::BottomLeft, 294 self.bottom_left().dist(pos).pythagorean(), 295 ), 296 ( 297 Corner::BottomRight, 298 self.bottom_right().dist(pos).pythagorean(), 299 ), 300 ]; 301 302 dists.sort_by_key(|&(corner, dist)| dist); 303 304 match dists.first().unwrap() { 305 (Corner::TopLeft, _) => { 306 let (left, _) = self.split_at_width((self.dim.w as f64 / 2f64).round() as i32); 307 let (topleft, _) = left.split_at_height((left.dim.h as f64 / 2f64).round() as i32); 308 309 Some(Pos::from_center_of_region(topleft)) 310 }, 311 (Corner::TopRight, _) => { 312 let (_, right) = self.split_at_width((self.dim.w as f64 / 2f64).round() as i32); 313 let (topright, _) = 314 right.split_at_height((right.dim.h as f64 / 2f64).round() as i32); 315 316 Some(Pos::from_center_of_region(topright)) 317 }, 318 (Corner::BottomLeft, _) => { 319 let (left, _) = self.split_at_width((self.dim.w as f64 / 2f64).round() as i32); 320 let (_, bottomleft) = 321 left.split_at_height((left.dim.h as f64 / 2f64).round() as i32); 322 323 Some(Pos::from_center_of_region(bottomleft)) 324 }, 325 (Corner::BottomRight, _) => { 326 let (_, right) = self.split_at_width((self.dim.w as f64 / 2f64).round() as i32); 327 let (_, bottomright) = 328 right.split_at_height((right.dim.h as f64 / 2f64).round() as i32); 329 330 Some(Pos::from_center_of_region(bottomright)) 331 }, 332 } 333 } 334 335 pub fn split_at_width( 336 &self, 337 width: i32, 338 ) -> (Self, Self) { 339 let width = std::cmp::min(width, self.dim.w); 340 341 ( 342 Self { 343 dim: Dim { 344 w: width, 345 ..self.dim 346 }, 347 ..*self 348 }, 349 Self { 350 pos: Pos { 351 x: self.pos.x + width, 352 ..self.pos 353 }, 354 dim: Dim { 355 w: self.dim.w - width, 356 ..self.dim 357 }, 358 }, 359 ) 360 } 361 362 pub fn split_at_height( 363 &self, 364 height: i32, 365 ) -> (Self, Self) { 366 let height = std::cmp::min(height, self.dim.h); 367 368 ( 369 Self { 370 dim: Dim { 371 h: height, 372 ..self.dim 373 }, 374 ..*self 375 }, 376 Self { 377 pos: Pos { 378 y: self.pos.y + height, 379 ..self.pos 380 }, 381 dim: Dim { 382 h: self.dim.h - height, 383 ..self.dim 384 }, 385 }, 386 ) 387 } 388 389 pub fn with_minimum_dim( 390 self, 391 minimum_dim: &Dim, 392 ) -> Self { 393 Self { 394 pos: self.pos, 395 dim: Dim { 396 w: std::cmp::max(minimum_dim.w, self.dim.w), 397 h: std::cmp::max(minimum_dim.h, self.dim.h), 398 }, 399 } 400 } 401 402 pub fn with_maximum_dim( 403 self, 404 maximum_dim: &Dim, 405 ) -> Self { 406 Self { 407 pos: self.pos, 408 dim: Dim { 409 w: std::cmp::min(maximum_dim.w, self.dim.w), 410 h: std::cmp::min(maximum_dim.h, self.dim.h), 411 }, 412 } 413 } 414 415 pub fn from_absolute_inner_center( 416 self, 417 dim: Dim, 418 ) -> Self { 419 Self { 420 pos: Pos { 421 x: if dim.w > self.dim.w { 422 self.pos.x 423 } else { 424 self.pos.x + ((self.dim.w - dim.w) as f32 / 2f32) as i32 425 }, 426 y: if dim.h > self.dim.h { 427 self.pos.y 428 } else { 429 self.pos.y + ((self.dim.h - dim.h) as f32 / 2f32) as i32 430 }, 431 }, 432 dim, 433 } 434 } 435 436 pub fn without_extents( 437 mut self, 438 extents: Extents, 439 ) -> Self { 440 self.pos.x += extents.left; 441 self.pos.y += extents.top; 442 self.dim.w -= extents.left + extents.right; 443 self.dim.h -= extents.top + extents.bottom; 444 self 445 } 446 447 pub fn with_extents( 448 mut self, 449 extents: Extents, 450 ) -> Self { 451 self.pos.x -= extents.left; 452 self.pos.y -= extents.top; 453 self.dim.w += extents.left + extents.right; 454 self.dim.h += extents.top + extents.bottom; 455 self 456 } 457 458 pub fn top_right(&self) -> Pos { 459 Pos { 460 x: self.pos.x + self.dim.w, 461 y: self.pos.y, 462 } 463 } 464 465 pub fn bottom_left(&self) -> Pos { 466 Pos { 467 x: self.pos.x, 468 y: self.pos.y + self.dim.h, 469 } 470 } 471 472 pub fn bottom_right(&self) -> Pos { 473 Pos { 474 x: self.pos.x + self.dim.w, 475 y: self.pos.y + self.dim.h, 476 } 477 } 478 } 479 480 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 481 pub struct Padding { 482 pub left: i32, 483 pub right: i32, 484 pub top: i32, 485 pub bottom: i32, 486 } 487 488 impl Default for Padding { 489 fn default() -> Self { 490 Self { 491 left: 0, 492 right: 0, 493 top: 0, 494 bottom: 0, 495 } 496 } 497 } 498 499 impl Padding { 500 pub fn with_each_edge(size: i32) -> Self { 501 Self { 502 left: size, 503 right: size, 504 top: size, 505 bottom: size, 506 } 507 } 508 } 509 510 impl Add<Padding> for Region { 511 type Output = Self; 512 513 fn add( 514 self, 515 padding: Padding, 516 ) -> Self::Output { 517 Self::Output { 518 pos: Pos { 519 x: self.pos.x - padding.left, 520 y: self.pos.y - padding.top, 521 }, 522 dim: Dim { 523 w: self.dim.w + padding.left + padding.right, 524 h: self.dim.h + padding.top + padding.bottom, 525 }, 526 } 527 } 528 } 529 530 impl Sub<Padding> for Region { 531 type Output = Self; 532 533 fn sub( 534 self, 535 padding: Padding, 536 ) -> Self::Output { 537 Self::Output { 538 pos: Pos { 539 x: self.pos.x + padding.left, 540 y: self.pos.y + padding.top, 541 }, 542 dim: Dim { 543 w: self.dim.w - padding.left - padding.right, 544 h: self.dim.h - padding.top - padding.bottom, 545 }, 546 } 547 } 548 } 549 550 impl AddAssign<Padding> for Region { 551 fn add_assign( 552 &mut self, 553 padding: Padding, 554 ) { 555 *self = Self { 556 pos: Pos { 557 x: self.pos.x - padding.left, 558 y: self.pos.y - padding.top, 559 }, 560 dim: Dim { 561 w: self.dim.w + padding.left + padding.right, 562 h: self.dim.h + padding.top + padding.bottom, 563 }, 564 }; 565 } 566 } 567 568 impl SubAssign<Padding> for Region { 569 fn sub_assign( 570 &mut self, 571 padding: Padding, 572 ) { 573 *self = Self { 574 pos: Pos { 575 x: self.pos.x + padding.left, 576 y: self.pos.y + padding.top, 577 }, 578 dim: Dim { 579 w: self.dim.w - padding.left - padding.right, 580 h: self.dim.h - padding.top - padding.bottom, 581 }, 582 }; 583 } 584 } 585 586 impl Add<Padding> for Dim { 587 type Output = Self; 588 589 fn add( 590 self, 591 padding: Padding, 592 ) -> Self::Output { 593 Self::Output { 594 w: self.w + padding.left + padding.right, 595 h: self.h + padding.top + padding.bottom, 596 } 597 } 598 } 599 600 impl Sub<Padding> for Dim { 601 type Output = Self; 602 603 fn sub( 604 self, 605 padding: Padding, 606 ) -> Self::Output { 607 Self::Output { 608 w: self.w - padding.left - padding.right, 609 h: self.h - padding.top - padding.bottom, 610 } 611 } 612 } 613 614 impl AddAssign<Padding> for Dim { 615 fn add_assign( 616 &mut self, 617 padding: Padding, 618 ) { 619 *self = Self { 620 w: self.w + padding.left + padding.right, 621 h: self.h + padding.top + padding.bottom, 622 }; 623 } 624 } 625 626 impl SubAssign<Padding> for Dim { 627 fn sub_assign( 628 &mut self, 629 padding: Padding, 630 ) { 631 *self = Self { 632 w: self.w - padding.left - padding.right, 633 h: self.h - padding.top - padding.bottom, 634 }; 635 } 636 } 637 638 #[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] 639 pub struct Distance { 640 pub dx: i32, 641 pub dy: i32, 642 } 643 644 impl Distance { 645 pub fn values(&self) -> (i32, i32) { 646 (self.dx, self.dy) 647 } 648 649 pub fn pythagorean(&self) -> i32 { 650 let dx = self.dx.pow(2) as f64; 651 let dy = self.dy.pow(2) as f64; 652 653 (dx + dy).sqrt().round() as i32 654 } 655 } 656 657 impl Add<Distance> for Pos { 658 type Output = Self; 659 660 fn add( 661 self, 662 dist: Distance, 663 ) -> Self::Output { 664 Self::Output { 665 x: self.x + dist.dx, 666 y: self.y + dist.dy, 667 } 668 } 669 } 670 671 impl AddAssign<Distance> for Pos { 672 fn add_assign( 673 &mut self, 674 dist: Distance, 675 ) { 676 *self = Self { 677 x: self.x + dist.dx, 678 y: self.y + dist.dy, 679 }; 680 } 681 } 682 683 impl Sub<Distance> for Pos { 684 type Output = Self; 685 686 fn sub( 687 self, 688 dist: Distance, 689 ) -> Self::Output { 690 Self::Output { 691 x: self.x - dist.dx, 692 y: self.y - dist.dy, 693 } 694 } 695 } 696 697 impl SubAssign<Distance> for Pos { 698 fn sub_assign( 699 &mut self, 700 dist: Distance, 701 ) { 702 *self = Self { 703 x: self.x - dist.dx, 704 y: self.y - dist.dy, 705 }; 706 } 707 } 708 709 impl Add<Distance> for Dim { 710 type Output = Self; 711 712 fn add( 713 self, 714 dist: Distance, 715 ) -> Self::Output { 716 Self::Output { 717 w: (self.w + dist.dx).abs(), 718 h: (self.h + dist.dy).abs(), 719 } 720 } 721 } 722 723 impl AddAssign<Distance> for Dim { 724 fn add_assign( 725 &mut self, 726 dist: Distance, 727 ) { 728 *self = Self { 729 w: (self.w + dist.dx).abs(), 730 h: (self.h + dist.dy).abs(), 731 }; 732 } 733 } 734 735 impl Sub<Distance> for Dim { 736 type Output = Self; 737 738 fn sub( 739 self, 740 dist: Distance, 741 ) -> Self::Output { 742 Self::Output { 743 w: (self.w - dist.dx).abs(), 744 h: (self.h - dist.dy).abs(), 745 } 746 } 747 } 748 749 impl SubAssign<Distance> for Dim { 750 fn sub_assign( 751 &mut self, 752 dist: Distance, 753 ) { 754 *self = Self { 755 w: (self.w - dist.dx).abs(), 756 h: (self.h - dist.dy).abs(), 757 }; 758 } 759 } 760 761 #[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] 762 pub struct Ratio { 763 pub numerator: i32, 764 pub denominator: i32, 765 } 766 767 impl Ratio { 768 pub fn new( 769 numerator: i32, 770 denominator: i32, 771 ) -> Self { 772 Self { 773 numerator, 774 denominator, 775 } 776 } 777 } 778 779 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 780 pub struct Strut { 781 pub window: Window, 782 pub width: u32, 783 } 784 785 impl Strut { 786 pub fn new( 787 window: Window, 788 width: u32, 789 ) -> Self { 790 Self { 791 window, 792 width, 793 } 794 } 795 } 796 797 impl PartialOrd for Strut { 798 fn partial_cmp( 799 &self, 800 other: &Self, 801 ) -> Option<std::cmp::Ordering> { 802 Some(self.cmp(other)) 803 } 804 } 805 806 impl Ord for Strut { 807 fn cmp( 808 &self, 809 other: &Self, 810 ) -> std::cmp::Ordering { 811 other.width.cmp(&self.width) 812 } 813 }