Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3c85ef1

Browse files
committed
FIX: Handle masked arrays within PolyQuadMesh
Update the PolyQuadMesh handling to factor out the mask calculation and setting of vertices so we make sure we are updating properly when new arrays get passed in that may have a different mask.
1 parent ccb3966 commit 3c85ef1

File tree

2 files changed

+61
-25
lines changed

2 files changed

+61
-25
lines changed

lib/matplotlib/collections.py

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,24 +2242,38 @@ class PolyQuadMesh(_MeshData, PolyCollection):
22422242

22432243
def __init__(self, coordinates, **kwargs):
22442244
super().__init__(coordinates=coordinates)
2245-
X = coordinates[..., 0]
2246-
Y = coordinates[..., 1]
2247-
2248-
mask = np.ma.getmaskarray(X) | np.ma.getmaskarray(Y)
2249-
# We want the shape of the polygon, which is the corner of
2250-
# each X/Y array
2251-
mask = (mask[0:-1, 0:-1] | mask[1:, 1:] |
2252-
mask[0:-1, 1:] | mask[1:, 0:-1])
2253-
# Take account of the array data too
2254-
C = kwargs.get("array", np.empty(coordinates.shape[:2]))
2255-
if C.ndim == 3:
2256-
# RGB(A) case
2257-
mask |= np.any(np.ma.getmaskarray(C), axis=-1)
2258-
else:
2259-
mask |= np.ma.getmaskarray(C)
2245+
PolyCollection.__init__(self, verts=[], **kwargs)
2246+
# Setting the verts updates the paths of the PolyCollection
2247+
# This is called after the initializers to make sure the kwargs
2248+
# have all been processed and available for the masking calculations
2249+
self._set_unmasked_verts()
2250+
2251+
def _get_unmasked_polys(self):
2252+
"""Get the unmasked regions using the coordinates and array"""
2253+
# mask(X) | mask(Y)
2254+
mask = np.any(np.ma.getmaskarray(self._coordinates), axis=-1)
2255+
2256+
# We want the shape of the polygon, which is the corner of each X/Y array
2257+
mask = (mask[0:-1, 0:-1] | mask[1:, 1:] | mask[0:-1, 1:] | mask[1:, 0:-1])
2258+
2259+
# Take account of the array data too, expand if it is None
2260+
arr = self.get_array()
2261+
if arr is not None:
2262+
arr = np.ma.getmaskarray(arr)
2263+
if arr.ndim == 3:
2264+
# RGB(A) case
2265+
mask |= np.any(arr, axis=-1)
2266+
elif arr.ndim == 2:
2267+
mask |= arr
2268+
else:
2269+
mask |= arr.reshape(self._coordinates[:-1, :-1, :].shape[:2])
2270+
return ~mask
2271+
2272+
def _set_unmasked_verts(self):
2273+
X = self._coordinates[..., 0]
2274+
Y = self._coordinates[..., 1]
22602275

2261-
unmask = ~mask
2262-
self._valid_polys = unmask.ravel()
2276+
unmask = self._get_unmasked_polys()
22632277
X1 = np.ma.filled(X[:-1, :-1])[unmask]
22642278
Y1 = np.ma.filled(Y[:-1, :-1])[unmask]
22652279
X2 = np.ma.filled(X[1:, :-1])[unmask]
@@ -2272,25 +2286,34 @@ def __init__(self, coordinates, **kwargs):
22722286

22732287
xy = np.ma.stack([X1, Y1, X2, Y2, X3, Y3, X4, Y4, X1, Y1], axis=-1)
22742288
verts = xy.reshape((npoly, 5, 2))
2275-
# Setting the verts updates the paths of the PolyCollection
2276-
PolyCollection.__init__(self, verts=verts, **kwargs)
2289+
self.set_verts(verts)
22772290

22782291
def get_edgecolor(self):
22792292
# docstring inherited
22802293
# We only want to return the facecolors of the polygons
22812294
# that were drawn.
22822295
ec = super().get_edgecolor()
2283-
if len(ec) != len(self._valid_polys):
2296+
unmasked_polys = self._get_unmasked_polys().ravel()
2297+
if len(ec) != len(unmasked_polys):
22842298
# Mapping is off
22852299
return ec
2286-
return ec[self._valid_polys, :]
2300+
return ec[unmasked_polys, :]
22872301

22882302
def get_facecolor(self):
22892303
# docstring inherited
22902304
# We only want to return the facecolors of the polygons
22912305
# that were drawn.
22922306
fc = super().get_facecolor()
2293-
if len(fc) != len(self._valid_polys):
2307+
unmasked_polys = self._get_unmasked_polys().ravel()
2308+
if len(fc) != len(unmasked_polys):
22942309
# Mapping is off
22952310
return fc
2296-
return fc[self._valid_polys, :]
2311+
return fc[unmasked_polys, :]
2312+
2313+
def set_array(self, A):
2314+
prev_mask = self._get_unmasked_polys()
2315+
super().set_array(A)
2316+
# If the mask has changed at all we need to update
2317+
# the set of Polys that we are drawing
2318+
if not np.array_equal(prev_mask, self._get_unmasked_polys()):
2319+
self._set_unmasked_verts()

lib/matplotlib/tests/test_collections.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ def test_quadmesh_set_array_validation(pcfunc):
871871
coll = ax.pcolormesh(x, y, z, shading='gouraud')
872872

873873

874-
def test_polyquadmesh_masked_vertices():
874+
def test_polyquadmesh_masked_vertices_array():
875875
xx, yy = np.meshgrid([0, 1, 2], [0, 1, 2, 3])
876876
# 2 x 3 mesh data
877877
zz = (xx*yy)[:-1, :-1]
@@ -898,13 +898,26 @@ def test_polyquadmesh_masked_vertices():
898898

899899
# Mask the origin cell data
900900
zz = np.ma.masked_where((xx[:-1, :-1] == 0) & (yy[:-1, :-1] == 0), zz)
901-
polymesh = plt.pcolor(xx, yy, zz)
901+
polymesh = plt.pcolor(zz)
902902
polymesh.update_scalarmappable()
903903
# One cell should be left out
904904
assert len(polymesh.get_paths()) == 5
905905
# Poly version should have the same facecolors as the end of the quadmesh
906906
assert_array_equal(quadmesh_fc, polymesh.get_facecolor())
907907

908+
# We should also be able to call set_array with a new mask and get
909+
# updated polys
910+
# Remove mask, should add all polys back
911+
zz = np.arange(6).reshape((3, 2))
912+
polymesh.set_array(zz)
913+
polymesh.update_scalarmappable()
914+
assert len(polymesh.get_paths()) == 6
915+
# Add mask should remove polys
916+
zz = np.ma.masked_less(zz, 2)
917+
polymesh.set_array(zz)
918+
polymesh.update_scalarmappable()
919+
assert len(polymesh.get_paths()) == 4
920+
908921

909922
def test_quadmesh_get_coordinates(pcfunc):
910923
x = [0, 1, 2]

0 commit comments

Comments
 (0)