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

Skip to content

Commit 083689d

Browse files
committed
ENH: Add a PolyQuadMesh for 2d meshes as Polygons
This adds a new collection PolyQuadMesh that is a combination of a PolyCollection and QuadMesh, storing all of the coordinate data and arrays through QuadMesh, but drawing each quad individually through the PolyCollection.draw() method.
1 parent 31b374c commit 083689d

3 files changed

Lines changed: 69 additions & 48 deletions

File tree

lib/matplotlib/axes/_axes.py

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5787,9 +5787,13 @@ def _interp_grid(X):
57875787
"This may lead to incorrectly calculated cell "
57885788
"edges, in which case, please supply "
57895789
f"explicit cell edges to {funcname}.")
5790-
X = np.hstack((X[:, [0]] - dX[:, [0]],
5791-
X[:, :-1] + dX,
5792-
X[:, [-1]] + dX[:, [-1]]))
5790+
if isinstance(X, np.ma.core.MaskedArray):
5791+
hstack = np.ma.hstack
5792+
else:
5793+
hstack = np.hstack
5794+
X = hstack((X[:, [0]] - dX[:, [0]],
5795+
X[:, :-1] + dX,
5796+
X[:, [-1]] + dX[:, [-1]]))
57935797
else:
57945798
# This is just degenerate, but we can't reliably guess
57955799
# a dX if there is just one value.
@@ -5907,7 +5911,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
59075911
59085912
Returns
59095913
-------
5910-
`matplotlib.collections.Collection`
5914+
`matplotlib.collections.PolyQuadMesh`
59115915
59125916
Other Parameters
59135917
----------------
@@ -5925,7 +5929,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
59255929
59265930
**kwargs
59275931
Additionally, the following arguments are allowed. They are passed
5928-
along to the `~matplotlib.collections.PolyCollection` constructor:
5932+
along to the `~matplotlib.collections.PolyQuadMesh` constructor:
59295933
59305934
%(PolyCollection:kwdoc)s
59315935
@@ -5959,35 +5963,6 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
59595963
shading = shading.lower()
59605964
X, Y, C, shading = self._pcolorargs('pcolor', *args, shading=shading,
59615965
kwargs=kwargs)
5962-
Ny, Nx = X.shape
5963-
5964-
# convert to MA, if necessary.
5965-
C = ma.asarray(C)
5966-
X = ma.asarray(X)
5967-
Y = ma.asarray(Y)
5968-
5969-
mask = ma.getmaskarray(X) + ma.getmaskarray(Y)
5970-
xymask = (mask[0:-1, 0:-1] + mask[1:, 1:] +
5971-
mask[0:-1, 1:] + mask[1:, 0:-1])
5972-
# don't plot if C or any of the surrounding vertices are masked.
5973-
mask = ma.getmaskarray(C) + xymask
5974-
5975-
unmask = ~mask
5976-
X1 = ma.filled(X[:-1, :-1])[unmask]
5977-
Y1 = ma.filled(Y[:-1, :-1])[unmask]
5978-
X2 = ma.filled(X[1:, :-1])[unmask]
5979-
Y2 = ma.filled(Y[1:, :-1])[unmask]
5980-
X3 = ma.filled(X[1:, 1:])[unmask]
5981-
Y3 = ma.filled(Y[1:, 1:])[unmask]
5982-
X4 = ma.filled(X[:-1, 1:])[unmask]
5983-
Y4 = ma.filled(Y[:-1, 1:])[unmask]
5984-
npoly = len(X1)
5985-
5986-
xy = np.stack([X1, Y1, X2, Y2, X3, Y3, X4, Y4, X1, Y1], axis=-1)
5987-
verts = xy.reshape((npoly, 5, 2))
5988-
5989-
C = ma.filled(C[:Ny - 1, :Nx - 1])[unmask]
5990-
59915966
linewidths = (0.25,)
59925967
if 'linewidth' in kwargs:
59935968
kwargs['linewidths'] = kwargs.pop('linewidth')
@@ -6001,19 +5976,30 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
60015976
# unless the boundary is not stroked, in which case the
60025977
# default will be False; with unstroked boundaries, aa
60035978
# makes artifacts that are often disturbing.
6004-
if 'antialiased' in kwargs:
6005-
kwargs['antialiaseds'] = kwargs.pop('antialiased')
6006-
if 'antialiaseds' not in kwargs and cbook._str_lower_equal(ec, "none"):
6007-
kwargs['antialiaseds'] = False
5979+
if 'antialiaseds' in kwargs:
5980+
kwargs['antialiased'] = kwargs.pop('antialiaseds')
5981+
if 'antialiased' not in kwargs and cbook._str_lower_equal(ec, "none"):
5982+
kwargs['antialiased'] = False
60085983

60095984
kwargs.setdefault('snap', False)
60105985

6011-
collection = mcoll.PolyCollection(
6012-
verts, array=C, cmap=cmap, norm=norm, alpha=alpha, **kwargs)
6013-
collection._scale_norm(norm, vmin, vmax)
5986+
if (isinstance(X, np.ma.MaskedArray)
5987+
or isinstance(Y, np.ma.MaskedArray)):
5988+
stack = np.ma.stack
5989+
X = np.ma.asarray(X)
5990+
Y = np.ma.asarray(Y)
5991+
# For bounds collections later
5992+
x = X.compressed()
5993+
y = Y.compressed()
5994+
else:
5995+
stack = np.stack
5996+
x = X
5997+
y = Y
5998+
coords = stack([X, Y], axis=-1)
60145999

6015-
x = X.compressed()
6016-
y = Y.compressed()
6000+
collection = mcoll.PolyQuadMesh(
6001+
coords, array=C, cmap=cmap, norm=norm, alpha=alpha, **kwargs)
6002+
collection._scale_norm(norm, vmin, vmax)
60176003

60186004
# Transform from native to data coordinates?
60196005
t = collection._transform

lib/matplotlib/collections.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2166,3 +2166,39 @@ def get_cursor_data(self, event):
21662166
if contained and self.get_array() is not None:
21672167
return self.get_array().ravel()[info["ind"]]
21682168
return None
2169+
2170+
2171+
class PolyQuadMesh(PolyCollection, QuadMesh):
2172+
def __init__(self, coordinates, **kwargs):
2173+
QuadMesh.__init__(self, coordinates=coordinates, **kwargs)
2174+
# Need sizes set because we are using Polycollection.draw()
2175+
self._sizes = np.ones(len(self._coordinates))
2176+
C = self.get_array()
2177+
X = self._coordinates[..., 0]
2178+
Y = self._coordinates[..., 1]
2179+
2180+
mask = np.ma.getmaskarray(X) | np.ma.getmaskarray(Y)
2181+
xymask = (mask[0:-1, 0:-1] + mask[1:, 1:] +
2182+
mask[0:-1, 1:] + mask[1:, 0:-1])
2183+
# don't plot if C or any of the surrounding vertices are masked.
2184+
mask = np.ma.getmaskarray(C) | xymask
2185+
2186+
unmask = ~mask
2187+
X1 = np.ma.filled(X[:-1, :-1])[unmask]
2188+
Y1 = np.ma.filled(Y[:-1, :-1])[unmask]
2189+
X2 = np.ma.filled(X[1:, :-1])[unmask]
2190+
Y2 = np.ma.filled(Y[1:, :-1])[unmask]
2191+
X3 = np.ma.filled(X[1:, 1:])[unmask]
2192+
Y3 = np.ma.filled(Y[1:, 1:])[unmask]
2193+
X4 = np.ma.filled(X[:-1, 1:])[unmask]
2194+
Y4 = np.ma.filled(Y[:-1, 1:])[unmask]
2195+
npoly = len(X1)
2196+
2197+
xy = np.ma.stack([X1, Y1, X2, Y2, X3, Y3, X4, Y4, X1, Y1], axis=-1)
2198+
verts = xy.reshape((npoly, 5, 2))
2199+
self.set_verts(verts)
2200+
2201+
@artist.allow_rasterization
2202+
def draw(self, renderer):
2203+
# We want to use the PolyCollection's draw
2204+
return PolyCollection.draw(self, renderer)

lib/matplotlib/tests/test_collections.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,7 @@ def test_color_logic(pcfunc):
993993
pc.update_scalarmappable() # This is called in draw().
994994
# Define 2 reference "colors" here for multiple use.
995995
face_default = mcolors.to_rgba_array(pc._get_default_facecolor())
996-
mapped = pc.get_cmap()(pc.norm(z.ravel() if pcfunc == plt.pcolor else z))
996+
mapped = pc.get_cmap()(pc.norm(z))
997997
# GitHub issue #1302:
998998
assert mcolors.same_color(pc.get_edgecolor(), 'red')
999999
# Check setting attributes after initialization:
@@ -1023,7 +1023,7 @@ def test_color_logic(pcfunc):
10231023
assert mcolors.same_color(pc.get_edgecolor(), 'none')
10241024
assert mcolors.same_color(pc.get_facecolor(), face_default) # not mapped
10251025
# Turn it back on by restoring the array (must be 1D!):
1026-
pc.set_array(z.ravel() if pcfunc == plt.pcolor else z)
1026+
pc.set_array(z)
10271027
pc.update_scalarmappable()
10281028
assert np.array_equal(pc.get_facecolor(), mapped)
10291029
assert mcolors.same_color(pc.get_edgecolor(), 'none')
@@ -1071,9 +1071,8 @@ def test_LineCollection_args():
10711071
def test_array_wrong_dimensions():
10721072
z = np.arange(12).reshape(3, 4)
10731073
pc = plt.pcolor(z)
1074-
with pytest.raises(ValueError, match="^Collections can only map"):
1075-
pc.set_array(z)
1076-
pc.update_scalarmappable()
1074+
pc.set_array(z)
1075+
pc.update_scalarmappable()
10771076
pc = plt.pcolormesh(z)
10781077
pc.set_array(z) # 2D is OK for Quadmesh
10791078
pc.update_scalarmappable()

0 commit comments

Comments
 (0)