diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index 1fd0eced..34c797ec 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -7584,33 +7584,18 @@ def union_bbox(elements, transformed=True, with_stroke=False): :param with_stroke: should the stroke-width be included in the bounds of the elements :return: union of all bounding boxes of elements within the iterable. """ - boundary_points = [] + boxes = [] for e in elements: - if not hasattr(e, "bbox"): + if not hasattr(e, "bbox") or isinstance(e, (Group, Use)): continue - box = e.bbox(transformed=False, with_stroke=with_stroke) + box = e.bbox(transformed=transformed, with_stroke=with_stroke) if box is None: continue - top_left = (box[0], box[1]) - top_right = (box[2], box[1]) - bottom_left = (box[0], box[3]) - bottom_right = (box[2], box[3]) - if transformed: - top_left = e.transform.point_in_matrix_space(top_left) - top_right = e.transform.point_in_matrix_space(top_right) - bottom_left = e.transform.point_in_matrix_space(bottom_left) - bottom_right = e.transform.point_in_matrix_space(bottom_right) - boundary_points.append(top_left) - boundary_points.append(top_right) - boundary_points.append(bottom_left) - boundary_points.append(bottom_right) - if len(boundary_points) == 0: + boxes.append(box) + if len(boxes) == 0: return None - xmin = min([e[0] for e in boundary_points]) - ymin = min([e[1] for e in boundary_points]) - xmax = max([e[0] for e in boundary_points]) - ymax = max([e[1] for e in boundary_points]) - return xmin, ymin, xmax, ymax + (xmins, ymins, xmaxs, ymaxs) = zip(*boxes) + return (min(xmins), min(ymins), max(xmaxs), max(ymaxs)) def bbox(self, transformed=True, with_stroke=False): """ diff --git a/test/test_bbox.py b/test/test_bbox.py index 292b8812..92f2061b 100644 --- a/test/test_bbox.py +++ b/test/test_bbox.py @@ -275,6 +275,58 @@ def test_bbox_subpath_stroke(self): 61 + (5. / 2.) )) + def test_bbox_rotated_circle(self): + # Rotation of circle must not affect it's bounding box + c = Circle(cx=0, cy=0, r=1, transform="rotate(45)") + (xmin, ymin, xmax, ymax) = c.bbox() + self.assertAlmostEqual(-1, xmin) + self.assertAlmostEqual(-1, ymin) + self.assertAlmostEqual( 1, xmax) + self.assertAlmostEqual( 1, ymax) + + def test_bbox_svg_with_rotated_circle(self): + # Rotation of circle within group must not affect it's bounding box + q = io.StringIO( + u''' + + + + ''' + ) + svg = SVG.parse(q) + (xmin, ymin, xmax, ymax) = svg.bbox() + self.assertAlmostEqual(-1, xmin) + self.assertAlmostEqual(-1, ymin) + self.assertAlmostEqual( 1, xmax) + self.assertAlmostEqual( 1, ymax) + + def test_bbox_translated_circle(self): + c = Circle(cx=0, cy=0, r=1, transform="translate(-1,-1)") + (xmin, ymin, xmax, ymax) = c.bbox() + self.assertAlmostEqual(-2, xmin) + self.assertAlmostEqual(-2, ymin) + self.assertAlmostEqual( 0, xmax) + self.assertAlmostEqual( 0, ymax) + + def test_bbox_svg_with_translated_group_with_circle(self): + # Translation of nested group must be applied correctly + q = io.StringIO( + u''' + + + + + + ''' + ) + svg = SVG.parse(q) + (xmin, ymin, xmax, ymax) = svg.bbox() + self.assertAlmostEqual(-2, xmin) + self.assertAlmostEqual(-2, ymin) + self.assertAlmostEqual( 0, xmax) + self.assertAlmostEqual( 0, ymax) + + def test_issue_104(self): """Testing Issue 104 rotated bbox""" rect = Rect(10,10,10,10)