Skip to content

Commit 0dce5a8

Browse files
committed
Switch to mpl #20839 rotation implementation
1 parent 2e72595 commit 0dce5a8

File tree

5 files changed

+44
-49
lines changed

5 files changed

+44
-49
lines changed

regions/_utils/optional_deps.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
try:
44
import matplotlib
55
HAS_MATPLOTLIB = True
6-
MPL_VERSION = getattr(matplotlib, '__version__', None)
7-
if MPL_VERSION is None:
8-
MPL_VERSION = matplotlib._version.version
9-
MPL_VERSION = MPL_VERSION.split('.')
6+
MPL_VER_STR = getattr(matplotlib, '__version__', None)
7+
if MPL_VER_STR is None:
8+
MPL_VER_STR = matplotlib._version.version
9+
MPL_VERSION = MPL_VER_STR.split('.')
1010
MPL_VERSION = 10 * int(MPL_VERSION[0]) + int(MPL_VERSION[1])
1111
except ImportError:
1212
HAS_MATPLOTLIB = False
13+
MPL_VER_STR = 'None'
1314
MPL_VERSION = 0

regions/shapes/ellipse.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,10 @@ def as_artist(self, origin=(0, 0), **kwargs):
212212
**mpl_kwargs)
213213

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

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

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

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

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

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

309309
if self.angle.value != 0:
310-
self._mpl_selector.rotation = self.angle.to_value('radian')
310+
self._mpl_selector.rotation = self.angle.to_value('deg')
311311

312312
self._mpl_selector.set_active(active)
313313
self._mpl_selector_callback = callback

regions/shapes/rectangle.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,10 @@ def as_artist(self, origin=(0, 0), **kwargs):
208208
angle=angle, **mpl_kwargs)
209209

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

216216
xmin, xmax, ymin, ymax = self._mpl_selector.extents
217217
self.width = xmax - xmin
@@ -222,7 +222,7 @@ def _update_from_mpl_selector(self, *args, **kwargs):
222222
else:
223223
self.center = PixCoord(x=0.5 * (xmin + xmax), y=0.5 * (ymin + ymax))
224224
rotation = 0
225-
self.angle = rotation * u.radian
225+
self.angle = rotation * u.deg
226226

227227
if getattr(self, '_mpl_selector_callback', None) is not None:
228228
self._mpl_selector_callback(self)
@@ -270,14 +270,14 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None,
270270
``selector.set_active(True)`` or ``selector.set_active(False)``.
271271
"""
272272
from matplotlib.widgets import RectangleSelector
273-
274-
from regions._utils.optional_deps import MPL_VERSION
273+
from regions._utils.optional_deps import MPL_VERSION, MPL_VER_STR
275274

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

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

282282
if sync:
283283
sync_callback = self._update_from_mpl_selector
@@ -298,12 +298,12 @@ def sync_callback(*args, **kwargs):
298298

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

301-
xy0 = [self.center.x - self.width / 2, self.center.y - self.height / 2]
302-
self._mpl_selector.extents = (xy0[0], self.center.x + self.width / 2,
303-
xy0[1], self.center.y + self.height / 2)
301+
dxy = [self.width / 2, self.height / 2]
302+
self._mpl_selector.extents = (self.center.x - dxy[0], self.center.x + dxy[0],
303+
self.center.y - dxy[1], self.center.y + dxy[1])
304304

305305
if self.angle.value != 0:
306-
self._mpl_selector.rotation = self.angle.to_value('radian')
306+
self._mpl_selector.rotation = self.angle.to_value('deg')
307307

308308
self._mpl_selector.set_active(active)
309309
self._mpl_selector_callback = callback

regions/shapes/tests/test_ellipse.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,18 @@ def update_mask(reg):
130130
# works with rotated ellipses, the following exception check can
131131
# be removed as well as the ``angle=0 * u.deg`` in the call to
132132
# copy() below - should (hopefully) be implemented with mpl 3.6.
133+
expected = [8.3, 4.9, 2.0, 1.0]
133134
if MPL_VERSION < 36:
134135
with pytest.raises(NotImplementedError,
135-
match=('Cannot create matplotlib selector for rotated ellipse.')):
136+
match='Creating selectors for rotated shapes is not yet supported'):
136137
self.reg.as_mpl_selector(ax)
137138
angle = 0 * u.deg
138139
else:
139140
angle = self.reg.angle
140141

142+
if not sync:
143+
expected = [3, 4, 4, 3]
144+
141145
region = self.reg.copy(angle=angle)
142146

143147
selector = region.as_mpl_selector(ax, callback=update_mask, sync=sync)
@@ -148,24 +152,16 @@ def update_mask(reg):
148152

149153
ax.figure.canvas.draw()
150154

151-
if sync:
155+
assert_allclose(region.center.x, expected[0])
156+
assert_allclose(region.center.y, expected[1])
157+
assert_allclose(region.width, expected[2])
158+
assert_allclose(region.height, expected[3])
152159

153-
assert_allclose(region.center.x, 8.3)
154-
assert_allclose(region.center.y, 4.9)
155-
assert_allclose(region.width, 2)
156-
assert_allclose(region.height, 1)
160+
if sync:
157161
assert_quantity_allclose(region.angle, 0 * u.deg)
158-
159162
assert_equal(mask, region.to_mask(mode='subpixels', subpixels=10).to_image(data.shape))
160-
161163
else:
162-
163-
assert_allclose(region.center.x, 3)
164-
assert_allclose(region.center.y, 4)
165-
assert_allclose(region.width, 4)
166-
assert_allclose(region.height, 3)
167164
assert_quantity_allclose(region.angle, angle)
168-
169165
assert_equal(mask, 0)
170166

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

regions/shapes/tests/test_rectangle.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,19 @@ def update_mask(reg):
136136
# this works with rotated rectangles, the following exception
137137
# check can be removed as well as the ``angle=0 * u.deg`` in the
138138
# copy() below - should (hopefully) be implemented with mpl 3.6.
139+
expected = [8.3, 4.9, 2.0, 1.0]
139140
if MPL_VERSION < 36:
140141
with pytest.raises(NotImplementedError,
141-
match=('Cannot create matplotlib selector for rotated rectangle.')):
142+
match='Creating selectors for rotated shapes is not yet supported'):
142143
self.reg.as_mpl_selector(ax)
143144

144145
angle = 0 * u.deg
145146
else:
146147
angle = self.reg.angle
147148

149+
if not sync:
150+
expected = [3, 4, 4, 3]
151+
148152
region = self.reg.copy(angle=angle)
149153

150154
selector = region.as_mpl_selector(ax, callback=update_mask, sync=sync)
@@ -155,22 +159,16 @@ def update_mask(reg):
155159

156160
ax.figure.canvas.draw()
157161

162+
assert_allclose(region.center.x, expected[0])
163+
assert_allclose(region.center.y, expected[1])
164+
assert_allclose(region.width, expected[2])
165+
assert_allclose(region.height, expected[3])
166+
158167
if sync:
159-
assert_allclose(region.center.x, 8.3)
160-
assert_allclose(region.center.y, 4.9)
161-
assert_allclose(region.width, 2)
162-
assert_allclose(region.height, 1)
163168
assert_quantity_allclose(region.angle, 0 * u.deg)
164-
165169
assert_equal(mask, region.to_mask(mode='subpixels', subpixels=10).to_image(data.shape))
166-
167170
else:
168-
assert_allclose(region.center.x, 3)
169-
assert_allclose(region.center.y, 4)
170-
assert_allclose(region.width, 4)
171-
assert_allclose(region.height, 3)
172171
assert_quantity_allclose(region.angle, angle)
173-
174172
assert_equal(mask, 0)
175173

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

0 commit comments

Comments
 (0)