@@ -11,9 +11,9 @@ use super::UnknownUnit;
11
11
use length:: Length ;
12
12
use scale_factor:: ScaleFactor ;
13
13
use num:: * ;
14
- use point:: TypedPoint2D ;
14
+ use point:: { TypedPoint2D , point2 } ;
15
15
use vector:: TypedVector2D ;
16
- use size:: TypedSize2D ;
16
+ use size:: { TypedSize2D , size2 } ;
17
17
18
18
use heapsize:: HeapSizeOf ;
19
19
use num_traits:: NumCast ;
@@ -22,8 +22,9 @@ use std::cmp::PartialOrd;
22
22
use std:: fmt;
23
23
use std:: hash:: { Hash , Hasher } ;
24
24
use std:: ops:: { Add , Sub , Mul , Div } ;
25
+ use { min, max} ;
25
26
26
- /// A 2d Rectangle optionally tagged with a unit.
27
+ /// A 2d Rectangle represented using a point and a size, optionally tagged with a unit.
27
28
#[ repr( C ) ]
28
29
pub struct TypedRect < T , U = UnknownUnit > {
29
30
pub origin : TypedPoint2D < T , U > ,
@@ -165,6 +166,11 @@ where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output=T> + Sub<T
165
166
lower_right_y - upper_left. y ) ) )
166
167
}
167
168
169
+ #[ inline]
170
+ pub fn to_box ( & self ) -> TypedBox2D < T , U > {
171
+ TypedBox2D :: new ( self . origin , point2 ( self . max_x ( ) , self . max_y ( ) ) )
172
+ }
173
+
168
174
/// Returns the same rectangle, translated by a vector.
169
175
#[ inline]
170
176
#[ must_use]
@@ -317,15 +323,6 @@ impl<T: Copy + PartialEq + Zero, U> TypedRect<T, U> {
317
323
}
318
324
}
319
325
320
-
321
- pub fn min < T : Clone + PartialOrd > ( x : T , y : T ) -> T {
322
- if x <= y { x } else { y }
323
- }
324
-
325
- pub fn max < T : Clone + PartialOrd > ( x : T , y : T ) -> T {
326
- if x >= y { x } else { y }
327
- }
328
-
329
326
impl < T : Copy + Mul < T , Output =T > , U > Mul < T > for TypedRect < T , U > {
330
327
type Output = Self ;
331
328
#[ inline]
@@ -455,9 +452,165 @@ impl<T: NumCast + Copy, Unit> TypedRect<T, Unit> {
455
452
}
456
453
}
457
454
455
+ /// A 2d Rectangle represented using two points, optionally tagged with a unit.
456
+ #[ repr( C ) ]
457
+ pub struct TypedBox2D < T , Unit = UnknownUnit > {
458
+ pub min : TypedPoint2D < T , Unit > ,
459
+ pub max : TypedPoint2D < T , Unit > ,
460
+ }
461
+
462
+ /// The default box type with no unit.
463
+ pub type Box2D < T > = TypedBox2D < T , UnknownUnit > ;
464
+
465
+ impl < T , U > TypedBox2D < T , U > {
466
+ pub fn new ( min : TypedPoint2D < T , U > , max : TypedPoint2D < T , U > ) -> Self {
467
+ TypedBox2D { min, max }
468
+ }
469
+ }
470
+
471
+ impl < T , U > TypedBox2D < T , U >
472
+ where T : Copy + Clone + Zero + One + PartialOrd + PartialEq + Add < Output =T > + Sub < Output =T > + Mul < Output =T > {
473
+ /// Returns a rectangle if this box is positive.
474
+ #[ inline]
475
+ pub fn to_rect ( & self ) -> Option < TypedRect < T , U > > {
476
+ let size = self . size ( ) ;
477
+
478
+ let _0 = T :: zero ( ) ;
479
+ if size. width <= _0 || size. height <= _0 {
480
+ return None
481
+ }
482
+
483
+ Some ( TypedRect { origin : self . min , size } )
484
+ }
485
+
486
+ #[ inline]
487
+ pub fn size ( & self ) -> TypedSize2D < T , U > {
488
+ ( self . max - self . min ) . to_size ( )
489
+ }
490
+
491
+ /// Returns true if both width and height are superior or equal to zero.
492
+ #[ inline]
493
+ pub fn is_positive ( & self ) -> bool {
494
+ let size = self . size ( ) ;
495
+ let _0 = T :: zero ( ) ;
496
+
497
+ size. width >= _0 && size. height >= _0
498
+ }
499
+
500
+ /// Returns true if either width or height are inferior to zero.
501
+ #[ inline]
502
+ pub fn is_negative ( & self ) -> bool {
503
+ let size = self . size ( ) ;
504
+ let _0 = T :: zero ( ) ;
505
+
506
+ size. width < _0 || size. height < _0
507
+ }
508
+
509
+ /// Returns true if either width or height are inferior or equal to zero.
510
+ #[ inline]
511
+ pub fn is_empty_or_negative ( & self ) -> bool {
512
+ let size = self . size ( ) ;
513
+ let _0 = T :: zero ( ) ;
514
+
515
+ size. width <= _0 || size. height <= _0
516
+ }
517
+
518
+ #[ inline]
519
+ pub fn intersects ( & self , other : & Self ) -> bool {
520
+ self . intersection ( other) . is_positive ( )
521
+ }
522
+
523
+ #[ inline]
524
+ pub fn intersection ( & self , other : & Self ) -> Self {
525
+ TypedBox2D {
526
+ min : point2 ( max ( self . min . x , other. min . x ) , max ( self . min . y , other. min . y ) ) ,
527
+ max : point2 ( min ( self . max . x , other. max . x ) , min ( self . max . y , other. max . y ) ) ,
528
+ }
529
+ }
530
+
531
+ #[ inline]
532
+ pub fn union ( & self , other : & Self ) -> Self {
533
+ if other. is_empty_or_negative ( ) {
534
+ return * self ;
535
+ }
536
+
537
+ if self . is_empty_or_negative ( ) {
538
+ return * other;
539
+ }
540
+
541
+ TypedBox2D {
542
+ min : point2 ( min ( self . min . x , other. min . x ) , min ( self . min . y , other. min . y ) ) ,
543
+ max : point2 ( max ( self . max . x , other. max . x ) , max ( self . max . y , other. max . y ) ) ,
544
+ }
545
+ }
546
+
547
+ /// Returns true if this box contains the point. Points are considered
548
+ /// in the box if they are on the left or top edge, but outside if they
549
+ /// are on the right or bottom edge.
550
+ #[ inline]
551
+ pub fn contains ( & self , point : & TypedPoint2D < T , U > ) -> bool {
552
+ self . min . x <= point. x && point. x < self . max . x &&
553
+ self . min . y <= point. y && point. y < self . max . y
554
+ }
555
+
556
+ /// Linearly interpolate between this box and another box.
557
+ ///
558
+ /// The box should be positive.
559
+ /// `t` is expected to be between zero and one.
560
+ #[ inline]
561
+ pub fn lerp ( & self , other : Self , t : T ) -> Self {
562
+ debug_assert ! ( !self . is_positive( ) ) ;
563
+ debug_assert ! ( !other. is_positive( ) ) ;
564
+ Self :: new (
565
+ self . min . lerp ( other. min , t) ,
566
+ self . max . lerp ( other. max , t) ,
567
+ )
568
+ }
569
+
570
+ /// Return the same box if positive, or an empty box if negative.
571
+ ///
572
+ /// This is useful after computing intersections since the latter can produce negative boxes.
573
+ #[ inline]
574
+ #[ must_use]
575
+ pub fn ensure_positive ( & self ) -> Self {
576
+ TypedBox2D { min : self . min , max : self . max . max ( self . min ) }
577
+ }
578
+ }
579
+
580
+ impl < T : Copy , U > Copy for TypedBox2D < T , U > { }
581
+
582
+ impl < T : Copy , U > Clone for TypedBox2D < T , U > {
583
+ fn clone ( & self ) -> Self { * self }
584
+ }
585
+
586
+ impl < T : PartialEq , U > PartialEq < TypedBox2D < T , U > > for TypedBox2D < T , U > {
587
+ fn eq ( & self , other : & Self ) -> bool {
588
+ self . min . eq ( & other. min ) && self . max . eq ( & other. max )
589
+ }
590
+ }
591
+
592
+ impl < T : Eq , U > Eq for TypedBox2D < T , U > { }
593
+
594
+ impl < T : fmt:: Debug , U > fmt:: Debug for TypedBox2D < T , U > {
595
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
596
+ write ! ( f, "Box2D({:?} to {:?})" , self . min, self . max)
597
+ }
598
+ }
599
+
600
+ impl < T : fmt:: Display , U > fmt:: Display for TypedBox2D < T , U > {
601
+ fn fmt ( & self , formatter : & mut fmt:: Formatter ) -> fmt:: Result {
602
+ write ! ( formatter, "Box2D({} to {})" , self . min, self . max)
603
+ }
604
+ }
605
+
458
606
/// Shorthand for `TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h))`.
459
607
pub fn rect < T : Copy , U > ( x : T , y : T , w : T , h : T ) -> TypedRect < T , U > {
460
- TypedRect :: new ( TypedPoint2D :: new ( x, y) , TypedSize2D :: new ( w, h) )
608
+ TypedRect :: new ( point2 ( x, y) , size2 ( w, h) )
609
+ }
610
+
611
+ /// Shorthand for `TypedBox2D::new(TypedPoint2D::new(min_x, min_y), TypedBox2D::new(max_x, max_y))`.
612
+ pub fn box2 < T : Copy , U > ( min_x : T , min_y : T , max_x : T , max_y : T ) -> TypedBox2D < T , U > {
613
+ TypedBox2D :: new ( point2 ( min_x, min_y) , point2 ( max_x, max_y) )
461
614
}
462
615
463
616
#[ cfg( test) ]
@@ -517,7 +670,7 @@ mod tests {
517
670
}
518
671
519
672
#[ test]
520
- fn test_union ( ) {
673
+ fn test_rect_union ( ) {
521
674
let p = Rect :: new ( Point2D :: new ( 0 , 0 ) , Size2D :: new ( 50 , 40 ) ) ;
522
675
let q = Rect :: new ( Point2D :: new ( 20 , 20 ) , Size2D :: new ( 5 , 5 ) ) ;
523
676
let r = Rect :: new ( Point2D :: new ( -15 , -30 ) , Size2D :: new ( 200 , 15 ) ) ;
@@ -538,7 +691,27 @@ mod tests {
538
691
}
539
692
540
693
#[ test]
541
- fn test_intersection ( ) {
694
+ fn test_box_union ( ) {
695
+ let p = Rect :: new ( point2 ( 0 , 0 ) , size2 ( 50 , 40 ) ) . to_box ( ) ;
696
+ let q = Rect :: new ( point2 ( 20 , 20 ) , size2 ( 5 , 5 ) ) . to_box ( ) ;
697
+ let r = Rect :: new ( point2 ( -15 , -30 ) , size2 ( 200 , 15 ) ) . to_box ( ) ;
698
+ let s = Rect :: new ( point2 ( 20 , -15 ) , size2 ( 250 , 200 ) ) . to_box ( ) ;
699
+
700
+ let pq = p. union ( & q) ;
701
+ assert ! ( pq. min == point2( 0 , 0 ) ) ;
702
+ assert ! ( pq. size( ) == size2( 50 , 40 ) ) ;
703
+
704
+ let pr = p. union ( & r) ;
705
+ assert ! ( pr. min == point2( -15 , -30 ) ) ;
706
+ assert ! ( pr. size( ) == size2( 200 , 70 ) ) ;
707
+
708
+ let ps = p. union ( & s) ;
709
+ assert ! ( ps. min == point2( 0 , -15 ) ) ;
710
+ assert ! ( ps. size( ) == size2( 270 , 200 ) ) ;
711
+ }
712
+
713
+ #[ test]
714
+ fn test_rect_intersection ( ) {
542
715
let p = Rect :: new ( Point2D :: new ( 0 , 0 ) , Size2D :: new ( 10 , 20 ) ) ;
543
716
let q = Rect :: new ( Point2D :: new ( 5 , 15 ) , Size2D :: new ( 10 , 10 ) ) ;
544
717
let r = Rect :: new ( Point2D :: new ( -5 , -5 ) , Size2D :: new ( 8 , 8 ) ) ;
@@ -559,6 +732,26 @@ mod tests {
559
732
assert ! ( qr. is_none( ) ) ;
560
733
}
561
734
735
+ #[ test]
736
+ fn test_box_intersection ( ) {
737
+ let p = Rect :: new ( point2 ( 0 , 0 ) , size2 ( 10 , 20 ) ) . to_box ( ) ;
738
+ let q = Rect :: new ( point2 ( 5 , 15 ) , size2 ( 10 , 10 ) ) . to_box ( ) ;
739
+ let r = Rect :: new ( point2 ( -5 , -5 ) , size2 ( 8 , 8 ) ) . to_box ( ) ;
740
+
741
+ let pq = p. intersection ( & q) ;
742
+ assert ! ( pq. is_positive( ) ) ;
743
+ assert ! ( pq. min == point2( 5 , 15 ) ) ;
744
+ assert ! ( pq. size( ) == size2( 5 , 5 ) ) ;
745
+
746
+ let pr = p. intersection ( & r) ;
747
+ assert ! ( pr. is_positive( ) ) ;
748
+ assert ! ( pr. min == point2( 0 , 0 ) ) ;
749
+ assert ! ( pr. size( ) == size2( 3 , 3 ) ) ;
750
+
751
+ let qr = q. intersection ( & r) ;
752
+ assert ! ( qr. is_empty_or_negative( ) ) ;
753
+ }
754
+
562
755
#[ test]
563
756
fn test_contains ( ) {
564
757
let r = Rect :: new ( Point2D :: new ( -20 , 15 ) , Size2D :: new ( 100 , 200 ) ) ;
0 commit comments