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

Skip to content

Commit 265995a

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 ae1b6bf commit 265995a

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
@@ -5772,9 +5772,13 @@ def _interp_grid(X):
57725772
"This may lead to incorrectly calculated cell "
57735773
"edges, in which case, please supply "
57745774
f"explicit cell edges to {funcname}.")
5775-
X = np.hstack((X[:, [0]] - dX[:, [0]],
5776-
X[:, :-1] + dX,
5777-
X[:, [-1]] + dX[:, [-1]]))
5775+
if isinstance(X, np.ma.core.MaskedArray):
5776+
hstack = np.ma.hstack
5777+
else:
5778+
hstack = np.hstack
5779+
X = hstack((X[:, [0]] - dX[:, [0]],
5780+
X[:, :-1] + dX,
5781+
X[:, [-1]] + dX[:, [-1]]))
57785782
else:
57795783
# This is just degenerate, but we can't reliably guess
57805784
# a dX if there is just one value.
@@ -5892,7 +5896,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
58925896
58935897
Returns
58945898
-------
5895-
`matplotlib.collections.Collection`
5899+
`matplotlib.collections.PolyQuadMesh`
58965900
58975901
Other Parameters
58985902
----------------
@@ -5910,7 +5914,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
59105914
59115915
**kwargs
59125916
Additionally, the following arguments are allowed. They are passed
5913-
along to the `~matplotlib.collections.PolyCollection` constructor:
5917+
along to the `~matplotlib.collections.PolyQuadMesh` constructor:
59145918
59155919
%(PolyCollection:kwdoc)s
59165920
@@ -5944,35 +5948,6 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
59445948
shading = shading.lower()
59455949
X, Y, C, shading = self._pcolorargs('pcolor', *args, shading=shading,
59465950
kwargs=kwargs)
5947-
Ny, Nx = X.shape
5948-
5949-
# convert to MA, if necessary.
5950-
C = ma.asarray(C)
5951-
X = ma.asarray(X)
5952-
Y = ma.asarray(Y)
5953-
5954-
mask = ma.getmaskarray(X) + ma.getmaskarray(Y)
5955-
xymask = (mask[0:-1, 0:-1] + mask[1:, 1:] +
5956-
mask[0:-1, 1:] + mask[1:, 0:-1])
5957-
# don't plot if C or any of the surrounding vertices are masked.
5958-
mask = ma.getmaskarray(C) + xymask
5959-
5960-
unmask = ~mask
5961-
X1 = ma.filled(X[:-1, :-1])[unmask]
5962-
Y1 = ma.filled(Y[:-1, :-1])[unmask]
5963-
X2 = ma.filled(X[1:, :-1])[unmask]
5964-
Y2 = ma.filled(Y[1:, :-1])[unmask]
5965-
X3 = ma.filled(X[1:, 1:])[unmask]
5966-
Y3 = ma.filled(Y[1:, 1:])[unmask]
5967-
X4 = ma.filled(X[:-1, 1:])[unmask]
5968-
Y4 = ma.filled(Y[:-1, 1:])[unmask]
5969-
npoly = len(X1)
5970-
5971-
xy = np.stack([X1, Y1, X2, Y2, X3, Y3, X4, Y4, X1, Y1], axis=-1)
5972-
verts = xy.reshape((npoly, 5, 2))
5973-
5974-
C = ma.filled(C[:Ny - 1, :Nx - 1])[unmask]
5975-
59765951
linewidths = (0.25,)
59775952
if 'linewidth' in kwargs:
59785953
kwargs['linewidths'] = kwargs.pop('linewidth')
@@ -5986,19 +5961,30 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
59865961
# unless the boundary is not stroked, in which case the
59875962
# default will be False; with unstroked boundaries, aa
59885963
# makes artifacts that are often disturbing.
5989-
if 'antialiased' in kwargs:
5990-
kwargs['antialiaseds'] = kwargs.pop('antialiased')
5991-
if 'antialiaseds' not in kwargs and cbook._str_lower_equal(ec, "none"):
5992-
kwargs['antialiaseds'] = False
5964+
if 'antialiaseds' in kwargs:
5965+
kwargs['antialiased'] = kwargs.pop('antialiaseds')
5966+
if 'antialiased' not in kwargs and cbook._str_lower_equal(ec, "none"):
5967+
kwargs['antialiased'] = False
59935968

59945969
kwargs.setdefault('snap', False)
59955970

5996-
collection = mcoll.PolyCollection(
5997-
verts, array=C, cmap=cmap, norm=norm, alpha=alpha, **kwargs)
5998-
collection._scale_norm(norm, vmin, vmax)
5971+
if (isinstance(X, np.ma.MaskedArray)
5972+
or isinstance(Y, np.ma.MaskedArray)):
5973+
stack = np.ma.stack
5974+
X = np.ma.asarray(X)
5975+
Y = np.ma.asarray(Y)
5976+
# For bounds collections later
5977+
x = X.compressed()
5978+
y = Y.compressed()
5979+
else:
5980+
stack = np.stack
5981+
x = X
5982+
y = Y
5983+
coords = stack([X, Y], axis=-1)
59995984

6000-
x = X.compressed()
6001-
y = Y.compressed()
5985+
collection = mcoll.PolyQuadMesh(
5986+
coords, array=C, cmap=cmap, norm=norm, alpha=alpha, **kwargs)
5987+
collection._scale_norm(norm, vmin, vmax)
60025988

60035989
# Transform from native to data coordinates?
60045990
t = collection._transform

lib/matplotlib/collections.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,3 +2111,39 @@ def get_cursor_data(self, event):
21112111
if contained and self.get_array() is not None:
21122112
return self.get_array().ravel()[info["ind"]]
21132113
return None
2114+
2115+
2116+
class PolyQuadMesh(PolyCollection, QuadMesh):
2117+
def __init__(self, coordinates, **kwargs):
2118+
QuadMesh.__init__(self, coordinates=coordinates, **kwargs)
2119+
# Need sizes set because we are using Polycollection.draw()
2120+
self._sizes = np.ones(len(self._coordinates))
2121+
C = self.get_array()
2122+
X = self._coordinates[..., 0]
2123+
Y = self._coordinates[..., 1]
2124+
2125+
mask = np.ma.getmaskarray(X) | np.ma.getmaskarray(Y)
2126+
xymask = (mask[0:-1, 0:-1] + mask[1:, 1:] +
2127+
mask[0:-1, 1:] + mask[1:, 0:-1])
2128+
# don't plot if C or any of the surrounding vertices are masked.
2129+
mask = np.ma.getmaskarray(C) | xymask
2130+
2131+
unmask = ~mask
2132+
X1 = np.ma.filled(X[:-1, :-1])[unmask]
2133+
Y1 = np.ma.filled(Y[:-1, :-1])[unmask]
2134+
X2 = np.ma.filled(X[1:, :-1])[unmask]
2135+
Y2 = np.ma.filled(Y[1:, :-1])[unmask]
2136+
X3 = np.ma.filled(X[1:, 1:])[unmask]
2137+
Y3 = np.ma.filled(Y[1:, 1:])[unmask]
2138+
X4 = np.ma.filled(X[:-1, 1:])[unmask]
2139+
Y4 = np.ma.filled(Y[:-1, 1:])[unmask]
2140+
npoly = len(X1)
2141+
2142+
xy = np.ma.stack([X1, Y1, X2, Y2, X3, Y3, X4, Y4, X1, Y1], axis=-1)
2143+
verts = xy.reshape((npoly, 5, 2))
2144+
self.set_verts(verts)
2145+
2146+
@artist.allow_rasterization
2147+
def draw(self, renderer):
2148+
# We want to use the PolyCollection's draw
2149+
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
@@ -992,7 +992,7 @@ def test_color_logic(pcfunc):
992992
pc.update_scalarmappable() # This is called in draw().
993993
# Define 2 reference "colors" here for multiple use.
994994
face_default = mcolors.to_rgba_array(pc._get_default_facecolor())
995-
mapped = pc.get_cmap()(pc.norm((z.ravel() if pcfunc == plt.pcolor else z)))
995+
mapped = pc.get_cmap()(pc.norm(z))
996996
# GitHub issue #1302:
997997
assert mcolors.same_color(pc.get_edgecolor(), 'red')
998998
# Check setting attributes after initialization:
@@ -1022,7 +1022,7 @@ def test_color_logic(pcfunc):
10221022
assert mcolors.same_color(pc.get_edgecolor(), 'none')
10231023
assert mcolors.same_color(pc.get_facecolor(), face_default) # not mapped
10241024
# Turn it back on by restoring the array (must be 1D!):
1025-
pc.set_array(z.ravel() if pcfunc == plt.pcolor else z)
1025+
pc.set_array(z)
10261026
pc.update_scalarmappable()
10271027
assert np.array_equal(pc.get_facecolor(), mapped)
10281028
assert mcolors.same_color(pc.get_edgecolor(), 'none')
@@ -1070,9 +1070,8 @@ def test_LineCollection_args():
10701070
def test_array_wrong_dimensions():
10711071
z = np.arange(12).reshape(3, 4)
10721072
pc = plt.pcolor(z)
1073-
with pytest.raises(ValueError, match="^Collections can only map"):
1074-
pc.set_array(z)
1075-
pc.update_scalarmappable()
1073+
pc.set_array(z)
1074+
pc.update_scalarmappable()
10761075
pc = plt.pcolormesh(z)
10771076
pc.set_array(z) # 2D is OK for Quadmesh
10781077
pc.update_scalarmappable()

0 commit comments

Comments
 (0)