From 142f080e82418a697f99b2afd06365a13378ebb4 Mon Sep 17 00:00:00 2001 From: Derek Homeier Date: Wed, 28 Jul 2021 21:12:02 +0200 Subject: [PATCH] Support rotation of Ellipse,RectanglePixelRegion MPL selectors --- regions/shapes/ellipse.py | 42 +++++++++++++++++++++++------------- regions/shapes/rectangle.py | 43 ++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/regions/shapes/ellipse.py b/regions/shapes/ellipse.py index b3414671..f1bf061a 100644 --- a/regions/shapes/ellipse.py +++ b/regions/shapes/ellipse.py @@ -209,13 +209,22 @@ def as_artist(self, origin=(0, 0), **kwargs): **mpl_kwargs) def _update_from_mpl_selector(self, *args, **kwargs): - xmin, xmax, ymin, ymax = self._mpl_selector.extents - self.center = PixCoord(x=0.5 * (xmin + xmax), - y=0.5 * (ymin + ymax)) - self.width = (xmax - xmin) - self.height = (ymax - ymin) - self.angle = 0. * u.deg - if self._mpl_selector_callback is not None: + # _rect_properties replace _rect_bbox in matplotlib#19864 + # "Note that if rotation != 0, ``xmin, ymin`` are interpreted as the + # lower corner, and ``xmax, ymax`` are calculated using only width and + # height assuming no rotation." + + self.center = PixCoord(*self._mpl_selector.center) + if hasattr(self._mpl_selector, '_rotation'): + x0, y0, self.width, self.height, rotation = self._mpl_selector._rect_properties + else: + xmin, xmax, ymin, ymax = self._mpl_selector.extents + self.width = 2 * (self.center.x - xmin) + self.height = 2 * (self.center.y - ymin) + rotation = 0 + self.angle = rotation * u.radian + + if getattr(self, '_mpl_selector_callback', None) is not None: self._mpl_selector_callback(self) def as_mpl_selector(self, ax, active=True, sync=True, callback=None, @@ -262,10 +271,6 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None, raise Exception('Cannot attach more than one selector to a ' 'region.') - if self.angle.value != 0: - raise NotImplementedError('Cannot create matplotlib selector for ' - 'rotated ellipse.') - if sync: sync_callback = self._update_from_mpl_selector else: @@ -279,10 +284,17 @@ def sync_callback(*args, **kwargs): 'linewidth': self.visual.get('linewidth', 1), 'linestyle': self.visual.get('linestyle', 'solid')}) - self._mpl_selector.extents = (self.center.x - self.width / 2, - self.center.x + self.width / 2, - self.center.y - self.height / 2, - self.center.y + self.height / 2) + xy0 = [self.center.x - self.width / 2, self.center.y - self.height / 2] + self._mpl_selector.extents = (xy0[0], self.center.x + self.width / 2, + xy0[1], self.center.y + self.height / 2) + + if self.angle.value != 0: + if hasattr(self._mpl_selector, '_rotation'): + self._mpl_selector._set_corner_width_rotation(xy0, self.width, self.height, + self.angle.to_value('radian')) + else: + raise NotImplementedError('Cannot create matplotlib selector for rotated ellipse.') + self._mpl_selector.set_active(active) self._mpl_selector_callback = callback diff --git a/regions/shapes/rectangle.py b/regions/shapes/rectangle.py index 57947772..9176befe 100644 --- a/regions/shapes/rectangle.py +++ b/regions/shapes/rectangle.py @@ -202,13 +202,22 @@ def as_artist(self, origin=(0, 0), **kwargs): angle=angle, **mpl_kwargs) def _update_from_mpl_selector(self, *args, **kwargs): - xmin, xmax, ymin, ymax = self._mpl_selector.extents - self.center = PixCoord(x=0.5 * (xmin + xmax), - y=0.5 * (ymin + ymax)) - self.width = (xmax - xmin) - self.height = (ymax - ymin) - self.angle = 0. * u.deg - if self._mpl_selector_callback is not None: + # _rect_properties replace _rect_bbox in matplotlib#19864 + # "Note that if rotation != 0, ``xmin, ymin`` are interpreted as the + # lower corner, and ``xmax, ymax`` are calculated using only width and + # height assuming no rotation." + + self.center = PixCoord(*self._mpl_selector.center) + if hasattr(self._mpl_selector, '_rotation'): + x0, y0, self.width, self.height, rotation = self._mpl_selector._rect_properties + else: + xmin, xmax, ymin, ymax = self._mpl_selector.extents + self.width = 2 * (self.center.x - xmin) + self.height = 2 * (self.center.y - ymin) + rotation = 0 + self.angle = rotation * u.radian + + if getattr(self, '_mpl_selector_callback', None) is not None: self._mpl_selector_callback(self) def as_mpl_selector(self, ax, active=True, sync=True, callback=None, @@ -255,10 +264,6 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None, raise Exception('Cannot attach more than one selector to a ' 'region.') - if self.angle.value != 0: - raise NotImplementedError('Cannot create matplotlib selector for ' - 'rotated rectangle.') - if sync: sync_callback = self._update_from_mpl_selector else: @@ -272,10 +277,18 @@ def sync_callback(*args, **kwargs): 'linewidth': self.visual.get('linewidth', 1), 'linestyle': self.visual.get('linestyle', 'solid')}) - self._mpl_selector.extents = (self.center.x - self.width / 2, - self.center.x + self.width / 2, - self.center.y - self.height / 2, - self.center.y + self.height / 2) + xy0 = [self.center.x - self.width / 2, self.center.y - self.height / 2] + self._mpl_selector.extents = (xy0[0], self.center.x + self.width / 2, + xy0[1], self.center.y + self.height / 2) + + if self.angle.value != 0: + if hasattr(self._mpl_selector, '_rotation'): + self._mpl_selector._set_corner_width_rotation(xy0, self.width, self.height, + self.angle.to_value('radian')) + else: + raise NotImplementedError('Cannot create matplotlib selector for rotated ' + 'rectangle.') + self._mpl_selector.set_active(active) self._mpl_selector_callback = callback