@@ -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