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

Skip to content

Commit af35462

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 4bdae2e commit af35462

File tree

3 files changed

+69
-48
lines changed

3 files changed

+69
-48
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5806,9 +5806,13 @@ def _interp_grid(X):
58065806
"This may lead to incorrectly calculated cell "
58075807
"edges, in which case, please supply "
58085808
f"explicit cell edges to {funcname}.")
5809-
X = np.hstack((X[:, [0]] - dX[:, [0]],
5810-
X[:, :-1] + dX,
5811-
X[:, [-1]] + dX[:, [-1]]))
5809+
if isinstance(X, np.ma.core.MaskedArray):
5810+
hstack = np.ma.hstack
5811+
else:
5812+
hstack = np.hstack
5813+
X = hstack((X[:, [0]] - dX[:, [0]],
5814+
X[:, :-1] + dX,
5815+
X[:, [-1]] + dX[:, [-1]]))
58125816
else:
58135817
# This is just degenerate, but we can't reliably guess
58145818
# a dX if there is just one value.
@@ -5926,7 +5930,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
59265930
59275931
Returns
59285932
-------
5929-
`matplotlib.collections.Collection`
5933+
`matplotlib.collections.PolyQuadMesh`
59305934
59315935
Other Parameters
59325936
----------------
@@ -5944,7 +5948,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
59445948
59455949
**kwargs
59465950
Additionally, the following arguments are allowed. They are passed
5947-
along to the `~matplotlib.collections.PolyCollection` constructor:
5951+
along to the `~matplotlib.collections.PolyQuadMesh` constructor:
59485952
59495953
%(PolyCollection:kwdoc)s
59505954
@@ -5978,35 +5982,6 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
59785982
shading = shading.lower()
59795983
X, Y, C, shading = self._pcolorargs('pcolor', *args, shading=shading,
59805984
kwargs=kwargs)
5981-
Ny, Nx = X.shape
5982-
5983-
# convert to MA, if necessary.
5984-
C = ma.asarray(C)
5985-
X = ma.asarray(X)
5986-
Y = ma.asarray(Y)
5987-
5988-
mask = ma.getmaskarray(X) + ma.getmaskarray(Y)
5989-
xymask = (mask[0:-1, 0:-1] + mask[1:, 1:] +
5990-
mask[0:-1, 1:] + mask[1:, 0:-1])
5991-
# don't plot if C or any of the surrounding vertices are masked.
5992-
mask = ma.getmaskarray(C) + xymask
5993-
5994-
unmask = ~mask
5995-
X1 = ma.filled(X[:-1, :-1])[unmask]
5996-
Y1 = ma.filled(Y[:-1, :-1])[unmask]
5997-
X2 = ma.filled(X[1:, :-1])[unmask]
5998-
Y2 = ma.filled(Y[1:, :-1])[unmask]
5999-
X3 = ma.filled(X[1:, 1:])[unmask]
6000-
Y3 = ma.filled(Y[1:, 1:])[unmask]
6001-
X4 = ma.filled(X[:-1, 1:])[unmask]
6002-
Y4 = ma.filled(Y[:-1, 1:])[unmask]
6003-
npoly = len(X1)
6004-
6005-
xy = np.stack([X1, Y1, X2, Y2, X3, Y3, X4, Y4, X1, Y1], axis=-1)
6006-
verts = xy.reshape((npoly, 5, 2))
6007-
6008-
C = ma.filled(C[:Ny - 1, :Nx - 1])[unmask]
6009-
60105985
linewidths = (0.25,)
60115986
if 'linewidth' in kwargs:
60125987
kwargs['linewidths'] = kwargs.pop('linewidth')
@@ -6020,19 +5995,30 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
60205995
# unless the boundary is not stroked, in which case the
60215996
# default will be False; with unstroked boundaries, aa
60225997
# makes artifacts that are often disturbing.
6023-
if 'antialiased' in kwargs:
6024-
kwargs['antialiaseds'] = kwargs.pop('antialiased')
6025-
if 'antialiaseds' not in kwargs and cbook._str_lower_equal(ec, "none"):
6026-
kwargs['antialiaseds'] = False
5998+
if 'antialiaseds' in kwargs:
5999+
kwargs['antialiased'] = kwargs.pop('antialiaseds')
6000+
if 'antialiased' not in kwargs and cbook._str_lower_equal(ec, "none"):
6001+
kwargs['antialiased'] = False
60276002

60286003
kwargs.setdefault('snap', False)
60296004

6030-
collection = mcoll.PolyCollection(
6031-
verts, array=C, cmap=cmap, norm=norm, alpha=alpha, **kwargs)
6032-
collection._scale_norm(norm, vmin, vmax)
6005+
if (isinstance(X, np.ma.MaskedArray)
6006+
or isinstance(Y, np.ma.MaskedArray)):
6007+
stack = np.ma.stack
6008+
X = np.ma.asarray(X)
6009+
Y = np.ma.asarray(Y)
6010+
# For bounds collections later
6011+
x = X.compressed()
6012+
y = Y.compressed()
6013+
else:
6014+
stack = np.stack
6015+
x = X
6016+
y = Y
6017+
coords = stack([X, Y], axis=-1)
60336018

6034-
x = X.compressed()
6035-
y = Y.compressed()
6019+
collection = mcoll.PolyQuadMesh(
6020+
coords, array=C, cmap=cmap, norm=norm, alpha=alpha, **kwargs)
6021+
collection._scale_norm(norm, vmin, vmax)
60366022

60376023
# Transform from native to data coordinates?
60386024
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)