geometry/
union.rs

1//! Unions of geometric objects.
2
3/// Trait for calculating the union with another geometric object.
4pub trait Union<T: ?Sized> {
5    /// The type of the output shape representing the union.
6    type Output;
7    /// Calculates the union of this shape with `other`.
8    fn union(&self, other: &T) -> Self::Output;
9}
10
11/// Trait for calculating a shape that bounds the union of two geometric objects.
12pub trait BoundingUnion<T: ?Sized> {
13    /// The type of the output shape representing the bounding union.
14    type Output;
15    /// Calculates the bounding union of this shape with `other`.
16    ///
17    /// # Example
18    ///
19    /// ```
20    /// # use geometry::prelude::*;
21    /// # use geometry::union::BoundingUnion;
22    ///
23    /// let r1 = Rect::from_sides(0, 0, 100, 200);
24    /// let r2 = Rect::from_sides(-50, 20, 120, 160);
25    /// assert_eq!(r1.bounding_union(&r2), Rect::from_sides(-50, 0, 120, 200));
26    ///
27    /// let r1 = Rect::from_sides(0, 0, 100, 200);
28    /// let r2 = None;
29    /// assert_eq!(r1.bounding_union(&r2), Rect::from_sides(0, 0, 100, 200));
30    /// ```
31    fn bounding_union(&self, other: &T) -> Self::Output;
32}
33
34impl<T: BoundingUnion<T, Output = T> + Clone> BoundingUnion<Option<T>> for T {
35    type Output = T;
36
37    fn bounding_union(&self, other: &Option<T>) -> Self::Output {
38        if let Some(obj) = other {
39            self.bounding_union(obj)
40        } else {
41            self.clone()
42        }
43    }
44}
45
46impl<T: BoundingUnion<T, Output = T> + Clone> BoundingUnion<T> for Option<T> {
47    type Output = T;
48    fn bounding_union(&self, other: &T) -> Self::Output {
49        if let Some(obj) = self {
50            obj.bounding_union(other)
51        } else {
52            other.clone()
53        }
54    }
55}
56
57impl<T: BoundingUnion<Option<T>, Output = T> + Clone> BoundingUnion<Option<T>> for Option<T> {
58    type Output = Option<T>;
59    fn bounding_union(&self, other: &Option<T>) -> Self::Output {
60        if let Some(obj) = self {
61            Some(obj.bounding_union(other))
62        } else {
63            other.clone()
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use crate::{rect::Rect, span::Span, union::BoundingUnion};
71
72    #[test]
73    fn union_works_for_option_rects() {
74        let r1 = Rect::from_sides(0, 0, 100, 200);
75        let r2 = Rect::from_sides(-50, 20, 120, 160);
76        assert_eq!(r1.bounding_union(&r2), Rect::from_sides(-50, 0, 120, 200));
77
78        let r1 = Rect::from_sides(0, 0, 100, 200);
79        let r2 = None;
80        assert_eq!(r1.bounding_union(&r2), Rect::from_sides(0, 0, 100, 200));
81        assert_eq!(r2.bounding_union(&r1), Rect::from_sides(0, 0, 100, 200));
82
83        let r1 = Some(Rect::from_sides(0, 0, 100, 200));
84        let r2: Option<Rect> = None;
85        assert_eq!(
86            r1.bounding_union(&r2),
87            Some(Rect::from_sides(0, 0, 100, 200))
88        );
89        assert_eq!(
90            r2.bounding_union(&r1),
91            Some(Rect::from_sides(0, 0, 100, 200))
92        );
93
94        let r1: Option<Rect> = None;
95        let r2: Option<Rect> = None;
96        assert_eq!(r1.bounding_union(&r2), None,);
97    }
98
99    #[test]
100    fn union_works_for_option_spans() {
101        let r1 = Span::new(0, 100);
102        let r2 = Span::new(-50, 120);
103        assert_eq!(r1.bounding_union(&r2), Span::new(-50, 120));
104
105        let r1 = Span::new(0, 100);
106        let r2 = None;
107        assert_eq!(r1.bounding_union(&r2), Span::new(0, 100));
108        assert_eq!(r2.bounding_union(&r1), Span::new(0, 100));
109
110        let r1 = Some(Span::new(0, 100));
111        let r2: Option<Span> = None;
112        assert_eq!(r1.bounding_union(&r2), Some(Span::new(0, 100)));
113        assert_eq!(r2.bounding_union(&r1), Some(Span::new(0, 100)));
114
115        let r1: Option<Span> = None;
116        let r2: Option<Span> = None;
117        assert_eq!(r1.bounding_union(&r2), None,);
118    }
119}