rsvg/
bbox.rs

1//! Bounding boxes that know their coordinate space.
2
3use crate::rect::Rect;
4use crate::transform::Transform;
5
6#[derive(Debug, Default, Copy, Clone)]
7pub struct BoundingBox {
8    transform: Transform,
9    pub rect: Option<Rect>,     // without stroke
10    pub ink_rect: Option<Rect>, // with stroke
11}
12
13impl BoundingBox {
14    pub fn new() -> BoundingBox {
15        Default::default()
16    }
17
18    pub fn with_transform(self, transform: Transform) -> BoundingBox {
19        BoundingBox { transform, ..self }
20    }
21
22    pub fn with_rect(self, rect: Rect) -> BoundingBox {
23        BoundingBox {
24            rect: Some(rect),
25            ..self
26        }
27    }
28
29    pub fn with_ink_rect(self, ink_rect: Rect) -> BoundingBox {
30        BoundingBox {
31            ink_rect: Some(ink_rect),
32            ..self
33        }
34    }
35
36    fn combine(&mut self, src: &BoundingBox, clip: bool) {
37        if src.rect.is_none() && src.ink_rect.is_none() {
38            return;
39        }
40
41        // this will panic!() if it's not invertible... should we check on our own?
42        let transform = self
43            .transform
44            .invert()
45            .unwrap()
46            .pre_transform(&src.transform);
47
48        self.rect = combine_rects(self.rect, src.rect, &transform, clip);
49        self.ink_rect = combine_rects(self.ink_rect, src.ink_rect, &transform, clip);
50    }
51
52    pub fn insert(&mut self, src: &BoundingBox) {
53        self.combine(src, false);
54    }
55
56    pub fn clip(&mut self, src: &BoundingBox) {
57        self.combine(src, true);
58    }
59}
60
61fn combine_rects(
62    r1: Option<Rect>,
63    r2: Option<Rect>,
64    transform: &Transform,
65    clip: bool,
66) -> Option<Rect> {
67    match (r1, r2, clip) {
68        (r1, None, _) => r1,
69        (None, Some(r2), _) => Some(transform.transform_rect(&r2)),
70        (Some(r1), Some(r2), true) => transform
71            .transform_rect(&r2)
72            .intersection(&r1)
73            .or_else(|| Some(Rect::default())),
74        (Some(r1), Some(r2), false) => Some(transform.transform_rect(&r2).union(&r1)),
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn combine() {
84        let r1 = Rect::new(1.0, 2.0, 3.0, 4.0);
85        let r2 = Rect::new(1.5, 2.5, 3.5, 4.5);
86        let r3 = Rect::new(10.0, 11.0, 12.0, 13.0);
87        let t = Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, 0.5, 0.5);
88
89        let res = combine_rects(None, None, &t, true);
90        assert_eq!(res, None);
91
92        let res = combine_rects(None, None, &t, false);
93        assert_eq!(res, None);
94
95        let res = combine_rects(Some(r1), None, &t, true);
96        assert_eq!(res, Some(r1));
97
98        let res = combine_rects(Some(r1), None, &t, false);
99        assert_eq!(res, Some(r1));
100
101        let res = combine_rects(None, Some(r2), &t, true);
102        assert_eq!(res, Some(Rect::new(2.0, 3.0, 4.0, 5.0)));
103
104        let res = combine_rects(None, Some(r2), &t, false);
105        assert_eq!(res, Some(Rect::new(2.0, 3.0, 4.0, 5.0)));
106
107        let res = combine_rects(Some(r1), Some(r2), &t, true);
108        assert_eq!(res, Some(Rect::new(2.0, 3.0, 3.0, 4.0)));
109
110        let res = combine_rects(Some(r1), Some(r3), &t, true);
111        assert_eq!(res, Some(Rect::default()));
112
113        let res = combine_rects(Some(r1), Some(r2), &t, false);
114        assert_eq!(res, Some(Rect::new(1.0, 2.0, 4.0, 5.0)));
115    }
116}