diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index b83714bdcd7b..544a626b8abe 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -806,6 +806,18 @@ def get_xy(self): """Return the left and bottom coords of the rectangle as a tuple.""" return self._x0, self._y0 + def get_corners(self): + """ + Return the corners of the rectangle, moving anti-clockwise from + (x0, y0). + """ + return self.get_patch_transform().transform( + [(0, 0), (1, 0), (1, 1), (0, 1)]) + + def get_center(self): + """Return the centre of the rectangle.""" + return self.get_patch_transform().transform((0.5, 0.5)) + def get_width(self): """Return the width of the rectangle.""" return self._width @@ -1657,6 +1669,16 @@ def get_angle(self): angle = property(get_angle, set_angle) + def get_corners(self): + """ + Return the corners of the ellipse bounding box. + + The bounding box orientation is moving anti-clockwise from the + lower left corner defined before rotation. + """ + return self.get_patch_transform().transform( + [(-1, -1), (1, -1), (1, 1), (-1, 1)]) + class Annulus(Patch): """ diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index 9487758c8aef..6a8ddc87f3ae 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -6,7 +6,7 @@ import pytest import matplotlib as mpl -from matplotlib.patches import (Annulus, Patch, Polygon, Rectangle, +from matplotlib.patches import (Annulus, Ellipse, Patch, Polygon, Rectangle, FancyArrowPatch) from matplotlib.testing.decorators import image_comparison, check_figures_equal from matplotlib.transforms import Bbox @@ -54,6 +54,54 @@ def test_Polygon_close(): assert_array_equal(p.get_xy(), xyclosed) +def test_corner_center(): + loc = [10, 20] + width = 1 + height = 2 + + # Rectangle + # No rotation + corners = ((10, 20), (11, 20), (11, 22), (10, 22)) + rect = Rectangle(loc, width, height) + assert_array_equal(rect.get_corners(), corners) + assert_array_equal(rect.get_center(), (10.5, 21)) + + # 90 deg rotation + corners_rot = ((10, 20), (10, 21), (8, 21), (8, 20)) + rect.set_angle(90) + assert_array_equal(rect.get_corners(), corners_rot) + assert_array_equal(rect.get_center(), (9, 20.5)) + + # Rotation not a multiple of 90 deg + theta = 33 + t = mtransforms.Affine2D().rotate_around(*loc, np.deg2rad(theta)) + corners_rot = t.transform(corners) + rect.set_angle(theta) + assert_almost_equal(rect.get_corners(), corners_rot) + + # Ellipse + loc = [loc[0] + width / 2, + loc[1] + height / 2] + ellipse = Ellipse(loc, width, height) + + # No rotation + assert_array_equal(ellipse.get_corners(), corners) + + # 90 deg rotation + corners_rot = ((11.5, 20.5), (11.5, 21.5), (9.5, 21.5), (9.5, 20.5)) + ellipse.set_angle(90) + assert_array_equal(ellipse.get_corners(), corners_rot) + # Rotation shouldn't change ellipse center + assert_array_equal(ellipse.get_center(), loc) + + # Rotation not a multiple of 90 deg + theta = 33 + t = mtransforms.Affine2D().rotate_around(*loc, np.deg2rad(theta)) + corners_rot = t.transform(corners) + ellipse.set_angle(theta) + assert_almost_equal(ellipse.get_corners(), corners_rot) + + def test_rotate_rect(): loc = np.asarray([1.0, 2.0]) width = 2