Skip to content

Commit 1bca1db

Browse files
committed
Merge pull request matplotlib#4311 from tacaswell/bug_bbox_overlap_nan
BUG : bbox with any nan points can not overlap
2 parents 70b54aa + 8e5d338 commit 1bca1db

File tree

3 files changed

+110
-76
lines changed

3 files changed

+110
-76
lines changed

lib/matplotlib/tests/test_coding_standards.py

-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,6 @@ def test_pep8_conformance_installed_files():
217217
'tests/test_streamplot.py',
218218
'tests/test_subplots.py',
219219
'tests/test_tightlayout.py',
220-
'tests/test_transforms.py',
221220
'tests/test_triangulation.py',
222221
'compat/subprocess.py',
223222
'backends/__init__.py',

lib/matplotlib/tests/test_transforms.py

+108-75
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from nose.tools import assert_equal, assert_raises
1010
import numpy.testing as np_test
1111
from numpy.testing import assert_almost_equal, assert_array_equal
12-
from matplotlib.transforms import Affine2D, BlendedGenericTransform
12+
from matplotlib.transforms import Affine2D, BlendedGenericTransform, Bbox
1313
from matplotlib.path import Path
1414
from matplotlib.scale import LogScale
1515
from matplotlib.testing.decorators import cleanup, image_comparison
@@ -31,6 +31,7 @@ class AssertingNonAffineTransform(mtrans.Transform):
3131
"""
3232
input_dims = output_dims = 2
3333
is_affine = False
34+
3435
def __init__(self, *args, **kwargs):
3536
mtrans.Transform.__init__(self, *args, **kwargs)
3637
self.raise_on_transform = False
@@ -39,14 +40,14 @@ def __init__(self, *args, **kwargs):
3940
def transform_path_non_affine(self, path):
4041
if self.raise_on_transform:
4142
assert False, ('Invalidated affine part of transform '
42-
'unnecessarily.')
43+
'unnecessarily.')
4344
return self.underlying_transform.transform_path(path)
4445
transform_path = transform_path_non_affine
4546

4647
def transform_non_affine(self, path):
4748
if self.raise_on_transform:
4849
assert False, ('Invalidated affine part of transform '
49-
'unnecessarily.')
50+
'unnecessarily.')
5051
return self.underlying_transform.transform(path)
5152
transform = transform_non_affine
5253

@@ -81,8 +82,9 @@ def _as_mpl_transform(self, axes):
8182

8283
@image_comparison(baseline_images=['pre_transform_data'])
8384
def test_pre_transform_plotting():
84-
# a catch-all for as many as possible plot layouts which handle pre-transforming the data
85-
# NOTE: The axis range is important in this plot. It should be x10 what the data suggests it should be
85+
# a catch-all for as many as possible plot layouts which handle
86+
# pre-transforming the data NOTE: The axis range is important in this
87+
# plot. It should be x10 what the data suggests it should be
8688
ax = plt.axes()
8789
times10 = mtrans.Affine2D().scale(10)
8890

@@ -96,7 +98,6 @@ def test_pre_transform_plotting():
9698
ax.scatter(np.linspace(0, 10), np.linspace(10, 0),
9799
transform=times10 + ax.transData)
98100

99-
100101
x = np.linspace(8, 10, 20)
101102
y = np.linspace(1, 5, 20)
102103
u = 2*np.sin(x) + np.cos(y[:, np.newaxis])
@@ -119,10 +120,11 @@ def test_pre_transform_plotting():
119120
def test_contour_pre_transform_limits():
120121
ax = plt.axes()
121122
xs, ys = np.meshgrid(np.linspace(15, 20, 15), np.linspace(12.4, 12.5, 20))
122-
ax.contourf(xs, ys, np.log(xs * ys), transform=mtrans.Affine2D().scale(0.1) + ax.transData)
123+
ax.contourf(xs, ys, np.log(xs * ys),
124+
transform=mtrans.Affine2D().scale(0.1) + ax.transData)
123125

124-
expected = np.array([[ 1.5 , 1.24],
125-
[ 2. , 1.25]])
126+
expected = np.array([[1.5, 1.24],
127+
[2., 1.25]])
126128
assert_almost_equal(expected, ax.dataLim.get_points())
127129

128130

@@ -131,10 +133,11 @@ def test_pcolor_pre_transform_limits():
131133
# Based on test_contour_pre_transform_limits()
132134
ax = plt.axes()
133135
xs, ys = np.meshgrid(np.linspace(15, 20, 15), np.linspace(12.4, 12.5, 20))
134-
ax.pcolor(xs, ys, np.log(xs * ys), transform=mtrans.Affine2D().scale(0.1) + ax.transData)
136+
ax.pcolor(xs, ys, np.log(xs * ys),
137+
transform=mtrans.Affine2D().scale(0.1) + ax.transData)
135138

136-
expected = np.array([[ 1.5 , 1.24],
137-
[ 2. , 1.25]])
139+
expected = np.array([[1.5, 1.24],
140+
[2., 1.25]])
138141
assert_almost_equal(expected, ax.dataLim.get_points())
139142

140143

@@ -143,55 +146,56 @@ def test_pcolormesh_pre_transform_limits():
143146
# Based on test_contour_pre_transform_limits()
144147
ax = plt.axes()
145148
xs, ys = np.meshgrid(np.linspace(15, 20, 15), np.linspace(12.4, 12.5, 20))
146-
ax.pcolormesh(xs, ys, np.log(xs * ys), transform=mtrans.Affine2D().scale(0.1) + ax.transData)
149+
ax.pcolormesh(xs, ys, np.log(xs * ys),
150+
transform=mtrans.Affine2D().scale(0.1) + ax.transData)
147151

148-
expected = np.array([[ 1.5 , 1.24],
149-
[ 2. , 1.25]])
152+
expected = np.array([[1.5, 1.24],
153+
[2., 1.25]])
150154
assert_almost_equal(expected, ax.dataLim.get_points())
151155

152156

153157
def test_Affine2D_from_values():
154-
points = np.array([ [0,0],
155-
[10,20],
156-
[-1,0],
157-
])
158+
points = np.array([[0, 0],
159+
[10, 20],
160+
[-1, 0],
161+
])
158162

159-
t = mtrans.Affine2D.from_values(1,0,0,0,0,0)
163+
t = mtrans.Affine2D.from_values(1, 0, 0, 0, 0, 0)
160164
actual = t.transform(points)
161-
expected = np.array( [[0,0],[10,0],[-1,0]] )
162-
assert_almost_equal(actual,expected)
165+
expected = np.array([[0, 0], [10, 0], [-1, 0]])
166+
assert_almost_equal(actual, expected)
163167

164-
t = mtrans.Affine2D.from_values(0,2,0,0,0,0)
168+
t = mtrans.Affine2D.from_values(0, 2, 0, 0, 0, 0)
165169
actual = t.transform(points)
166-
expected = np.array( [[0,0],[0,20],[0,-2]] )
167-
assert_almost_equal(actual,expected)
170+
expected = np.array([[0, 0], [0, 20], [0, -2]])
171+
assert_almost_equal(actual, expected)
168172

169-
t = mtrans.Affine2D.from_values(0,0,3,0,0,0)
173+
t = mtrans.Affine2D.from_values(0, 0, 3, 0, 0, 0)
170174
actual = t.transform(points)
171-
expected = np.array( [[0,0],[60,0],[0,0]] )
172-
assert_almost_equal(actual,expected)
175+
expected = np.array([[0, 0], [60, 0], [0, 0]])
176+
assert_almost_equal(actual, expected)
173177

174-
t = mtrans.Affine2D.from_values(0,0,0,4,0,0)
178+
t = mtrans.Affine2D.from_values(0, 0, 0, 4, 0, 0)
175179
actual = t.transform(points)
176-
expected = np.array( [[0,0],[0,80],[0,0]] )
177-
assert_almost_equal(actual,expected)
180+
expected = np.array([[0, 0], [0, 80], [0, 0]])
181+
assert_almost_equal(actual, expected)
178182

179-
t = mtrans.Affine2D.from_values(0,0,0,0,5,0)
183+
t = mtrans.Affine2D.from_values(0, 0, 0, 0, 5, 0)
180184
actual = t.transform(points)
181-
expected = np.array( [[5,0],[5,0],[5,0]] )
182-
assert_almost_equal(actual,expected)
185+
expected = np.array([[5, 0], [5, 0], [5, 0]])
186+
assert_almost_equal(actual, expected)
183187

184-
t = mtrans.Affine2D.from_values(0,0,0,0,0,6)
188+
t = mtrans.Affine2D.from_values(0, 0, 0, 0, 0, 6)
185189
actual = t.transform(points)
186-
expected = np.array( [[0,6],[0,6],[0,6]] )
187-
assert_almost_equal(actual,expected)
190+
expected = np.array([[0, 6], [0, 6], [0, 6]])
191+
assert_almost_equal(actual, expected)
188192

189193

190194
def test_clipping_of_log():
191195
# issue 804
192-
M,L,C = Path.MOVETO, Path.LINETO, Path.CLOSEPOLY
193-
points = [ (0.2, -99), (0.4, -99), (0.4, 20), (0.2, 20), (0.2, -99) ]
194-
codes = [ M, L, L, L, C ]
196+
M, L, C = Path.MOVETO, Path.LINETO, Path.CLOSEPOLY
197+
points = [(0.2, -99), (0.4, -99), (0.4, 20), (0.2, 20), (0.2, -99)]
198+
codes = [M, L, L, L, C]
195199
path = Path(points, codes)
196200

197201
# something like this happens in plotting logarithmic histograms
@@ -206,7 +210,7 @@ def test_clipping_of_log():
206210
# Because y coordinate -99 is outside the clip zone, the first
207211
# line segment is effectively removed. That means that the closepoly
208212
# operation must be replaced by a move to the first point.
209-
assert np.allclose(tcodes, [ M, M, L, L, L, C ])
213+
assert np.allclose(tcodes, [M, M, L, L, L, C])
210214

211215

212216
class NonAffineForTest(mtrans.Transform):
@@ -238,9 +242,12 @@ def setUp(self):
238242
self.ta2 = mtrans.Affine2D(shorthand_name='ta2').translate(10, 0)
239243
self.ta3 = mtrans.Affine2D(shorthand_name='ta3').scale(1, 2)
240244

241-
self.tn1 = NonAffineForTest(mtrans.Affine2D().translate(1, 2), shorthand_name='tn1')
242-
self.tn2 = NonAffineForTest(mtrans.Affine2D().translate(1, 2), shorthand_name='tn2')
243-
self.tn3 = NonAffineForTest(mtrans.Affine2D().translate(1, 2), shorthand_name='tn3')
245+
self.tn1 = NonAffineForTest(mtrans.Affine2D().translate(1, 2),
246+
shorthand_name='tn1')
247+
self.tn2 = NonAffineForTest(mtrans.Affine2D().translate(1, 2),
248+
shorthand_name='tn2')
249+
self.tn3 = NonAffineForTest(mtrans.Affine2D().translate(1, 2),
250+
shorthand_name='tn3')
244251

245252
# creates a transform stack which looks like ((A, (N, A)), A)
246253
self.stack1 = (self.ta1 + (self.tn1 + self.ta2)) + self.ta3
@@ -280,8 +287,8 @@ def test_transform_shortcuts(self):
280287
self.assertEqual(self.stack2 - self.stack2_subset, self.ta1)
281288

282289
assert_equal((self.stack2_subset - self.stack2),
283-
self.ta1.inverted(),
284-
)
290+
self.ta1.inverted(),
291+
)
285292
assert_equal((self.stack2_subset - self.stack2).depth, 1)
286293

287294
assert_raises(ValueError, self.stack1.__sub__, self.stack2)
@@ -292,10 +299,13 @@ def test_transform_shortcuts(self):
292299
self.assertEqual(aff1 - aff2, self.ta1)
293300
self.assertEqual(aff1 - self.ta2, aff1 + self.ta2.inverted())
294301

295-
self.assertEqual(self.stack1 - self.ta3, self.ta1 + (self.tn1 + self.ta2))
296-
self.assertEqual(self.stack2 - self.ta3, self.ta1 + self.tn1 + self.ta2)
302+
self.assertEqual(self.stack1 - self.ta3,
303+
self.ta1 + (self.tn1 + self.ta2))
304+
self.assertEqual(self.stack2 - self.ta3,
305+
self.ta1 + self.tn1 + self.ta2)
297306

298-
self.assertEqual((self.ta2 + self.ta3) - self.ta3 + self.ta3, self.ta2 + self.ta3)
307+
self.assertEqual((self.ta2 + self.ta3) - self.ta3 + self.ta3,
308+
self.ta2 + self.ta3)
299309

300310
def test_contains_branch(self):
301311
r1 = (self.ta2 + self.ta1)
@@ -324,30 +334,35 @@ def test_contains_branch(self):
324334
self.assertFalse(self.stack1.contains_branch((self.tn1 + self.ta2)))
325335

326336
def test_affine_simplification(self):
327-
# tests that a transform stack only calls as much is absolutely necessary
328-
# "non-affine" allowing the best possible optimization with complex
329-
# transformation stacks.
330-
points = np.array([[0, 0], [10, 20], [np.nan, 1], [-1, 0]], dtype=np.float64)
337+
# tests that a transform stack only calls as much is absolutely
338+
# necessary "non-affine" allowing the best possible optimization with
339+
# complex transformation stacks.
340+
points = np.array([[0, 0], [10, 20], [np.nan, 1], [-1, 0]],
341+
dtype=np.float64)
331342
na_pts = self.stack1.transform_non_affine(points)
332343
all_pts = self.stack1.transform(points)
333344

334345
na_expected = np.array([[1., 2.], [-19., 12.],
335346
[np.nan, np.nan], [1., 1.]], dtype=np.float64)
336347
all_expected = np.array([[11., 4.], [-9., 24.],
337-
[np.nan, np.nan], [11., 2.]], dtype=np.float64)
348+
[np.nan, np.nan], [11., 2.]],
349+
dtype=np.float64)
338350

339351
# check we have the expected results from doing the affine part only
340352
np_test.assert_array_almost_equal(na_pts, na_expected)
341353
# check we have the expected results from a full transformation
342354
np_test.assert_array_almost_equal(all_pts, all_expected)
343-
# check we have the expected results from doing the transformation in two steps
344-
np_test.assert_array_almost_equal(self.stack1.transform_affine(na_pts), all_expected)
345-
# check that getting the affine transformation first, then fully transforming using that
346-
# yields the same result as before.
347-
np_test.assert_array_almost_equal(self.stack1.get_affine().transform(na_pts), all_expected)
348-
349-
# check that the affine part of stack1 & stack2 are equivalent (i.e. the optimization
350-
# is working)
355+
# check we have the expected results from doing the transformation in
356+
# two steps
357+
np_test.assert_array_almost_equal(self.stack1.transform_affine(na_pts),
358+
all_expected)
359+
# check that getting the affine transformation first, then fully
360+
# transforming using that yields the same result as before.
361+
np_test.assert_array_almost_equal(
362+
self.stack1.get_affine().transform(na_pts), all_expected)
363+
364+
# check that the affine part of stack1 & stack2 are equivalent
365+
# (i.e. the optimization is working)
351366
expected_result = (self.ta2 + self.ta3).get_matrix()
352367
result = self.stack1.get_affine().get_matrix()
353368
np_test.assert_array_equal(expected_result, result)
@@ -364,36 +379,47 @@ def test_line_extent_axes_coords(self):
364379
# a simple line in axes coordinates
365380
ax = plt.axes()
366381
ax.plot([0.1, 1.2, 0.8], [0.9, 0.5, 0.8], transform=ax.transAxes)
367-
np.testing.assert_array_equal(ax.dataLim.get_points(), np.array([[np.inf, np.inf], [-np.inf, -np.inf]]))
382+
np.testing.assert_array_equal(ax.dataLim.get_points(),
383+
np.array([[np.inf, np.inf],
384+
[-np.inf, -np.inf]]))
368385

369386
def test_line_extent_data_coords(self):
370387
# a simple line in data coordinates
371388
ax = plt.axes()
372389
ax.plot([0.1, 1.2, 0.8], [0.9, 0.5, 0.8], transform=ax.transData)
373-
np.testing.assert_array_equal(ax.dataLim.get_points(), np.array([[ 0.1, 0.5], [ 1.2, 0.9]]))
390+
np.testing.assert_array_equal(ax.dataLim.get_points(),
391+
np.array([[0.1, 0.5], [1.2, 0.9]]))
374392

375393
def test_line_extent_compound_coords1(self):
376-
# a simple line in data coordinates in the y component, and in axes coordinates in the x
394+
# a simple line in data coordinates in the y component, and in axes
395+
# coordinates in the x
377396
ax = plt.axes()
378397
trans = mtrans.blended_transform_factory(ax.transAxes, ax.transData)
379398
ax.plot([0.1, 1.2, 0.8], [35, -5, 18], transform=trans)
380-
np.testing.assert_array_equal(ax.dataLim.get_points(), np.array([[ np.inf, -5.], [ -np.inf, 35.]]))
399+
np.testing.assert_array_equal(ax.dataLim.get_points(),
400+
np.array([[np.inf, -5.],
401+
[-np.inf, 35.]]))
381402
plt.close()
382403

383404
def test_line_extent_predata_transform_coords(self):
384405
# a simple line in (offset + data) coordinates
385406
ax = plt.axes()
386407
trans = mtrans.Affine2D().scale(10) + ax.transData
387408
ax.plot([0.1, 1.2, 0.8], [35, -5, 18], transform=trans)
388-
np.testing.assert_array_equal(ax.dataLim.get_points(), np.array([[1., -50.], [12., 350.]]))
409+
np.testing.assert_array_equal(ax.dataLim.get_points(),
410+
np.array([[1., -50.], [12., 350.]]))
389411
plt.close()
390412

391413
def test_line_extent_compound_coords2(self):
392-
# a simple line in (offset + data) coordinates in the y component, and in axes coordinates in the x
414+
# a simple line in (offset + data) coordinates in the y component, and
415+
# in axes coordinates in the x
393416
ax = plt.axes()
394-
trans = mtrans.blended_transform_factory(ax.transAxes, mtrans.Affine2D().scale(10) + ax.transData)
417+
trans = mtrans.blended_transform_factory(
418+
ax.transAxes, mtrans.Affine2D().scale(10) + ax.transData)
395419
ax.plot([0.1, 1.2, 0.8], [35, -5, 18], transform=trans)
396-
np.testing.assert_array_equal(ax.dataLim.get_points(), np.array([[ np.inf, -50.], [ -np.inf, 350.]]))
420+
np.testing.assert_array_equal(
421+
ax.dataLim.get_points(),
422+
np.array([[np.inf, -50.], [-np.inf, 350.]]))
397423
plt.close()
398424

399425
def test_line_extents_affine(self):
@@ -418,7 +444,8 @@ def test_pathc_extents_non_affine(self):
418444
offset = mtrans.Affine2D().translate(10, 10)
419445
na_offset = NonAffineForTest(mtrans.Affine2D().translate(10, 10))
420446
pth = mpath.Path(np.array([[0, 0], [0, 10], [10, 10], [10, 0]]))
421-
patch = mpatches.PathPatch(pth, transform=offset + na_offset + ax.transData)
447+
patch = mpatches.PathPatch(pth,
448+
transform=offset + na_offset + ax.transData)
422449
ax.add_patch(patch)
423450
expeted_data_lim = np.array([[0., 0.], [10., 10.]]) + 20
424451
np.testing.assert_array_almost_equal(ax.dataLim.get_points(),
@@ -434,7 +461,6 @@ def test_pathc_extents_affine(self):
434461
np.testing.assert_array_almost_equal(ax.dataLim.get_points(),
435462
expeted_data_lim)
436463

437-
438464
def test_line_extents_for_non_affine_transData(self):
439465
ax = plt.axes(projection='polar')
440466
# add 10 to the radius of the data
@@ -499,9 +525,16 @@ def test_log_transform():
499525
# transform would fail if one of the axes was logarithmic).
500526
fig, ax = plt.subplots()
501527
ax.set_yscale('log')
502-
ax.transData.transform((1,1))
528+
ax.transData.transform((1, 1))
529+
530+
531+
@cleanup
532+
def test_nan_overlap():
533+
a = Bbox([[0, 0], [1, 1]])
534+
b = Bbox([[0, 0], [1, np.nan]])
535+
assert not a.overlaps(b)
503536

504537

505-
if __name__=='__main__':
538+
if __name__ == '__main__':
506539
import nose
507-
nose.runmodule(argv=['-s','--with-doctest'], exit=False)
540+
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

lib/matplotlib/transforms.py

+2
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,8 @@ def overlaps(self, other):
447447
"""
448448
ax1, ay1, ax2, ay2 = self._get_extents()
449449
bx1, by1, bx2, by2 = other._get_extents()
450+
if any(np.isnan(v) for v in [ax1, ay1, ax2, ay2, bx1, by1, bx2, by2]):
451+
return False
450452

451453
if ax2 < ax1:
452454
ax2, ax1 = ax1, ax2

0 commit comments

Comments
 (0)