Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch to mpl #20839 rotation implementation
Browse files Browse the repository at this point in the history
dhomeier committed Feb 17, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent feb2be5 commit 2ebeaca
Showing 5 changed files with 44 additions and 47 deletions.
9 changes: 5 additions & 4 deletions regions/_utils/optional_deps.py
Original file line number Diff line number Diff line change
@@ -3,11 +3,12 @@
try:
import matplotlib # noqa
HAS_MATPLOTLIB = True
MPL_VERSION = getattr(matplotlib, '__version__', None)
if MPL_VERSION is None:
MPL_VERSION = matplotlib._version.version
MPL_VERSION = MPL_VERSION.split('.')
MPL_VER_STR = getattr(matplotlib, '__version__', None)
if MPL_VER_STR is None:
MPL_VER_STR = matplotlib._version.version
MPL_VERSION = MPL_VER_STR.split('.')
MPL_VERSION = 10 * int(MPL_VERSION[0]) + int(MPL_VERSION[1])
except ImportError:
HAS_MATPLOTLIB = False
MPL_VER_STR = 'None'
MPL_VERSION = 0
15 changes: 8 additions & 7 deletions regions/shapes/ellipse.py
Original file line number Diff line number Diff line change
@@ -212,10 +212,10 @@ def as_artist(self, origin=(0, 0), **kwargs):
**mpl_kwargs)

def _update_from_mpl_selector(self, *args, **kwargs):
# _rect_properties replace _rect_bbox in matplotlib#19864
# "Note that if rotation != 0, ``xmin, ymin`` are interpreted as the
# _rect_properties replace _rect_bbox in matplotlib#19864, unchanged in #20839.
# "Note that if rotation != 0, ``xmin, ymin`` are always interpreted as the
# lower corner, and ``xmax, ymax`` are calculated using only width and
# height assuming no rotation."
# height assuming no rotation (as specified for ``selector.extents``)."

xmin, xmax, ymin, ymax = self._mpl_selector.extents
self.width = xmax - xmin
@@ -226,7 +226,7 @@ def _update_from_mpl_selector(self, *args, **kwargs):
else:
self.center = PixCoord(x=0.5 * (xmin + xmax), y=0.5 * (ymin + ymax))
rotation = 0
self.angle = rotation * u.radian
self.angle = rotation * u.deg

if getattr(self, '_mpl_selector_callback', None) is not None:
self._mpl_selector_callback(self)
@@ -274,13 +274,14 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None,
``selector.set_active(True)`` or ``selector.set_active(False)``.
"""
from matplotlib.widgets import EllipseSelector
from .._utils.optional_deps import MPL_VERSION
from .._utils.optional_deps import MPL_VERSION, MPL_VER_STR

if hasattr(self, '_mpl_selector'):
raise AttributeError('Cannot attach more than one selector to a region.')

if self.angle.value != 0 and not hasattr(EllipseSelector, 'rotation'):
raise NotImplementedError('Cannot create matplotlib selector for rotated ellipse.')
raise NotImplementedError('Creating selectors for rotated shapes is not '
f'yet supported with matplotlib {MPL_VER_STR}.')

if sync:
sync_callback = self._update_from_mpl_selector
@@ -306,7 +307,7 @@ def sync_callback(*args, **kwargs):
xy0[1], self.center.y + self.height / 2)

if self.angle.value != 0:
self._mpl_selector.rotation = self.angle.to_value('radian')
self._mpl_selector.rotation = self.angle.to_value('deg')

self._mpl_selector.set_active(active)
self._mpl_selector_callback = callback
21 changes: 11 additions & 10 deletions regions/shapes/rectangle.py
Original file line number Diff line number Diff line change
@@ -205,10 +205,10 @@ def as_artist(self, origin=(0, 0), **kwargs):
angle=angle, **mpl_kwargs)

def _update_from_mpl_selector(self, *args, **kwargs):
# _rect_properties replace _rect_bbox in matplotlib#19864
# "Note that if rotation != 0, ``xmin, ymin`` are interpreted as the
# _rect_properties replace _rect_bbox in matplotlib#19864, unchanged in #20839.
# "Note that if rotation != 0, ``xmin, ymin`` are always interpreted as the
# lower corner, and ``xmax, ymax`` are calculated using only width and
# height assuming no rotation."
# height assuming no rotation (as specified for ``selector.extents``)."

xmin, xmax, ymin, ymax = self._mpl_selector.extents
self.width = xmax - xmin
@@ -219,7 +219,7 @@ def _update_from_mpl_selector(self, *args, **kwargs):
else:
self.center = PixCoord(x=0.5 * (xmin + xmax), y=0.5 * (ymin + ymax))
rotation = 0
self.angle = rotation * u.radian
self.angle = rotation * u.deg

if getattr(self, '_mpl_selector_callback', None) is not None:
self._mpl_selector_callback(self)
@@ -267,13 +267,14 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None,
``selector.set_active(True)`` or ``selector.set_active(False)``.
"""
from matplotlib.widgets import RectangleSelector
from .._utils.optional_deps import MPL_VERSION
from .._utils.optional_deps import MPL_VERSION, MPL_VER_STR

if hasattr(self, '_mpl_selector'):
raise AttributeError('Cannot attach more than one selector to a region.')

if self.angle.value != 0 and not hasattr(RectangleSelector, 'rotation'):
raise NotImplementedError('Cannot create matplotlib selector for rotated rectangle.')
raise NotImplementedError('Creating selectors for rotated shapes is not '
f'yet supported with matplotlib {MPL_VER_STR}.')

if sync:
sync_callback = self._update_from_mpl_selector
@@ -294,12 +295,12 @@ def sync_callback(*args, **kwargs):

self._mpl_selector = RectangleSelector(ax, sync_callback, interactive=True, **kwargs)

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)
dxy = [self.width / 2, self.height / 2]
self._mpl_selector.extents = (self.center.x - dxy[0], self.center.x + dxy[0],
self.center.y - dxy[1], self.center.y + dxy[1])

if self.angle.value != 0:
self._mpl_selector.rotation = self.angle.to_value('radian')
self._mpl_selector.rotation = self.angle.to_value('deg')

self._mpl_selector.set_active(active)
self._mpl_selector_callback = callback
24 changes: 10 additions & 14 deletions regions/shapes/tests/test_ellipse.py
Original file line number Diff line number Diff line change
@@ -128,14 +128,18 @@ def update_mask(reg):
# works with rotated ellipses, the following exception check can
# be removed as well as the ``angle=0 * u.deg`` in the call to
# copy() below - should (hopefully) be implemented with mpl 3.6.
expected = [8.3, 4.9, 2.0, 1.0]
if MPL_VERSION < 36:
with pytest.raises(NotImplementedError,
match=('Cannot create matplotlib selector for rotated ellipse.')):
match='Creating selectors for rotated shapes is not yet supported'):
self.reg.as_mpl_selector(ax)
angle = 0 * u.deg
else:
angle = self.reg.angle

if not sync:
expected = [3, 4, 4, 3]

region = self.reg.copy(angle=angle)

selector = region.as_mpl_selector(ax, callback=update_mask, sync=sync) # noqa
@@ -146,24 +150,16 @@ def update_mask(reg):

ax.figure.canvas.draw()

if sync:
assert_allclose(region.center.x, expected[0])
assert_allclose(region.center.y, expected[1])
assert_allclose(region.width, expected[2])
assert_allclose(region.height, expected[3])

assert_allclose(region.center.x, 8.3)
assert_allclose(region.center.y, 4.9)
assert_allclose(region.width, 2)
assert_allclose(region.height, 1)
if sync:
assert_quantity_allclose(region.angle, 0 * u.deg)

assert_equal(mask, region.to_mask(mode='subpixels', subpixels=10).to_image(data.shape))

else:

assert_allclose(region.center.x, 3)
assert_allclose(region.center.y, 4)
assert_allclose(region.width, 4)
assert_allclose(region.height, 3)
assert_quantity_allclose(region.angle, angle)

assert_equal(mask, 0)

with pytest.raises(AttributeError, match=('Cannot attach more than one selector to a reg')):
22 changes: 10 additions & 12 deletions regions/shapes/tests/test_rectangle.py
Original file line number Diff line number Diff line change
@@ -134,15 +134,19 @@ def update_mask(reg):
# this works with rotated rectangles, the following exception
# check can be removed as well as the ``angle=0 * u.deg`` in the
# copy() below - should (hopefully) be implemented with mpl 3.6.
expected = [8.3, 4.9, 2.0, 1.0]
if MPL_VERSION < 36:
with pytest.raises(NotImplementedError,
match=('Cannot create matplotlib selector for rotated rectangle.')):
match='Creating selectors for rotated shapes is not yet supported'):
self.reg.as_mpl_selector(ax)

angle = 0 * u.deg
else:
angle = self.reg.angle

if not sync:
expected = [3, 4, 4, 3]

region = self.reg.copy(angle=angle)

selector = region.as_mpl_selector(ax, callback=update_mask, sync=sync) # noqa
@@ -153,22 +157,16 @@ def update_mask(reg):

ax.figure.canvas.draw()

assert_allclose(region.center.x, expected[0])
assert_allclose(region.center.y, expected[1])
assert_allclose(region.width, expected[2])
assert_allclose(region.height, expected[3])

if sync:
assert_allclose(region.center.x, 8.3)
assert_allclose(region.center.y, 4.9)
assert_allclose(region.width, 2)
assert_allclose(region.height, 1)
assert_quantity_allclose(region.angle, 0 * u.deg)

assert_equal(mask, region.to_mask(mode='subpixels', subpixels=10).to_image(data.shape))

else:
assert_allclose(region.center.x, 3)
assert_allclose(region.center.y, 4)
assert_allclose(region.width, 4)
assert_allclose(region.height, 3)
assert_quantity_allclose(region.angle, angle)

assert_equal(mask, 0)

with pytest.raises(AttributeError, match=('Cannot attach more than one selector to a reg')):

0 comments on commit 2ebeaca

Please sign in to comment.