[librsvg: 2/6] bbox: factor out a rect module



commit 5ab459252158690a9c59bd5aca50e453e3822032
Author: Paolo Borelli <pborelli gnome org>
Date:   Sun May 13 18:45:53 2018 +0200

    bbox: factor out a rect module
    
    Module for generic cairo::Rectangle utils

 Makefile.am                |   1 +
 rsvg_internals/src/bbox.rs | 117 ++++--------------------------------
 rsvg_internals/src/lib.rs  |   1 +
 rsvg_internals/src/rect.rs | 147 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 162 insertions(+), 104 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 6da52d82..5dcd8865 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -107,6 +107,7 @@ RUST_SRC =                                  \
        rsvg_internals/src/pattern.rs           \
        rsvg_internals/src/property_bag.rs      \
        rsvg_internals/src/property_macros.rs   \
+       rsvg_internals/src/rect.rs              \
        rsvg_internals/src/shapes.rs            \
        rsvg_internals/src/space.rs             \
        rsvg_internals/src/state.rs             \
diff --git a/rsvg_internals/src/bbox.rs b/rsvg_internals/src/bbox.rs
index fe63b0f7..abc2df82 100644
--- a/rsvg_internals/src/bbox.rs
+++ b/rsvg_internals/src/bbox.rs
@@ -6,6 +6,8 @@ use glib::translate::*;
 
 use float_eq_cairo::ApproxEqCairo;
 
+use rect;
+
 // Keep this in sync with ../../rsvg-private.h:RsvgBbox
 #[repr(C)]
 #[derive(Copy, Clone)]
@@ -49,61 +51,19 @@ impl RsvgBbox {
             return;
         }
 
-        let (mut xmin, mut ymin, mut xmax, mut ymax) = if !self.is_virgin() {
-            (
-                self.rect.x,
-                self.rect.y,
-                (self.rect.x + self.rect.width),
-                (self.rect.y + self.rect.height),
-            )
-        } else {
-            (0.0, 0.0, 0.0, 0.0)
-        };
-
         let mut affine = self.affine;
 
         // this will panic!() if it's not invertible... should we check on our own?
         affine.invert();
         affine = cairo::Matrix::multiply(&src.affine, &affine);
 
-        // This is a trick.  We want to transform each of the corners of
-        // the rectangle defined by src.rect with the affine
-        // transformation, and get the bounding box of all the four
-        // resulting points.  The modulus and division accomplish this by
-        // running through all the combinations of adding or not adding
-        // the width/height to the first point src.rect.(x, y).
-        for i in 0..4 {
-            let rx: f64 = src.rect.x + src.rect.width * f64::from(i % 2);
-            let ry: f64 = src.rect.y + src.rect.height * f64::from(i / 2);
-            let x: f64 = affine.xx * rx + affine.xy * ry + affine.x0;
-            let y: f64 = affine.yx * rx + affine.yy * ry + affine.y0;
-
-            if self.is_virgin() {
-                xmin = x;
-                xmax = x;
-                ymin = y;
-                ymax = y;
-                self.virgin = false.to_glib();
-            } else {
-                if x < xmin {
-                    xmin = x;
-                }
-                if x > xmax {
-                    xmax = x;
-                }
-                if y < ymin {
-                    ymin = y;
-                }
-                if y > ymax {
-                    ymax = y;
-                }
-            }
-        }
+        let rect = rect::transform(&affine, &src.rect);
 
-        self.rect.x = xmin;
-        self.rect.y = ymin;
-        self.rect.width = xmax - xmin;
-        self.rect.height = ymax - ymin;
+        if self.is_virgin() {
+            self.set_rect(&rect);
+        } else {
+            self.rect = rect::union(&rect, &self.rect);
+        }
     }
 
     pub fn clip(&mut self, src: &RsvgBbox) {
@@ -111,69 +71,18 @@ impl RsvgBbox {
             return;
         }
 
-        let (mut xmin, mut ymin, mut xmax, mut ymax) = if !self.is_virgin() {
-            (
-                (self.rect.x + self.rect.width),
-                (self.rect.y + self.rect.height),
-                self.rect.x,
-                self.rect.y,
-            )
-        } else {
-            (0.0, 0.0, 0.0, 0.0)
-        };
-
         let mut affine = self.affine;
 
         affine.invert();
         affine = cairo::Matrix::multiply(&src.affine, &affine);
 
-        // This is a trick.  See rsvg_bbox_insert() for a description of how it works.
-        for i in 0..4 {
-            let rx: f64 = src.rect.x + src.rect.width * f64::from(i % 2);
-            let ry: f64 = src.rect.y + src.rect.height * f64::from(i / 2);
-            let x = affine.xx * rx + affine.xy * ry + affine.x0;
-            let y = affine.yx * rx + affine.yy * ry + affine.y0;
-
-            if self.is_virgin() {
-                xmin = x;
-                xmax = x;
-                ymin = y;
-                ymax = y;
-                self.virgin = false.to_glib();
-            } else {
-                if x < xmin {
-                    xmin = x;
-                }
-                if x > xmax {
-                    xmax = x;
-                }
-                if y < ymin {
-                    ymin = y;
-                }
-                if y > ymax {
-                    ymax = y;
-                }
-            }
-        }
-
-        if xmin < self.rect.x {
-            xmin = self.rect.x;
-        }
-        if ymin < self.rect.y {
-            ymin = self.rect.y;
-        }
+        let rect = rect::transform(&affine, &src.rect);
 
-        if xmax > self.rect.x + self.rect.width {
-            xmax = self.rect.x + self.rect.width;
-        }
-        if ymax > self.rect.y + self.rect.height {
-            ymax = self.rect.y + self.rect.height;
+        if self.is_virgin() {
+            self.set_rect(&rect);
+        } else {
+            self.rect = rect::intersect(&rect, &self.rect);
         }
-
-        self.rect.x = xmin;
-        self.rect.width = xmax - xmin;
-        self.rect.y = ymin;
-        self.rect.height = ymax - ymin;
     }
 }
 
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index 85194dcc..5aa731a9 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -189,6 +189,7 @@ mod space;
 mod state;
 mod stop;
 mod structure;
+mod rect;
 mod text;
 mod transform;
 mod unitinterval;
diff --git a/rsvg_internals/src/rect.rs b/rsvg_internals/src/rect.rs
new file mode 100644
index 00000000..06209f6c
--- /dev/null
+++ b/rsvg_internals/src/rect.rs
@@ -0,0 +1,147 @@
+use cairo;
+use cairo::MatrixTrait;
+
+pub fn intersect(r1: &cairo::Rectangle, r2: &cairo::Rectangle) -> cairo::Rectangle {
+    let (x1, y1, x2, y2) = (
+        r1.x.max(r2.x),
+        r1.y.max(r2.y),
+        (r1.x + r1.width).min(r2.x + r2.width),
+        (r1.y + r1.height).min(r2.y + r2.height),
+    );
+
+    cairo::Rectangle {
+        x: x1,
+        y: y1,
+        width: x2 - x1,
+        height: y2 - y1,
+    }
+}
+
+pub fn union(r1: &cairo::Rectangle, r2: &cairo::Rectangle) -> cairo::Rectangle {
+    let (x1, y1, x2, y2) = (
+        r1.x.min(r2.x),
+        r1.y.min(r2.y),
+        (r1.x + r1.width).max(r2.x + r2.width),
+        (r1.y + r1.height).max(r2.y + r2.height),
+    );
+
+    cairo::Rectangle {
+        x: x1,
+        y: y1,
+        width: x2 - x1,
+        height: y2 - y1,
+    }
+}
+
+pub fn transform(affine: &cairo::Matrix, rect: &cairo::Rectangle) -> cairo::Rectangle {
+    let points = vec![
+        affine.transform_point(rect.x, rect.y),
+        affine.transform_point(rect.x + rect.width, rect.y),
+        affine.transform_point(rect.x, rect.y + rect.height),
+        affine.transform_point(rect.x + rect.width, rect.y + rect.height),
+    ];
+
+    let (mut xmin, mut ymin, mut xmax, mut ymax) = {
+        let (x, y) = points[0];
+
+        (x, y, x, y)
+    };
+
+    for i in 1..4 {
+        let (x, y) = points[i];
+
+        if x < xmin {
+            xmin = x;
+        }
+
+        if x > xmax {
+            xmax = x;
+        }
+
+        if y < ymin {
+            ymin = y;
+        }
+
+        if y > ymax {
+            ymax = y;
+        }
+    }
+
+    cairo::Rectangle {
+        x: xmin,
+        y: ymin,
+        width: xmax - xmin,
+        height: ymax - ymin,
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use float_eq_cairo::ApproxEqCairo;
+
+    #[test]
+    fn intersect_rects() {
+        let r1 = cairo::Rectangle {
+            x: 0.42,
+            y: 0.42,
+            width: 4.14,
+            height: 4.14,
+        };
+        let r2 = cairo::Rectangle {
+            x: 0.22,
+            y: 0.22,
+            width: 3.14,
+            height: 3.14,
+        };
+
+        let r = intersect(&r1, &r2);
+        assert_approx_eq_cairo!(0.42_f64, r.x);
+        assert_approx_eq_cairo!(0.42_f64, r.y);
+        assert_approx_eq_cairo!(2.94_f64, r.width);
+        assert_approx_eq_cairo!(2.94_f64, r.height);
+    }
+
+    #[test]
+    fn union_rects() {
+        let r1 = cairo::Rectangle {
+            x: 0.42,
+            y: 0.42,
+            width: 4.14,
+            height: 4.14,
+        };
+        let r2 = cairo::Rectangle {
+            x: 0.22,
+            y: 0.22,
+            width: 3.14,
+            height: 3.14,
+        };
+
+        let r = union(&r1, &r2);
+        assert_approx_eq_cairo!(0.22_f64, r.x);
+        assert_approx_eq_cairo!(0.22_f64, r.y);
+        assert_approx_eq_cairo!(4.34_f64, r.width);
+        assert_approx_eq_cairo!(4.34_f64, r.height);
+    }
+
+    #[test]
+    fn transform_rect() {
+        let r = cairo::Rectangle {
+            x: 0.42,
+            y: 0.42,
+            width: 3.14,
+            height: 3.14,
+        };
+
+        let m = cairo::Matrix::identity();
+        let tr = transform(&m, &r);
+        assert_eq!(tr, r);
+
+        let m = cairo::Matrix::new(2.0, 0.0, 0.0, 2.0, 1.5, 1.5);
+        let tr = transform(&m, &r);
+        assert_approx_eq_cairo!(2.34_f64, tr.x);
+        assert_approx_eq_cairo!(2.34_f64, tr.y);
+        assert_approx_eq_cairo!(6.28_f64, tr.width);
+        assert_approx_eq_cairo!(6.28_f64, tr.height);
+    }
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]