1use num_traits::Zero;
18
19use crate::dimen::{Vec2, Vector2};
20use crate::numeric::{max, min, PrimitiveZero};
21
22pub type URect = Rectangle<u32>;
27
28pub type IRect = Rectangle<i32>;
33
34pub type Rect = Rectangle<f32>;
39
40#[derive(Debug, PartialEq, Eq, Clone)]
43#[repr(C)]
44pub struct Rectangle<T = f32>
45{
46    top_left: Vector2<T>,
47    bottom_right: Vector2<T>
48}
49
50impl<T> AsRef<Rectangle<T>> for Rectangle<T>
51{
52    fn as_ref(&self) -> &Self
53    {
54        self
55    }
56}
57
58impl<T> Rectangle<T>
59{
60    #[inline]
63    pub const fn new(top_left: Vector2<T>, bottom_right: Vector2<T>) -> Self
64    {
65        Rectangle {
66            top_left,
67            bottom_right
68        }
69    }
70
71    #[inline]
74    pub fn from_tuples(top_left: (T, T), bottom_right: (T, T)) -> Self
75    {
76        Rectangle {
77            top_left: Vector2::new(top_left.0, top_left.1),
78            bottom_right: Vector2::new(bottom_right.0, bottom_right.1)
79        }
80    }
81
82    #[inline]
84    pub const fn top_left(&self) -> &Vector2<T>
85    {
86        &self.top_left
87    }
88
89    #[inline]
91    pub const fn bottom_right(&self) -> &Vector2<T>
92    {
93        &self.bottom_right
94    }
95}
96
97impl<T: Copy> Rectangle<T>
98{
99    #[inline]
102    pub fn rounded(&self, radius: T) -> RoundedRectangle<T>
103    {
104        RoundedRectangle::from_rectangle(self.clone(), radius)
105    }
106    #[inline]
108    pub fn top_right(&self) -> Vector2<T>
109    {
110        Vector2::new(self.bottom_right.x, self.top_left.y)
111    }
112
113    #[inline]
115    pub fn bottom_left(&self) -> Vector2<T>
116    {
117        Vector2::new(self.top_left.x, self.bottom_right.y)
118    }
119
120    #[inline]
122    pub fn left(&self) -> T
123    {
124        self.top_left.x
125    }
126
127    #[inline]
129    pub fn right(&self) -> T
130    {
131        self.bottom_right.x
132    }
133
134    #[inline]
136    pub fn top(&self) -> T
137    {
138        self.top_left.y
139    }
140
141    #[inline]
143    pub fn bottom(&self) -> T
144    {
145        self.bottom_right.y
146    }
147}
148
149impl<T: Copy + std::ops::Neg<Output = T> + std::ops::Add<Output = T>> RoundedRectangle<T>
150{
151    pub fn inner(&self) -> Rectangle<T>
154    {
155        Rectangle::new(
156            *self.top_left() + Vector2::new(self.radius, self.radius),
157            self.bottom_right() + Vector2::new(-self.radius, -self.radius)
158        )
159    }
160}
161
162impl<T: std::ops::Sub<Output = T> + Copy> Rectangle<T>
163{
164    #[inline]
166    pub fn width(&self) -> T
167    {
168        self.bottom_right.x - self.top_left.x
169    }
170
171    #[inline]
173    pub fn height(&self) -> T
174    {
175        self.bottom_right.y - self.top_left.y
176    }
177
178    #[inline]
180    pub fn size(&self) -> Vector2<T>
181    {
182        Vector2::new(self.width(), self.height())
183    }
184}
185
186impl<T: std::cmp::PartialOrd<T> + Copy> Rectangle<T>
187{
188    #[inline]
192    #[must_use]
193    pub fn contains(&self, point: Vector2<T>) -> bool
194    {
195        point.x >= self.top_left.x
196            && point.y >= self.top_left.y
197            && point.x < self.bottom_right.x
198            && point.y < self.bottom_right.y
199    }
200}
201
202impl<T: std::cmp::PartialOrd + Copy> Rectangle<T>
203{
204    #[inline]
210    #[must_use]
211    pub fn intersect(&self, other: &Self) -> Option<Self>
212    {
213        let result = Self {
214            top_left: Vector2::new(
215                max(self.top_left.x, other.top_left.x),
216                max(self.top_left.y, other.top_left.y)
217            ),
218            bottom_right: Vector2::new(
219                min(self.bottom_right.x, other.bottom_right.x),
220                min(self.bottom_right.y, other.bottom_right.y)
221            )
222        };
223
224        if result.is_positive_area() {
225            Some(result)
226        } else {
227            None
228        }
229    }
230}
231
232impl<T: PrimitiveZero> Rectangle<T>
233{
234    pub const ZERO: Rectangle<T> = Rectangle::new(Vector2::ZERO, Vector2::ZERO);
237}
238
239impl<T: PartialEq> Rectangle<T>
240{
241    #[inline]
243    pub fn is_zero_area(&self) -> bool
244    {
245        self.top_left.x == self.bottom_right.x || self.top_left.y == self.bottom_right.y
246    }
247}
248
249impl<T: std::cmp::PartialOrd> Rectangle<T>
250{
251    #[inline]
253    pub fn is_positive_area(&self) -> bool
254    {
255        self.top_left.x < self.bottom_right.x && self.top_left.y < self.bottom_right.y
256    }
257}
258
259impl<T: Copy> Rectangle<T>
260where
261    Vector2<T>: std::ops::Add<Output = Vector2<T>>
262{
263    #[inline]
267    pub fn with_offset(&self, offset: impl Into<Vector2<T>>) -> Self
268    {
269        let offset = offset.into();
270        Rectangle::new(self.top_left + offset, self.bottom_right + offset)
271    }
272}
273
274impl<T: Copy> Rectangle<T>
275where
276    Vector2<T>: std::ops::Sub<Output = Vector2<T>>
277{
278    #[inline]
282    pub fn with_negative_offset(&self, offset: impl Into<Vector2<T>>) -> Self
283    {
284        let offset = offset.into();
285        Rectangle::new(self.top_left - offset, self.bottom_right - offset)
286    }
287}
288
289impl<T> From<rusttype::Rect<T>> for Rectangle<T>
290{
291    fn from(rect: rusttype::Rect<T>) -> Self
292    {
293        Rectangle::new(Vector2::from(rect.min), Vector2::from(rect.max))
294    }
295}
296
297impl<T: num_traits::AsPrimitive<f32>> Rectangle<T>
298{
299    #[inline]
302    #[must_use]
303    pub fn into_f32(self) -> Rectangle<f32>
304    {
305        Rectangle::new(self.top_left.into_f32(), self.bottom_right.into_f32())
306    }
307}
308
309impl<T: num_traits::AsPrimitive<f32> + Copy> Rectangle<T>
310{
311    #[inline]
314    #[must_use]
315    pub fn as_f32(&self) -> Rectangle<f32>
316    {
317        Rectangle::new(self.top_left.into_f32(), self.bottom_right.into_f32())
318    }
319}
320
321#[derive(Debug, Clone)]
323pub struct Polygon
324{
325    pub(crate) triangles: Vec<[Vec2; 3]>
326}
327
328impl Polygon
329{
330    pub fn new<Point: Into<Vec2> + Copy>(vertices: &[Point]) -> Self
334    {
335        let mut flattened = Vec::with_capacity(vertices.len() * 2);
340
341        for vertex in vertices {
342            let vertex: Vec2 = (*vertex).into();
343
344            flattened.push(vertex.x);
345            flattened.push(vertex.y);
346        }
347
348        let mut triangulation = earcutr::earcut(&flattened, &Vec::new(), 2);
349        let mut triangles = Vec::with_capacity(triangulation.len() / 3);
350
351        while !triangulation.is_empty() {
352            triangles.push([
353                vertices[triangulation.pop().unwrap()].into(),
354                vertices[triangulation.pop().unwrap()].into(),
355                vertices[triangulation.pop().unwrap()].into()
356            ])
357        }
358
359        Polygon { triangles }
360    }
361}
362
363#[cfg(test)]
364mod test
365{
366    use crate::shape::URect;
367
368    #[test]
369    pub fn test_intersect_1()
370    {
371        let r1 = URect::from_tuples((100, 100), (200, 200));
372        let r2 = URect::from_tuples((100, 300), (200, 400));
373        let r3 = URect::from_tuples((125, 50), (175, 500));
374
375        assert_eq!(None, r1.intersect(&r2));
376
377        assert_eq!(
378            Some(URect::from_tuples((125, 100), (175, 200))),
379            r1.intersect(&r3)
380        );
381
382        assert_eq!(
383            Some(URect::from_tuples((125, 300), (175, 400))),
384            r2.intersect(&r3)
385        );
386
387        assert_eq!(Some(r1.clone()), r1.intersect(&r1));
388        assert_eq!(Some(r2.clone()), r2.intersect(&r2));
389        assert_eq!(Some(r3.clone()), r3.intersect(&r3));
390    }
391
392    #[test]
393    pub fn test_intersect_2()
394    {
395        let r1 = URect::from_tuples((100, 100), (200, 200));
396        let r2 = URect::from_tuples((100, 200), (200, 300));
397
398        assert_eq!(None, r1.intersect(&r2));
399    }
400}
401
402pub type URoundRect = RoundedRectangle<u32>;
410
411pub type IRoundRect = RoundedRectangle<i32>;
417
418pub type RoundRect = RoundedRectangle<f32>;
424
425#[derive(Debug, PartialEq, Eq, Clone)]
429#[repr(C)]
430pub struct RoundedRectangle<T = f32>
431{
432    rect: Rectangle<T>,
433    radius: T
434}
435
436impl<T> AsRef<RoundedRectangle<T>> for RoundedRectangle<T>
437{
438    fn as_ref(&self) -> &Self
439    {
440        self
441    }
442}
443
444impl<T> RoundedRectangle<T>
445{
446    #[inline]
451    pub const fn new(top_left: Vector2<T>, bottom_right: Vector2<T>, radius: T) -> Self
452    {
453        RoundedRectangle {
454            rect: Rectangle::new(top_left, bottom_right),
455            radius
456        }
457    }
458
459    #[inline]
466    pub fn from_tuples(top_left: (T, T), bottom_right: (T, T), radius: T) -> Self
467    {
468        RoundedRectangle {
469            rect: Rectangle::from_tuples(top_left, bottom_right),
470            radius
471        }
472    }
473
474    #[inline]
479    pub fn from_rectangle(rect: Rectangle<T>, radius: T) -> Self
480    {
481        RoundedRectangle { rect, radius }
482    }
483
484    #[inline]
486    pub const fn top_left(&self) -> &Vector2<T>
487    {
488        &self.rect.top_left
489    }
490
491    #[inline]
493    pub const fn bottom_right(&self) -> &Vector2<T>
494    {
495        &self.rect.bottom_right
496    }
497}
498
499impl<T: Copy> RoundedRectangle<T>
500{
501    #[inline]
503    pub fn top_right(&self) -> Vector2<T>
504    {
505        Vector2::new(self.rect.bottom_right.x, self.rect.top_left.y)
506    }
507
508    #[inline]
510    pub fn bottom_left(&self) -> Vector2<T>
511    {
512        Vector2::new(self.rect.top_left.x, self.rect.bottom_right.y)
513    }
514
515    #[inline]
517    pub fn radius(&self) -> T
518    {
519        self.radius
520    }
521
522    #[inline]
524    pub fn left(&self) -> T
525    {
526        self.rect.top_left.x
527    }
528
529    #[inline]
531    pub fn right(&self) -> T
532    {
533        self.rect.bottom_right.x
534    }
535
536    #[inline]
538    pub fn top(&self) -> T
539    {
540        self.rect.top_left.y
541    }
542
543    #[inline]
545    pub fn bottom(&self) -> T
546    {
547        self.rect.bottom_right.y
548    }
549
550    #[inline]
553    pub fn as_rectangle(&self) -> &Rectangle<T>
554    {
555        &self.rect
556    }
557}
558
559impl<T: std::ops::Sub<Output = T> + Copy> RoundedRectangle<T>
560{
561    #[inline]
563    pub fn width(&self) -> T
564    {
565        self.rect.bottom_right.x - self.rect.top_left.x
566    }
567
568    #[inline]
570    pub fn height(&self) -> T
571    {
572        self.rect.bottom_right.y - self.rect.top_left.y
573    }
574
575    #[inline]
578    pub fn size(&self) -> Vector2<T>
579    {
580        Vector2::new(self.width(), self.height())
581    }
582}
583
584impl<T> RoundedRectangle<T>
585where
586    T: num_traits::AsPrimitive<f32>
587        + std::cmp::PartialOrd
588        + std::ops::Add<Output = T>
589        + std::ops::Sub<Output = T>
590        + std::ops::Mul<Output = T>
591        + std::ops::Neg<Output = T>
592        + std::ops::Div<Output = f32>
593        + std::ops::Div<f32, Output = T>
594        + Zero
595{
596    #[must_use]
600    pub fn contains(&self, point: Vector2<T>) -> bool
601    {
602        if !self.rect.contains(point) {
603            return false;
604        }
605        let inner = self.inner();
606        if inner.contains(point) {
607            return true;
608        }
609
610        let radius_squared = self.radius * self.radius;
611
612        let dx = max(
614            max(inner.left() - point.x, point.x - inner.right()),
615            T::zero()
616        );
617        let dy = max(
618            max(inner.top() - point.y, point.y - inner.bottom()),
619            T::zero()
620        );
621
622        if dx * dx + dy * dy <= radius_squared {
623            return true;
624        }
625
626        false
627    }
628}
629
630impl<T: PartialEq> RoundedRectangle<T>
631{
632    #[inline]
635    pub fn is_zero_area(&self) -> bool
636    {
637        self.rect.is_zero_area()
638    }
639}
640
641impl<T: std::cmp::PartialOrd> RoundedRectangle<T>
642{
643    #[inline]
646    pub fn is_positive_area(&self) -> bool
647    {
648        self.rect.is_positive_area()
649    }
650}
651
652impl<T: Copy> RoundedRectangle<T>
653where
654    Vector2<T>: std::ops::Add<Output = Vector2<T>>
655{
656    #[inline]
660    pub fn with_offset(&self, offset: impl Into<Vector2<T>>) -> Self
661    {
662        let offset = offset.into();
663        RoundedRectangle::new(
664            self.rect.top_left + offset,
665            self.rect.bottom_right + offset,
666            self.radius
667        )
668    }
669}
670
671impl<T: Copy> RoundedRectangle<T>
672where
673    Vector2<T>: std::ops::Sub<Output = Vector2<T>>
674{
675    #[inline]
679    pub fn with_negative_offset(&self, offset: impl Into<Vector2<T>>) -> Self
680    {
681        let offset = offset.into();
682        RoundedRectangle::new(
683            self.rect.top_left - offset,
684            self.rect.bottom_right - offset,
685            self.radius
686        )
687    }
688}
689
690impl<T: num_traits::AsPrimitive<f32>> RoundedRectangle<T>
691{
692    #[inline]
695    #[must_use]
696    pub fn into_f32(self) -> RoundedRectangle<f32>
697    {
698        RoundedRectangle::new(
699            self.rect.top_left.into_f32(),
700            self.rect.bottom_right.into_f32(),
701            self.radius.as_()
702        )
703    }
704}
705
706impl<T: num_traits::AsPrimitive<f32> + Copy> RoundedRectangle<T>
707{
708    #[inline]
711    #[must_use]
712    pub fn as_f32(&self) -> RoundedRectangle<f32>
713    {
714        RoundedRectangle::new(
715            self.rect.top_left.into_f32(),
716            self.rect.bottom_right.into_f32(),
717            self.radius.as_()
718        )
719    }
720}