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

Skip to content

Commit 474cd31

Browse files
committed
MNT: Add a mixin MeshData class for Collections
This adds a private _MeshData mixin class to help handle mesh coordinates and array data validation in a common place.
1 parent 5b40e07 commit 474cd31

File tree

2 files changed

+196
-64
lines changed

2 files changed

+196
-64
lines changed

lib/matplotlib/collections.py

Lines changed: 133 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ def update_scalarmappable(self):
875875
# Allow possibility to call 'self.set_array(None)'.
876876
if self._A is not None:
877877
# QuadMesh can map 2d arrays (but pcolormesh supplies 1d array)
878-
if self._A.ndim > 1 and not isinstance(self, QuadMesh):
878+
if self._A.ndim > 1 and not isinstance(self, _MeshData):
879879
raise ValueError('Collections can only map rank 1 arrays')
880880
if np.iterable(self._alpha):
881881
if self._alpha.size != self._A.size:
@@ -1944,9 +1944,11 @@ def draw(self, renderer):
19441944
renderer.close_group(self.__class__.__name__)
19451945

19461946

1947-
class QuadMesh(Collection):
1947+
class _MeshData:
19481948
r"""
1949-
Class for the efficient drawing of a quadrilateral mesh.
1949+
Class for managing the two dimensional coordinates of Quadrilateral meshes
1950+
and the associated data with them. This class is a mixin and is intended to
1951+
be used with another collection that will implement the draw separately.
19501952
19511953
A quadrilateral mesh is a grid of M by N adjacent quadrilaterals that are
19521954
defined via a (M+1, N+1) grid of vertices. The quadrilateral (m, n) is
@@ -1966,42 +1968,12 @@ class QuadMesh(Collection):
19661968
The vertices. ``coordinates[m, n]`` specifies the (x, y) coordinates
19671969
of vertex (m, n).
19681970
1969-
antialiased : bool, default: True
1970-
19711971
shading : {'flat', 'gouraud'}, default: 'flat'
1972-
1973-
Notes
1974-
-----
1975-
Unlike other `.Collection`\s, the default *pickradius* of `.QuadMesh` is 0,
1976-
i.e. `~.Artist.contains` checks whether the test point is within any of the
1977-
mesh quadrilaterals.
1978-
19791972
"""
1980-
1981-
def __init__(self, coordinates, *, antialiased=True, shading='flat',
1982-
**kwargs):
1983-
kwargs.setdefault("pickradius", 0)
1984-
# end of signature deprecation code
1985-
1973+
def __init__(self, coordinates, *, shading='flat'):
19861974
_api.check_shape((None, None, 2), coordinates=coordinates)
19871975
self._coordinates = coordinates
1988-
self._antialiased = antialiased
19891976
self._shading = shading
1990-
self._bbox = transforms.Bbox.unit()
1991-
self._bbox.update_from_data_xy(self._coordinates.reshape(-1, 2))
1992-
# super init delayed after own init because array kwarg requires
1993-
# self._coordinates and self._shading
1994-
super().__init__(**kwargs)
1995-
self.set_mouseover(False)
1996-
1997-
def get_paths(self):
1998-
if self._paths is None:
1999-
self.set_paths()
2000-
return self._paths
2001-
2002-
def set_paths(self):
2003-
self._paths = self._convert_mesh_to_paths(self._coordinates)
2004-
self.stale = True
20051977

20061978
def set_array(self, A):
20071979
"""
@@ -2040,9 +2012,6 @@ def set_array(self, A):
20402012
f"{' or '.join(map(str, ok_shapes))}, not {A.shape}")
20412013
return super().set_array(A)
20422014

2043-
def get_datalim(self, transData):
2044-
return (self.get_transform() - transData).transform_bbox(self._bbox)
2045-
20462015
def get_coordinates(self):
20472016
"""
20482017
Return the vertices of the mesh as an (M+1, N+1, 2) array.
@@ -2053,6 +2022,18 @@ def get_coordinates(self):
20532022
"""
20542023
return self._coordinates
20552024

2025+
def get_edgecolor(self):
2026+
# docstring inherited
2027+
# Note that we want to return an array of shape (N*M, 4)
2028+
# a flattened RGBA collection
2029+
return super().get_edgecolor().reshape(-1, 4)
2030+
2031+
def get_facecolor(self):
2032+
# docstring inherited
2033+
# Note that we want to return an array of shape (N*M, 4)
2034+
# a flattened RGBA collection
2035+
return super().get_facecolor().reshape(-1, 4)
2036+
20562037
@staticmethod
20572038
def _convert_mesh_to_paths(coordinates):
20582039
"""
@@ -2112,6 +2093,64 @@ def _convert_mesh_to_triangles(self, coordinates):
21122093

21132094
return triangles, colors
21142095

2096+
2097+
class QuadMesh(_MeshData, Collection):
2098+
r"""
2099+
Class for the efficient drawing of a quadrilateral mesh.
2100+
2101+
A quadrilateral mesh is a grid of M by N adjacent quadrilaterals that are
2102+
defined via a (M+1, N+1) grid of vertices. The quadrilateral (m, n) is
2103+
defined by the vertices ::
2104+
2105+
(m+1, n) ----------- (m+1, n+1)
2106+
/ /
2107+
/ /
2108+
/ /
2109+
(m, n) -------- (m, n+1)
2110+
2111+
The mesh need not be regular and the polygons need not be convex.
2112+
2113+
Parameters
2114+
----------
2115+
coordinates : (M+1, N+1, 2) array-like
2116+
The vertices. ``coordinates[m, n]`` specifies the (x, y) coordinates
2117+
of vertex (m, n).
2118+
2119+
antialiased : bool, default: True
2120+
2121+
shading : {'flat', 'gouraud'}, default: 'flat'
2122+
2123+
Notes
2124+
-----
2125+
Unlike other `.Collection`\s, the default *pickradius* of `.QuadMesh` is 0,
2126+
i.e. `~.Artist.contains` checks whether the test point is within any of the
2127+
mesh quadrilaterals.
2128+
2129+
"""
2130+
2131+
def __init__(self, coordinates, *, antialiased=True, shading='flat',
2132+
**kwargs):
2133+
kwargs.setdefault("pickradius", 0)
2134+
super().__init__(coordinates=coordinates, shading=shading)
2135+
Collection.__init__(self, **kwargs)
2136+
2137+
self._antialiased = antialiased
2138+
self._bbox = transforms.Bbox.unit()
2139+
self._bbox.update_from_data_xy(self._coordinates.reshape(-1, 2))
2140+
self.set_mouseover(False)
2141+
2142+
def get_paths(self):
2143+
if self._paths is None:
2144+
self.set_paths()
2145+
return self._paths
2146+
2147+
def set_paths(self):
2148+
self._paths = self._convert_mesh_to_paths(self._coordinates)
2149+
self.stale = True
2150+
2151+
def get_datalim(self, transData):
2152+
return (self.get_transform() - transData).transform_bbox(self._bbox)
2153+
21152154
@artist.allow_rasterization
21162155
def draw(self, renderer):
21172156
if not self.get_visible():
@@ -2168,8 +2207,41 @@ def get_cursor_data(self, event):
21682207
return None
21692208

21702209

2171-
class PolyQuadMesh(PolyCollection, QuadMesh):
2210+
class PolyQuadMesh(_MeshData, PolyCollection):
2211+
"""
2212+
Class for drawing a quadrilateral mesh as individual Polygons.
2213+
2214+
A quadrilateral mesh is a grid of M by N adjacent quadrilaterals that are
2215+
defined via a (M+1, N+1) grid of vertices. The quadrilateral (m, n) is
2216+
defined by the vertices ::
2217+
2218+
(m+1, n) ----------- (m+1, n+1)
2219+
/ /
2220+
/ /
2221+
/ /
2222+
(m, n) -------- (m, n+1)
2223+
2224+
The mesh need not be regular and the polygons need not be convex.
2225+
2226+
Parameters
2227+
----------
2228+
coordinates : (M+1, N+1, 2) array-like
2229+
The vertices. ``coordinates[m, n]`` specifies the (x, y) coordinates
2230+
of vertex (m, n).
2231+
2232+
Notes
2233+
-----
2234+
Unlike `.QuadMesh`, this class will draw each cell as an individual Polygon.
2235+
This is significantly slower, but allows for more flexibility when wanting
2236+
to add additional properties to the cells, such as hatching.
2237+
2238+
Another difference from `.QuadMesh` is that if any of the vertices or data
2239+
of a cell are masked, that Polygon will **not** be drawn and it won't be in
2240+
the list of paths returned.
2241+
"""
2242+
21722243
def __init__(self, coordinates, **kwargs):
2244+
super().__init__(coordinates=coordinates)
21732245
X = coordinates[..., 0]
21742246
Y = coordinates[..., 1]
21752247

@@ -2183,6 +2255,7 @@ def __init__(self, coordinates, **kwargs):
21832255
mask |= np.ma.getmaskarray(C)
21842256

21852257
unmask = ~mask
2258+
self._valid_polys = unmask.ravel()
21862259
X1 = np.ma.filled(X[:-1, :-1])[unmask]
21872260
Y1 = np.ma.filled(Y[:-1, :-1])[unmask]
21882261
X2 = np.ma.filled(X[1:, :-1])[unmask]
@@ -2195,5 +2268,25 @@ def __init__(self, coordinates, **kwargs):
21952268

21962269
xy = np.ma.stack([X1, Y1, X2, Y2, X3, Y3, X4, Y4, X1, Y1], axis=-1)
21972270
verts = xy.reshape((npoly, 5, 2))
2198-
# We need both verts and coordinates here to go through the super chain
2199-
super().__init__(coordinates=coordinates, verts=verts, **kwargs)
2271+
# Setting the verts updates the paths of the PolyCollection
2272+
PolyCollection.__init__(self, verts=verts, **kwargs)
2273+
2274+
def get_edgecolor(self):
2275+
# docstring inherited
2276+
# We only want to return the facecolors of the polygons
2277+
# that were drawn.
2278+
ec = super().get_edgecolor()
2279+
if len(ec) != len(self._valid_polys):
2280+
# Mapping is off
2281+
return ec
2282+
return ec[self._valid_polys, :]
2283+
2284+
def get_facecolor(self):
2285+
# docstring inherited
2286+
# We only want to return the facecolors of the polygons
2287+
# that were drawn.
2288+
fc = super().get_facecolor()
2289+
if len(fc) != len(self._valid_polys):
2290+
# Mapping is off
2291+
return fc
2292+
return fc[self._valid_polys, :]

0 commit comments

Comments
 (0)