@@ -1683,72 +1683,60 @@ def plot_surface(self, X, Y, Z, *args, **kwargs):
1683
1683
if shade and cmap is not None and fcolors is not None :
1684
1684
fcolors = self ._shade_colors_lightsource (Z , cmap , lightsource )
1685
1685
1686
+ # evenly spaced, and including both endpoints
1687
+ row_inds = list (range (0 , rows - 1 , rstride )) + [rows - 1 ]
1688
+ col_inds = list (range (0 , cols - 1 , cstride )) + [cols - 1 ]
1689
+
1690
+ colset = [] # the sampled facecolor
1686
1691
polys = []
1687
- # Only need these vectors to shade if there is no cmap
1688
- if cmap is None and shade :
1689
- totpts = int (np .ceil ((rows - 1 ) / rstride ) *
1690
- np .ceil ((cols - 1 ) / cstride ))
1691
- v1 = np .empty ((totpts , 3 ))
1692
- v2 = np .empty ((totpts , 3 ))
1693
- # This indexes the vertex points
1694
- which_pt = 0
1695
-
1696
-
1697
- #colset contains the data for coloring: either average z or the facecolor
1698
- colset = []
1699
- for rs in range (0 , rows - 1 , rstride ):
1700
- for cs in range (0 , cols - 1 , cstride ):
1701
- ps = []
1702
- for a in (X , Y , Z ):
1703
- ztop = a [rs ,cs :min (cols , cs + cstride + 1 )]
1704
- zleft = a [rs + 1 :min (rows , rs + rstride + 1 ),
1705
- min (cols - 1 , cs + cstride )]
1706
- zbase = a [min (rows - 1 , rs + rstride ), cs :min (cols , cs + cstride + 1 ):][::- 1 ]
1707
- zright = a [rs :min (rows - 1 , rs + rstride ):, cs ][::- 1 ]
1708
- z = np .concatenate ((ztop , zleft , zbase , zright ))
1709
- ps .append (z )
1710
-
1711
- # The construction leaves the array with duplicate points, which
1712
- # are removed here.
1713
- ps = list (zip (* ps ))
1714
- lastp = np .array ([])
1715
- ps2 = [ps [0 ]] + [ps [i ] for i in range (1 , len (ps )) if ps [i ] != ps [i - 1 ]]
1716
- avgzsum = sum (p [2 ] for p in ps2 )
1717
- polys .append (ps2 )
1692
+ for rs , rs_next in zip (row_inds [:- 1 ], row_inds [1 :]):
1693
+ for cs , cs_next in zip (col_inds [:- 1 ], col_inds [1 :]):
1694
+ ps = [
1695
+ # +1 ensures we share edges between polygons
1696
+ cbook ._array_perimeter (a [rs :rs_next + 1 , cs :cs_next + 1 ])
1697
+ for a in (X , Y , Z )
1698
+ ]
1699
+ # ps = np.stack(ps, axis=-1)
1700
+ ps = np .array (ps ).T
1701
+ polys .append (ps )
1718
1702
1719
1703
if fcolors is not None :
1720
1704
colset .append (fcolors [rs ][cs ])
1721
- else :
1722
- colset .append (avgzsum / len (ps2 ))
1723
-
1724
- # Only need vectors to shade if no cmap
1725
- if cmap is None and shade :
1726
- i1 , i2 , i3 = 0 , int (len (ps2 )/ 3 ), int (2 * len (ps2 )/ 3 )
1727
- v1 [which_pt ] = np .array (ps2 [i1 ]) - np .array (ps2 [i2 ])
1728
- v2 [which_pt ] = np .array (ps2 [i2 ]) - np .array (ps2 [i3 ])
1729
- which_pt += 1
1730
- if cmap is None and shade :
1731
- normals = np .cross (v1 , v2 )
1732
- else :
1733
- normals = []
1734
1705
1706
+ def get_normals (polygons ):
1707
+ """
1708
+ Takes a list of polygons and return an array of their normals
1709
+ """
1710
+ v1 = np .empty ((len (polygons ), 3 ))
1711
+ v2 = np .empty ((len (polygons ), 3 ))
1712
+ for poly_i , ps in enumerate (polygons ):
1713
+ # pick three points around the polygon at which to find the normal
1714
+ # doesn't vectorize because polygons is jagged
1715
+ i1 , i2 , i3 = 0 , len (ps )// 3 , 2 * len (ps )// 3
1716
+ v1 [poly_i , :] = ps [i1 , :] - ps [i2 , :]
1717
+ v2 [poly_i , :] = ps [i2 , :] - ps [i3 , :]
1718
+ return np .cross (v1 , v2 )
1719
+
1720
+ # note that the striding causes some polygons to have more coordinates
1721
+ # than others
1735
1722
polyc = art3d .Poly3DCollection (polys , * args , ** kwargs )
1736
1723
1737
1724
if fcolors is not None :
1738
1725
if shade :
1739
- colset = self ._shade_colors (colset , normals )
1726
+ colset = self ._shade_colors (colset , get_normals ( polys ) )
1740
1727
polyc .set_facecolors (colset )
1741
1728
polyc .set_edgecolors (colset )
1742
1729
elif cmap :
1743
- colset = np .array (colset )
1744
- polyc .set_array (colset )
1730
+ # doesn't vectorize because polys is jagged
1731
+ avg_z = np .array ([ps [:,2 ].mean () for ps in polys ])
1732
+ polyc .set_array (avg_z )
1745
1733
if vmin is not None or vmax is not None :
1746
1734
polyc .set_clim (vmin , vmax )
1747
1735
if norm is not None :
1748
1736
polyc .set_norm (norm )
1749
1737
else :
1750
1738
if shade :
1751
- colset = self ._shade_colors (color , normals )
1739
+ colset = self ._shade_colors (color , get_normals ( polys ) )
1752
1740
else :
1753
1741
colset = color
1754
1742
polyc .set_facecolors (colset )
0 commit comments