@@ -1668,15 +1668,13 @@ def plot_surface(self, X, Y, Z, *, norm=None, vmin=None,
1668
1668
1669
1669
# note that the striding causes some polygons to have more coordinates
1670
1670
# than others
1671
- polyc = art3d .Poly3DCollection (polys , ** kwargs )
1672
1671
1673
1672
if fcolors is not None :
1674
- if shade :
1675
- colset = self ._shade_colors (
1676
- colset , self ._generate_normals (polys ), lightsource )
1677
- polyc .set_facecolors (colset )
1678
- polyc .set_edgecolors (colset )
1673
+ polyc = art3d .Poly3DCollection (
1674
+ polys , edgecolors = colset , facecolors = colset , shade = shade ,
1675
+ lightsource = lightsource , ** kwargs )
1679
1676
elif cmap :
1677
+ polyc = art3d .Poly3DCollection (polys , ** kwargs )
1680
1678
# can't always vectorize, because polys might be jagged
1681
1679
if isinstance (polys , np .ndarray ):
1682
1680
avg_z = polys [..., 2 ].mean (axis = - 1 )
@@ -1688,97 +1686,15 @@ def plot_surface(self, X, Y, Z, *, norm=None, vmin=None,
1688
1686
if norm is not None :
1689
1687
polyc .set_norm (norm )
1690
1688
else :
1691
- if shade :
1692
- colset = self ._shade_colors (
1693
- color , self ._generate_normals (polys ), lightsource )
1694
- else :
1695
- colset = color
1696
- polyc .set_facecolors (colset )
1689
+ polyc = art3d .Poly3DCollection (
1690
+ polys , facecolors = color , shade = shade ,
1691
+ lightsource = lightsource , ** kwargs )
1697
1692
1698
1693
self .add_collection (polyc )
1699
1694
self .auto_scale_xyz (X , Y , Z , had_data )
1700
1695
1701
1696
return polyc
1702
1697
1703
- def _generate_normals (self , polygons ):
1704
- """
1705
- Compute the normals of a list of polygons.
1706
-
1707
- Normals point towards the viewer for a face with its vertices in
1708
- counterclockwise order, following the right hand rule.
1709
-
1710
- Uses three points equally spaced around the polygon.
1711
- This normal of course might not make sense for polygons with more than
1712
- three points not lying in a plane, but it's a plausible and fast
1713
- approximation.
1714
-
1715
- Parameters
1716
- ----------
1717
- polygons : list of (M_i, 3) array-like, or (..., M, 3) array-like
1718
- A sequence of polygons to compute normals for, which can have
1719
- varying numbers of vertices. If the polygons all have the same
1720
- number of vertices and array is passed, then the operation will
1721
- be vectorized.
1722
-
1723
- Returns
1724
- -------
1725
- normals : (..., 3) array
1726
- A normal vector estimated for the polygon.
1727
- """
1728
- if isinstance (polygons , np .ndarray ):
1729
- # optimization: polygons all have the same number of points, so can
1730
- # vectorize
1731
- n = polygons .shape [- 2 ]
1732
- i1 , i2 , i3 = 0 , n // 3 , 2 * n // 3
1733
- v1 = polygons [..., i1 , :] - polygons [..., i2 , :]
1734
- v2 = polygons [..., i2 , :] - polygons [..., i3 , :]
1735
- else :
1736
- # The subtraction doesn't vectorize because polygons is jagged.
1737
- v1 = np .empty ((len (polygons ), 3 ))
1738
- v2 = np .empty ((len (polygons ), 3 ))
1739
- for poly_i , ps in enumerate (polygons ):
1740
- n = len (ps )
1741
- i1 , i2 , i3 = 0 , n // 3 , 2 * n // 3
1742
- v1 [poly_i , :] = ps [i1 , :] - ps [i2 , :]
1743
- v2 [poly_i , :] = ps [i2 , :] - ps [i3 , :]
1744
- return np .cross (v1 , v2 )
1745
-
1746
- def _shade_colors (self , color , normals , lightsource = None ):
1747
- """
1748
- Shade *color* using normal vectors given by *normals*.
1749
- *color* can also be an array of the same length as *normals*.
1750
- """
1751
- if lightsource is None :
1752
- # chosen for backwards-compatibility
1753
- lightsource = mcolors .LightSource (azdeg = 225 , altdeg = 19.4712 )
1754
-
1755
- with np .errstate (invalid = "ignore" ):
1756
- shade = ((normals / np .linalg .norm (normals , axis = 1 , keepdims = True ))
1757
- @ lightsource .direction )
1758
- mask = ~ np .isnan (shade )
1759
-
1760
- if mask .any ():
1761
- # convert dot product to allowed shading fractions
1762
- in_norm = mcolors .Normalize (- 1 , 1 )
1763
- out_norm = mcolors .Normalize (0.3 , 1 ).inverse
1764
-
1765
- def norm (x ):
1766
- return out_norm (in_norm (x ))
1767
-
1768
- shade [~ mask ] = 0
1769
-
1770
- color = mcolors .to_rgba_array (color )
1771
- # shape of color should be (M, 4) (where M is number of faces)
1772
- # shape of shade should be (M,)
1773
- # colors should have final shape of (M, 4)
1774
- alpha = color [:, 3 ]
1775
- colors = norm (shade )[:, np .newaxis ] * color
1776
- colors [:, 3 ] = alpha
1777
- else :
1778
- colors = np .asanyarray (color ).copy ()
1779
-
1780
- return colors
1781
-
1782
1698
def plot_wireframe (self , X , Y , Z , ** kwargs ):
1783
1699
"""
1784
1700
Plot a 3D wireframe.
@@ -1975,9 +1891,8 @@ def plot_trisurf(self, *args, color=None, norm=None, vmin=None, vmax=None,
1975
1891
zt = z [triangles ]
1976
1892
verts = np .stack ((xt , yt , zt ), axis = - 1 )
1977
1893
1978
- polyc = art3d .Poly3DCollection (verts , * args , ** kwargs )
1979
-
1980
1894
if cmap :
1895
+ polyc = art3d .Poly3DCollection (verts , * args , ** kwargs )
1981
1896
# average over the three points of each triangle
1982
1897
avg_z = verts [:, :, 2 ].mean (axis = 1 )
1983
1898
polyc .set_array (avg_z )
@@ -1986,12 +1901,9 @@ def plot_trisurf(self, *args, color=None, norm=None, vmin=None, vmax=None,
1986
1901
if norm is not None :
1987
1902
polyc .set_norm (norm )
1988
1903
else :
1989
- if shade :
1990
- normals = self ._generate_normals (verts )
1991
- colset = self ._shade_colors (color , normals , lightsource )
1992
- else :
1993
- colset = color
1994
- polyc .set_facecolors (colset )
1904
+ polyc = art3d .Poly3DCollection (
1905
+ verts , * args , shade = shade , lightsource = lightsource ,
1906
+ facecolors = color , ** kwargs )
1995
1907
1996
1908
self .add_collection (polyc )
1997
1909
self .auto_scale_xyz (tri .x , tri .y , z , had_data )
@@ -2036,13 +1948,10 @@ def _3d_extend_contour(self, cset, stride=5):
2036
1948
2037
1949
# all polygons have 4 vertices, so vectorize
2038
1950
polyverts = np .array (polyverts )
2039
- normals = self ._generate_normals (polyverts )
2040
-
2041
- colors = self ._shade_colors (color , normals )
2042
- colors2 = self ._shade_colors (color , normals )
2043
1951
polycol = art3d .Poly3DCollection (polyverts ,
2044
- facecolors = colors ,
2045
- edgecolors = colors2 )
1952
+ facecolors = color ,
1953
+ edgecolors = color ,
1954
+ shade = True )
2046
1955
polycol .set_sort_zpos (z )
2047
1956
self .add_collection3d (polycol )
2048
1957
@@ -2587,15 +2496,11 @@ def bar3d(self, x, y, z, dx, dy, dz, color=None,
2587
2496
if len (facecolors ) < len (x ):
2588
2497
facecolors *= (6 * len (x ))
2589
2498
2590
- if shade :
2591
- normals = self ._generate_normals (polys )
2592
- sfacecolors = self ._shade_colors (facecolors , normals , lightsource )
2593
- else :
2594
- sfacecolors = facecolors
2595
-
2596
2499
col = art3d .Poly3DCollection (polys ,
2597
2500
zsort = zsort ,
2598
- facecolor = sfacecolors ,
2501
+ facecolors = facecolors ,
2502
+ shade = shade ,
2503
+ lightsource = lightsource ,
2599
2504
* args , ** kwargs )
2600
2505
self .add_collection (col )
2601
2506
@@ -2964,16 +2869,10 @@ def permutation_matrices(n):
2964
2869
# shade the faces
2965
2870
facecolor = facecolors [coord ]
2966
2871
edgecolor = edgecolors [coord ]
2967
- if shade :
2968
- normals = self ._generate_normals (faces )
2969
- facecolor = self ._shade_colors (facecolor , normals , lightsource )
2970
- if edgecolor is not None :
2971
- edgecolor = self ._shade_colors (
2972
- edgecolor , normals , lightsource
2973
- )
2974
2872
2975
2873
poly = art3d .Poly3DCollection (
2976
- faces , facecolors = facecolor , edgecolors = edgecolor , ** kwargs )
2874
+ faces , facecolors = facecolor , edgecolors = edgecolor ,
2875
+ shade = shade , lightsource = lightsource , ** kwargs )
2977
2876
self .add_collection3d (poly )
2978
2877
polygons [coord ] = poly
2979
2878
0 commit comments