From d6d82382dbb0f5af05d4735cc23624ddd03b5698 Mon Sep 17 00:00:00 2001 From: Benjamin Root Date: Fri, 28 Jun 2013 18:55:21 -0400 Subject: [PATCH 1/2] Much better alpha control in mplot3d. Closes #1541 Probably haven't solved them all, but this gets most of them. --- lib/mpl_toolkits/mplot3d/art3d.py | 36 ++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index 0058b302eb08..fe81ab5fd07b 100755 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -14,6 +14,7 @@ from six.moves import zip from matplotlib import lines, text as mtext, path as mpath, colors as mcolors +from matplotlib import artist from matplotlib.collections import Collection, LineCollection, \ PolyCollection, PatchCollection, PathCollection from matplotlib.cm import ScalarMappable @@ -340,9 +341,13 @@ def do_3d_projection(self, renderer): xs, ys, zs = self._offsets3d vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs, renderer.M) #FIXME: mpl allows us no way to unset the collection alpha value - self._alpha = None - self.set_facecolors(zalpha(self._facecolor3d, vzs)) - self.set_edgecolors(zalpha(self._edgecolor3d, vzs)) + #self._alpha = None + fcs = mcolors.colorConverter.to_rgba_array(self._facecolor3d, + self._alpha) + mcs = mcolors.colorConverter.to_rgba_array(self._edgecolor3d, + self._alpha) + self.set_facecolors(zalpha(fcs, vzs)) + self.set_edgecolors(zalpha(mcs, vzs)) super(self.__class__, self).set_offsets(list(zip(vxs, vys))) if vzs.size > 0 : @@ -461,6 +466,7 @@ def set_3d_properties(self): self.set_zsort(True) self._facecolors3d = PolyCollection.get_facecolors(self) self._edgecolors3d = PolyCollection.get_edgecolors(self) + self._alpha3d = PolyCollection.get_alpha(self) def set_sort_zpos(self,val): '''Set the position to use for z-sorting.''' @@ -529,6 +535,30 @@ def set_edgecolor(self, colors): self._edgecolors3d = PolyCollection.get_edgecolor(self) set_edgecolors = set_edgecolor + def set_alpha(self, alpha): + """ + Set the alpha tranparencies of the collection. *alpha* must be + a float or *None*. + + ACCEPTS: float or None + """ + if alpha is not None: + try: + float(alpha) + except TypeError: + raise TypeError('alpha must be a float or None') + artist.Artist.set_alpha(self, alpha) + try: + self._facecolors = mcolors.colorConverter.to_rgba_array( + self._facecolors3d, self._alpha) + except (AttributeError, TypeError, IndexError): + pass + try: + self._edgecolors = mcolors.colorConverter.to_rgba_array( + self._edgecolors3d, self._alpha) + except (AttributeError, TypeError, IndexError): + pass + def get_facecolors(self): return self._facecolors2d get_facecolor = get_facecolors From 923272f46082d37568dada89e9a2e7343bbfccbc Mon Sep 17 00:00:00 2001 From: Benjamin Root Date: Sat, 29 Jun 2013 20:09:10 -0400 Subject: [PATCH 2/2] ENH: Added depthshade argument for axes3d scatter --- lib/mpl_toolkits/mplot3d/art3d.py | 41 ++++++++++++++++++++++-------- lib/mpl_toolkits/mplot3d/axes3d.py | 10 ++++++-- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index fe81ab5fd07b..8063368e3ba8 100755 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -311,9 +311,14 @@ def __init__(self, *args, **kwargs): :class:`~matplotlib.collections.PatchCollection`. In addition, keywords *zs=0* and *zdir='z'* are available. + Also, the keyword argument "depthshade" is available to + indicate whether or not to shade the patches in order to + give the appearance of depth (default is *True*). + This is typically desired in scatter plots. """ zs = kwargs.pop('zs', 0) zdir = kwargs.pop('zdir', 'z') + self._depthshade = kwargs.pop('depthshade', True) PatchCollection.__init__(self, *args, **kwargs) self._old_draw = lambda x: PatchCollection.draw(self, x) self.set_3d_properties(zs, zdir) @@ -340,14 +345,16 @@ def set_3d_properties(self, zs, zdir): def do_3d_projection(self, renderer): xs, ys, zs = self._offsets3d vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs, renderer.M) - #FIXME: mpl allows us no way to unset the collection alpha value - #self._alpha = None - fcs = mcolors.colorConverter.to_rgba_array(self._facecolor3d, - self._alpha) - mcs = mcolors.colorConverter.to_rgba_array(self._edgecolor3d, - self._alpha) - self.set_facecolors(zalpha(fcs, vzs)) - self.set_edgecolors(zalpha(mcs, vzs)) + + fcs = (zalpha(self._facecolor3d, vzs) if self._depthshade else + self._facecolor3d) + fcs = mcolors.colorConverter.to_rgba_array(fcs, self._alpha) + self.set_facecolors(fcs) + + ecs = (zalpha(self._edgecolor3d, vzs) if self._depthshade else + self._edgecolor3d) + ecs = mcolors.colorConverter.to_rgba_array(ecs, self._alpha) + self.set_edgecolors(ecs) super(self.__class__, self).set_offsets(list(zip(vxs, vys))) if vzs.size > 0 : @@ -367,9 +374,22 @@ class Path3DCollection(Collection3D, PathCollection): pass -def patch_collection_2d_to_3d(col, zs=0, zdir='z'): - """Convert a PatchCollection to a Patch3DCollection object.""" +def patch_collection_2d_to_3d(col, zs=0, zdir='z', depthshade=True): + """ + Convert a :class:`~matplotlib.collections.PatchCollection` into a + :class:`Patch3DCollection` object + (or a :class:`~matplotlib.collections.PathCollection` into a + :class:`Path3DCollection` object). + + Keywords: + *za* The location or locations to place the patches in the + collection along the *zdir* axis. Defaults to 0. + *zdir* The axis in which to place the patches. Default is "z". + *depthshade* Whether to shade the patches to give a sense of depth. + Defaults to *True*. + + """ # The tricky part here is that there are several classes that are # derived from PatchCollection. We need to use the right draw method. col._old_draw = col.draw @@ -378,6 +398,7 @@ def patch_collection_2d_to_3d(col, zs=0, zdir='z'): col.__class__ = Path3DCollection elif isinstance(col, PatchCollection): col.__class__ = Patch3DCollection + col._depthshade = depthshade col.set_3d_properties(zs, zdir) class Poly3DCollection(PolyCollection): diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 1406d8032a99..6288b7b4f340 100755 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -2176,7 +2176,8 @@ def add_collection3d(self, col, zs=0, zdir='z'): Axes.add_collection(self, col) - def scatter(self, xs, ys, zs=0, zdir='z', s=20, c='b', *args, **kwargs): + def scatter(self, xs, ys, zs=0, zdir='z', s=20, c='b', depthshade=True, + *args, **kwargs): ''' Create a scatter plot. @@ -2200,6 +2201,10 @@ def scatter(self, xs, ys, zs=0, zdir='z', s=20, c='b', *args, **kwargs): sequence because that is indistinguishable from an array of values to be colormapped. *c* can be a 2-D array in which the rows are RGB or RGBA, however. + + *depthshade* + Whether or not to shade the scatter markers to give the + appearance of depth. Default is *True*. ========== ========================================================== Keyword arguments are passed on to @@ -2238,7 +2243,8 @@ def scatter(self, xs, ys, zs=0, zdir='z', s=20, c='b', *args, **kwargs): zs = np.ones(len(xs)) * zs else: is_2d = False - art3d.patch_collection_2d_to_3d(patches, zs=zs, zdir=zdir) + art3d.patch_collection_2d_to_3d(patches, zs=zs, zdir=zdir, + depthshade=depthshade) if self._zmargin < 0.05 and xs.size > 0: self.set_zmargin(0.05)