@@ -1957,6 +1957,130 @@ def plot(self, xs, ys, *args, zdir='z', **kwargs):
19571957
19581958 plot3D = plot
19591959
1960+ def fill_between (self , x1 , y1 , z1 , x2 , y2 , z2 , * ,
1961+ where = None , mode = 'auto' , facecolors = None , shade = None ,
1962+ ** kwargs ):
1963+ """
1964+ Fill the area between two 3D curves.
1965+
1966+ The curves are defined by the points (*x1*, *y1*, *z1*) and
1967+ (*x2*, *y2*, *z2*). This creates one or multiple quadrangle
1968+ polygons that are filled. All points must be the same length N, or a
1969+ single value to be used for all points.
1970+
1971+ Parameters
1972+ ----------
1973+ x1, y1, z1 : float or 1D array-like
1974+ x, y, and z coordinates of vertices for 1st line.
1975+
1976+ x2, y2, z2 : float or 1D array-like
1977+ x, y, and z coordinates of vertices for 2nd line.
1978+
1979+ where : array of bool (length N), optional
1980+ Define *where* to exclude some regions from being filled. The
1981+ filled regions are defined by the coordinates ``pts[where]``,
1982+ for all x, y, and z pts. More precisely, fill between ``pts[i]``
1983+ and ``pts[i+1]`` if ``where[i] and where[i+1]``. Note that this
1984+ definition implies that an isolated *True* value between two
1985+ *False* values in *where* will not result in filling. Both sides of
1986+ the *True* position remain unfilled due to the adjacent *False*
1987+ values.
1988+
1989+ mode : {'quad', 'polygon', 'auto'}, default: 'auto'
1990+ The fill mode. One of:
1991+
1992+ - 'quad': A separate quadrilateral polygon is created for each
1993+ pair of subsequent points in the two lines.
1994+ - 'polygon': The two lines are connected to form a single polygon.
1995+ This is faster and can render more cleanly for simple shapes
1996+ (e.g. for filling between two lines that lie within a plane).
1997+ - 'auto': If the points all lie on the same 3D plane, 'polygon' is
1998+ used. Otherwise, 'quad' is used.
1999+
2000+ facecolors : list of :mpltype:`color`, default: None
2001+ Colors of each individual patch, or a single color to be used for
2002+ all patches.
2003+
2004+ shade : bool, default: None
2005+ Whether to shade the facecolors. If *None*, then defaults to *True*
2006+ for 'quad' mode and *False* for 'polygon' mode.
2007+
2008+ **kwargs
2009+ All other keyword arguments are passed on to `.Poly3DCollection`.
2010+
2011+ Returns
2012+ -------
2013+ `.Poly3DCollection`
2014+ A `.Poly3DCollection` containing the plotted polygons.
2015+
2016+ """
2017+ _api .check_in_list (['auto' , 'quad' , 'polygon' ], mode = mode )
2018+
2019+ had_data = self .has_data ()
2020+ x1 , y1 , z1 , x2 , y2 , z2 = cbook ._broadcast_with_masks (x1 , y1 , z1 , x2 , y2 , z2 )
2021+
2022+ if facecolors is None :
2023+ facecolors = [self ._get_patches_for_fill .get_next_color ()]
2024+ facecolors = list (mcolors .to_rgba_array (facecolors ))
2025+
2026+ if where is None :
2027+ where = True
2028+ else :
2029+ where = np .asarray (where , dtype = bool )
2030+ if where .size != x1 .size :
2031+ raise ValueError (f"where size ({ where .size } ) does not match "
2032+ f"size ({ x1 .size } )" )
2033+ where = where & ~ np .isnan (x1 ) # NaNs were broadcast in _broadcast_with_masks
2034+
2035+ if mode == 'auto' :
2036+ if art3d ._all_points_on_plane (np .concatenate ((x1 [where ], x2 [where ])),
2037+ np .concatenate ((y1 [where ], y2 [where ])),
2038+ np .concatenate ((z1 [where ], z2 [where ])),
2039+ atol = 1e-12 ):
2040+ mode = 'polygon'
2041+ else :
2042+ mode = 'quad'
2043+
2044+ if shade is None :
2045+ if mode == 'quad' :
2046+ shade = True
2047+ else :
2048+ shade = False
2049+
2050+ polys = []
2051+ for idx0 , idx1 in cbook .contiguous_regions (where ):
2052+ x1i = x1 [idx0 :idx1 ]
2053+ y1i = y1 [idx0 :idx1 ]
2054+ z1i = z1 [idx0 :idx1 ]
2055+ x2i = x2 [idx0 :idx1 ]
2056+ y2i = y2 [idx0 :idx1 ]
2057+ z2i = z2 [idx0 :idx1 ]
2058+
2059+ if not len (x1i ):
2060+ continue
2061+
2062+ if mode == 'quad' :
2063+ # Preallocate the array for the region's vertices, and fill it in
2064+ n_polys_i = len (x1i ) - 1
2065+ polys_i = np .empty ((n_polys_i , 4 , 3 ))
2066+ polys_i [:, 0 , :] = np .column_stack ((x1i [:- 1 ], y1i [:- 1 ], z1i [:- 1 ]))
2067+ polys_i [:, 1 , :] = np .column_stack ((x1i [1 :], y1i [1 :], z1i [1 :]))
2068+ polys_i [:, 2 , :] = np .column_stack ((x2i [1 :], y2i [1 :], z2i [1 :]))
2069+ polys_i [:, 3 , :] = np .column_stack ((x2i [:- 1 ], y2i [:- 1 ], z2i [:- 1 ]))
2070+ polys = polys + [* polys_i ]
2071+ elif mode == 'polygon' :
2072+ line1 = np .column_stack ((x1i , y1i , z1i ))
2073+ line2 = np .column_stack ((x2i [::- 1 ], y2i [::- 1 ], z2i [::- 1 ]))
2074+ poly = np .concatenate ((line1 , line2 ), axis = 0 )
2075+ polys .append (poly )
2076+
2077+ polyc = art3d .Poly3DCollection (polys , facecolors = facecolors , shade = shade ,
2078+ ** kwargs )
2079+ self .add_collection (polyc )
2080+
2081+ self .auto_scale_xyz ([x1 , x2 ], [y1 , y2 ], [z1 , z2 ], had_data )
2082+ return polyc
2083+
19602084 def plot_surface (self , X , Y , Z , * , norm = None , vmin = None ,
19612085 vmax = None , lightsource = None , ** kwargs ):
19622086 """
0 commit comments