@@ -875,7 +875,7 @@ def update_scalarmappable(self):
875
875
# Allow possibility to call 'self.set_array(None)'.
876
876
if self ._A is not None :
877
877
# 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 ):
879
879
raise ValueError ('Collections can only map rank 1 arrays' )
880
880
if np .iterable (self ._alpha ):
881
881
if self ._alpha .size != self ._A .size :
@@ -1944,9 +1944,11 @@ def draw(self, renderer):
1944
1944
renderer .close_group (self .__class__ .__name__ )
1945
1945
1946
1946
1947
- class QuadMesh ( Collection ) :
1947
+ class _MeshData :
1948
1948
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.
1950
1952
1951
1953
A quadrilateral mesh is a grid of M by N adjacent quadrilaterals that are
1952
1954
defined via a (M+1, N+1) grid of vertices. The quadrilateral (m, n) is
@@ -1966,42 +1968,12 @@ class QuadMesh(Collection):
1966
1968
The vertices. ``coordinates[m, n]`` specifies the (x, y) coordinates
1967
1969
of vertex (m, n).
1968
1970
1969
- antialiased : bool, default: True
1970
-
1971
1971
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
-
1979
1972
"""
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' ):
1986
1974
_api .check_shape ((None , None , 2 ), coordinates = coordinates )
1987
1975
self ._coordinates = coordinates
1988
- self ._antialiased = antialiased
1989
1976
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
2005
1977
2006
1978
def set_array (self , A ):
2007
1979
"""
@@ -2040,9 +2012,6 @@ def set_array(self, A):
2040
2012
f"{ ' or ' .join (map (str , ok_shapes ))} , not { A .shape } " )
2041
2013
return super ().set_array (A )
2042
2014
2043
- def get_datalim (self , transData ):
2044
- return (self .get_transform () - transData ).transform_bbox (self ._bbox )
2045
-
2046
2015
def get_coordinates (self ):
2047
2016
"""
2048
2017
Return the vertices of the mesh as an (M+1, N+1, 2) array.
@@ -2053,6 +2022,18 @@ def get_coordinates(self):
2053
2022
"""
2054
2023
return self ._coordinates
2055
2024
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
+
2056
2037
@staticmethod
2057
2038
def _convert_mesh_to_paths (coordinates ):
2058
2039
"""
@@ -2112,6 +2093,64 @@ def _convert_mesh_to_triangles(self, coordinates):
2112
2093
2113
2094
return triangles , colors
2114
2095
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
+
2115
2154
@artist .allow_rasterization
2116
2155
def draw (self , renderer ):
2117
2156
if not self .get_visible ():
@@ -2168,8 +2207,41 @@ def get_cursor_data(self, event):
2168
2207
return None
2169
2208
2170
2209
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
+
2172
2243
def __init__ (self , coordinates , ** kwargs ):
2244
+ super ().__init__ (coordinates = coordinates )
2173
2245
X = coordinates [..., 0 ]
2174
2246
Y = coordinates [..., 1 ]
2175
2247
@@ -2183,6 +2255,7 @@ def __init__(self, coordinates, **kwargs):
2183
2255
mask |= np .ma .getmaskarray (C )
2184
2256
2185
2257
unmask = ~ mask
2258
+ self ._valid_polys = unmask .ravel ()
2186
2259
X1 = np .ma .filled (X [:- 1 , :- 1 ])[unmask ]
2187
2260
Y1 = np .ma .filled (Y [:- 1 , :- 1 ])[unmask ]
2188
2261
X2 = np .ma .filled (X [1 :, :- 1 ])[unmask ]
@@ -2195,5 +2268,25 @@ def __init__(self, coordinates, **kwargs):
2195
2268
2196
2269
xy = np .ma .stack ([X1 , Y1 , X2 , Y2 , X3 , Y3 , X4 , Y4 , X1 , Y1 ], axis = - 1 )
2197
2270
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