@@ -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,153 @@ 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
+ if self . is_positive ( ) {
477
+ return None
478
+ }
479
+
480
+ Some ( TypedRect { origin : self . min , size : self . size ( ) } )
481
+ }
482
+
483
+ #[ inline]
484
+ pub fn size ( & self ) -> TypedSize2D < T , U > {
485
+ ( self . max - self . min ) . to_size ( )
486
+ }
487
+
488
+ /// Returns true if both width and height are superior or equal to zero.
489
+ #[ inline]
490
+ pub fn is_positive ( & self ) -> bool {
491
+ self . max . x >= self . min . x && self . max . y >= self . min . y
492
+ }
493
+
494
+ /// Returns true if either width or height are inferior to zero.
495
+ #[ inline]
496
+ pub fn is_negative ( & self ) -> bool {
497
+ self . max . x < self . min . x || self . max . y < self . min . y
498
+ }
499
+
500
+ /// Returns true if either width or height are inferior or equal to zero.
501
+ #[ inline]
502
+ pub fn is_empty_or_negative ( & self ) -> bool {
503
+ self . max . x <= self . min . x || self . max . y <= self . min . y
504
+ }
505
+
506
+ #[ inline]
507
+ pub fn intersects ( & self , other : & Self ) -> bool {
508
+ self . intersection ( other) . is_positive ( )
509
+ }
510
+
511
+ #[ inline]
512
+ pub fn intersection ( & self , other : & Self ) -> Self {
513
+ TypedBox2D {
514
+ min : point2 ( max ( self . min . x , other. min . x ) , max ( self . min . y , other. min . y ) ) ,
515
+ max : point2 ( min ( self . max . x , other. max . x ) , min ( self . max . y , other. max . y ) ) ,
516
+ }
517
+ }
518
+
519
+ #[ inline]
520
+ pub fn union ( & self , other : & Self ) -> Self {
521
+ if other. is_empty_or_negative ( ) {
522
+ return * self ;
523
+ }
524
+
525
+ if self . is_empty_or_negative ( ) {
526
+ return * other;
527
+ }
528
+
529
+ TypedBox2D {
530
+ min : point2 ( min ( self . min . x , other. min . x ) , min ( self . min . y , other. min . y ) ) ,
531
+ max : point2 ( max ( self . max . x , other. max . x ) , max ( self . max . y , other. max . y ) ) ,
532
+ }
533
+ }
534
+
535
+ /// Returns true if this box contains the point. Points are considered
536
+ /// in the box if they are on the left or top edge, but outside if they
537
+ /// are on the right or bottom edge.
538
+ #[ inline]
539
+ pub fn contains ( & self , point : & TypedPoint2D < T , U > ) -> bool {
540
+ self . min . x <= point. x && point. x < self . max . x &&
541
+ self . min . y <= point. y && point. y < self . max . y
542
+ }
543
+
544
+ /// Linearly interpolate between this box and another box.
545
+ ///
546
+ /// The box should be positive.
547
+ /// `t` is expected to be between zero and one.
548
+ #[ inline]
549
+ pub fn lerp ( & self , other : Self , t : T ) -> Self {
550
+ debug_assert ! ( self . is_positive( ) ) ;
551
+ debug_assert ! ( other. is_positive( ) ) ;
552
+ Self :: new (
553
+ self . min . lerp ( other. min , t) ,
554
+ self . max . lerp ( other. max , t) ,
555
+ )
556
+ }
557
+
558
+ /// Return the same box if positive, or an empty box if negative.
559
+ ///
560
+ /// This is useful after computing intersections since the latter can produce negative boxes.
561
+ #[ inline]
562
+ #[ must_use]
563
+ pub fn ensure_positive ( & self ) -> Self {
564
+ TypedBox2D { min : self . min , max : self . max . max ( self . min ) }
565
+ }
566
+ }
567
+
568
+ impl < T : Copy , U > Copy for TypedBox2D < T , U > { }
569
+
570
+ impl < T : Copy , U > Clone for TypedBox2D < T , U > {
571
+ fn clone ( & self ) -> Self { * self }
572
+ }
573
+
574
+ impl < T : PartialEq , U > PartialEq < TypedBox2D < T , U > > for TypedBox2D < T , U > {
575
+ fn eq ( & self , other : & Self ) -> bool {
576
+ self . min . eq ( & other. min ) && self . max . eq ( & other. max )
577
+ }
578
+ }
579
+
580
+ impl < T : Eq , U > Eq for TypedBox2D < T , U > { }
581
+
582
+ impl < T : fmt:: Debug , U > fmt:: Debug for TypedBox2D < T , U > {
583
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
584
+ write ! ( f, "Box2D({:?} to {:?})" , self . min, self . max)
585
+ }
586
+ }
587
+
588
+ impl < T : fmt:: Display , U > fmt:: Display for TypedBox2D < T , U > {
589
+ fn fmt ( & self , formatter : & mut fmt:: Formatter ) -> fmt:: Result {
590
+ write ! ( formatter, "Box2D({} to {})" , self . min, self . max)
591
+ }
592
+ }
593
+
458
594
/// Shorthand for `TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h))`.
459
595
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) )
596
+ TypedRect :: new ( point2 ( x, y) , size2 ( w, h) )
597
+ }
598
+
599
+ /// Shorthand for `TypedBox2D::new(TypedPoint2D::new(min_x, min_y), TypedBox2D::new(max_x, max_y))`.
600
+ pub fn box2 < T : Copy , U > ( min_x : T , min_y : T , max_x : T , max_y : T ) -> TypedBox2D < T , U > {
601
+ TypedBox2D :: new ( point2 ( min_x, min_y) , point2 ( max_x, max_y) )
461
602
}
462
603
463
604
#[ cfg( test) ]
@@ -517,7 +658,7 @@ mod tests {
517
658
}
518
659
519
660
#[ test]
520
- fn test_union ( ) {
661
+ fn test_rect_union ( ) {
521
662
let p = Rect :: new ( Point2D :: new ( 0 , 0 ) , Size2D :: new ( 50 , 40 ) ) ;
522
663
let q = Rect :: new ( Point2D :: new ( 20 , 20 ) , Size2D :: new ( 5 , 5 ) ) ;
523
664
let r = Rect :: new ( Point2D :: new ( -15 , -30 ) , Size2D :: new ( 200 , 15 ) ) ;
@@ -538,7 +679,27 @@ mod tests {
538
679
}
539
680
540
681
#[ test]
541
- fn test_intersection ( ) {
682
+ fn test_box_union ( ) {
683
+ let p = Rect :: new ( point2 ( 0 , 0 ) , size2 ( 50 , 40 ) ) . to_box ( ) ;
684
+ let q = Rect :: new ( point2 ( 20 , 20 ) , size2 ( 5 , 5 ) ) . to_box ( ) ;
685
+ let r = Rect :: new ( point2 ( -15 , -30 ) , size2 ( 200 , 15 ) ) . to_box ( ) ;
686
+ let s = Rect :: new ( point2 ( 20 , -15 ) , size2 ( 250 , 200 ) ) . to_box ( ) ;
687
+
688
+ let pq = p. union ( & q) ;
689
+ assert_eq ! ( pq. min, point2( 0 , 0 ) ) ;
690
+ assert_eq ! ( pq. size( ) , size2( 50 , 40 ) ) ;
691
+
692
+ let pr = p. union ( & r) ;
693
+ assert_eq ! ( pr. min, point2( -15 , -30 ) ) ;
694
+ assert_eq ! ( pr. size( ) , size2( 200 , 70 ) ) ;
695
+
696
+ let ps = p. union ( & s) ;
697
+ assert_eq ! ( ps. min, point2( 0 , -15 ) ) ;
698
+ assert_eq ! ( ps. size( ) , size2( 270 , 200 ) ) ;
699
+ }
700
+
701
+ #[ test]
702
+ fn test_rect_intersection ( ) {
542
703
let p = Rect :: new ( Point2D :: new ( 0 , 0 ) , Size2D :: new ( 10 , 20 ) ) ;
543
704
let q = Rect :: new ( Point2D :: new ( 5 , 15 ) , Size2D :: new ( 10 , 10 ) ) ;
544
705
let r = Rect :: new ( Point2D :: new ( -5 , -5 ) , Size2D :: new ( 8 , 8 ) ) ;
@@ -559,6 +720,26 @@ mod tests {
559
720
assert ! ( qr. is_none( ) ) ;
560
721
}
561
722
723
+ #[ test]
724
+ fn test_box_intersection ( ) {
725
+ let p = Rect :: new ( point2 ( 0 , 0 ) , size2 ( 10 , 20 ) ) . to_box ( ) ;
726
+ let q = Rect :: new ( point2 ( 5 , 15 ) , size2 ( 10 , 10 ) ) . to_box ( ) ;
727
+ let r = Rect :: new ( point2 ( -5 , -5 ) , size2 ( 8 , 8 ) ) . to_box ( ) ;
728
+
729
+ let pq = p. intersection ( & q) ;
730
+ assert ! ( pq. is_positive( ) ) ;
731
+ assert_eq ! ( pq. min, point2( 5 , 15 ) ) ;
732
+ assert_eq ! ( pq. size( ) , size2( 5 , 5 ) ) ;
733
+
734
+ let pr = p. intersection ( & r) ;
735
+ assert ! ( pr. is_positive( ) ) ;
736
+ assert_eq ! ( pr. min, point2( 0 , 0 ) ) ;
737
+ assert_eq ! ( pr. size( ) , size2( 3 , 3 ) ) ;
738
+
739
+ let qr = q. intersection ( & r) ;
740
+ assert ! ( qr. is_empty_or_negative( ) ) ;
741
+ }
742
+
562
743
#[ test]
563
744
fn test_contains ( ) {
564
745
let r = Rect :: new ( Point2D :: new ( -20 , 15 ) , Size2D :: new ( 100 , 200 ) ) ;
@@ -697,4 +878,14 @@ mod tests {
697
878
x += 0.1
698
879
}
699
880
}
881
+
882
+ #[ test]
883
+ fn test_box_negative ( ) {
884
+ assert ! ( Box2D :: new( point2( 0.0 , 0.0 ) , point2( 0.0 , 0.0 ) ) . is_positive( ) ) ;
885
+ assert ! ( Box2D :: new( point2( 0.0 , 0.0 ) , point2( 0.0 , 0.0 ) ) . is_empty_or_negative( ) ) ;
886
+ assert ! ( Box2D :: new( point2( -1.0 , -2.0 ) , point2( 0.0 , 0.0 ) ) . is_positive( ) ) ;
887
+ assert ! ( Box2D :: new( point2( 1.0 , 2.0 ) , point2( 0.0 , 1.0 ) ) . is_negative( ) ) ;
888
+ assert ! ( Box2D :: new( point2( 1.0 , 2.0 ) , point2( 0.0 , 2.0 ) ) . is_negative( ) ) ;
889
+ assert ! ( Box2D :: new( point2( 1.0 , 2.0 ) , point2( 1.0 , 0.0 ) ) . is_negative( ) ) ;
890
+ }
700
891
}
0 commit comments