1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Unions of geometric objects.

/// Trait for calculating the union with another geometric object.
pub trait Union<T: ?Sized> {
    /// The type of the output shape representing the union.
    type Output;
    /// Calculates the union of this shape with `other`.
    fn union(&self, other: &T) -> Self::Output;
}

/// Trait for calculating a shape that bounds the union of two geometric objects.
pub trait BoundingUnion<T: ?Sized> {
    /// The type of the output shape representing the bounding union.
    type Output;
    /// Calculates the bounding union of this shape with `other`.
    ///
    /// # Example
    ///
    /// ```
    /// # use geometry::prelude::*;
    /// # use geometry::union::BoundingUnion;
    ///
    /// let r1 = Rect::from_sides(0, 0, 100, 200);
    /// let r2 = Rect::from_sides(-50, 20, 120, 160);
    /// assert_eq!(r1.bounding_union(&r2), Rect::from_sides(-50, 0, 120, 200));
    ///
    /// let r1 = Rect::from_sides(0, 0, 100, 200);
    /// let r2 = None;
    /// assert_eq!(r1.bounding_union(&r2), Rect::from_sides(0, 0, 100, 200));
    /// ```
    fn bounding_union(&self, other: &T) -> Self::Output;
}

impl<T: BoundingUnion<T, Output = T> + Clone> BoundingUnion<Option<T>> for T {
    type Output = T;

    fn bounding_union(&self, other: &Option<T>) -> Self::Output {
        if let Some(obj) = other {
            self.bounding_union(obj)
        } else {
            self.clone()
        }
    }
}

impl<T: BoundingUnion<T, Output = T> + Clone> BoundingUnion<T> for Option<T> {
    type Output = T;
    fn bounding_union(&self, other: &T) -> Self::Output {
        if let Some(obj) = self {
            obj.bounding_union(other)
        } else {
            other.clone()
        }
    }
}

impl<T: BoundingUnion<Option<T>, Output = T> + Clone> BoundingUnion<Option<T>> for Option<T> {
    type Output = Option<T>;
    fn bounding_union(&self, other: &Option<T>) -> Self::Output {
        if let Some(obj) = self {
            Some(obj.bounding_union(other))
        } else {
            other.clone()
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{rect::Rect, span::Span, union::BoundingUnion};

    #[test]
    fn union_works_for_option_rects() {
        let r1 = Rect::from_sides(0, 0, 100, 200);
        let r2 = Rect::from_sides(-50, 20, 120, 160);
        assert_eq!(r1.bounding_union(&r2), Rect::from_sides(-50, 0, 120, 200));

        let r1 = Rect::from_sides(0, 0, 100, 200);
        let r2 = None;
        assert_eq!(r1.bounding_union(&r2), Rect::from_sides(0, 0, 100, 200));
        assert_eq!(r2.bounding_union(&r1), Rect::from_sides(0, 0, 100, 200));

        let r1 = Some(Rect::from_sides(0, 0, 100, 200));
        let r2: Option<Rect> = None;
        assert_eq!(
            r1.bounding_union(&r2),
            Some(Rect::from_sides(0, 0, 100, 200))
        );
        assert_eq!(
            r2.bounding_union(&r1),
            Some(Rect::from_sides(0, 0, 100, 200))
        );

        let r1: Option<Rect> = None;
        let r2: Option<Rect> = None;
        assert_eq!(r1.bounding_union(&r2), None,);
    }

    #[test]
    fn union_works_for_option_spans() {
        let r1 = Span::new(0, 100);
        let r2 = Span::new(-50, 120);
        assert_eq!(r1.bounding_union(&r2), Span::new(-50, 120));

        let r1 = Span::new(0, 100);
        let r2 = None;
        assert_eq!(r1.bounding_union(&r2), Span::new(0, 100));
        assert_eq!(r2.bounding_union(&r1), Span::new(0, 100));

        let r1 = Some(Span::new(0, 100));
        let r2: Option<Span> = None;
        assert_eq!(r1.bounding_union(&r2), Some(Span::new(0, 100)));
        assert_eq!(r2.bounding_union(&r1), Some(Span::new(0, 100)));

        let r1: Option<Span> = None;
        let r2: Option<Span> = None;
        assert_eq!(r1.bounding_union(&r2), None,);
    }
}