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}