Here is a major patch for mplot3d.  Here is a summary of the changes:

 * bug fix: placement of title in 3D plots to match 2D plot behavior (see 
nonecolortester.py to demonstrate)
 * bug fix: allow facecolors and edgecolors to be specified as 'none' in 3D 
scatter plots to match the 2D scatter plot behavior (see nonecolortester.py to 
demonstrate)
 * bug fix: allow all keyword arguments to be used in text3D (see modified 
example code text3d_demo.py)
 * bug fix: allow an array of colors to be passed into bar3d to specify the 
colors on a per-bar or per-face basis (see new example code hist3d_demo2.py)
 * bug fix: allow all keyword arguments to be used in bar3d (see new example 
code hist3d_demo2.py)
 * bug fix: allow 3d scatter plots with 3 or 4 points with colors specified 
(see colortester2.py to demonstrate)
 * new feature: new method to disable mouse rotation in 3D plots
 * new feature: allow mouse rotation and zoom buttons to be specified by user
 * new feature: new Z-order sorting heuristic to eliminate rendering issues for 
the common case of using bar3d to visualize a 2D histogram (see modified 
example code hist3d_demo.py)
 * new feature: new method text2D (see modified example code text3d_demo.py)
 * code cleanup: warn when canvas is None which disables mouse callbacks
 * code cleanup: document more methods in mplot3d

Thanks,
-Ben
#!/usr/bin/env python
#
# Test script to test:
# * facecolor and edgecolor = 'none' in Axes3D
# * tile placement on Axes3D
#
# Compare 2D and 3D axes with the same titles to match functionality.
#
# Note that before patch:
# * Title in figure 2 is missing
# * Figures 4 and 6 are blank with exceptions on the console
#
# After patch, everything works fine.
#
# Ben Axelrod
# Feb. 24, 2010


import numpy as np
import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D

xs = [1,2,3,4,5,6]
ys = [1,2,3,4,5,6]
zs = [1,2,3,4,5,6]
cs = [1,2,3,4,5,6]


########################

fig1 = plt.figure()
ax1 = fig1.add_subplot(111)

ax1.scatter(xs, ys,
            facecolor='y',
            edgecolor='r')
ax1.set_title("2D   facecolor='y'   edgecolor='r'")

##########################

fig2 = plt.figure()
ax2 = Axes3D(fig2)

ax2.scatter(xs, ys, zs,
            facecolor='y',
            edgecolor='r')
ax2.set_title("3D   facecolor='y'   edgecolor='r'")

############################

fig3 = plt.figure()
ax3 = fig3.add_subplot(111)

ax3.scatter(xs, ys,
            facecolor='none',
            edgecolor=(1,0,0))
ax3.set_title("2D   facecolor='none'   edgecolor=(1,0,0)", ha='left', y=1, x=0.1)

##########################

fig4 = plt.figure()
ax4 = Axes3D(fig4)

ax4.scatter(xs, ys, zs,
            facecolor='none',
            edgecolor=(1,0,0))
ax4.set_title("3D   facecolor='none'   edgecolor=(1,0,0)", ha='left', y=1, x=0.1)

############################

fig5 = plt.figure()
ax5 = fig5.add_subplot(111)

ax5.scatter(xs, ys,
            edgecolor='none')
ax5.set_title("2D   edgecolor=(1,0,0)",color='g', y=0)

##########################

fig6 = plt.figure()
ax6 = Axes3D(fig6)

ax6.scatter(xs, ys, zs,
            edgecolor='none')
ax6.set_title("3D   edgecolor=(1,0,0)", color='g', y=0)

############################

plt.show()

#!/usr/bin/env python
#
# Test script to test color bug in Axes3d scatter plot when number of points
# is 3 or 4 and colors are specified.
#
# Note that before patch, figures 10, 11, 13, 14 are blank due to exceptions.
# After patch, these plots display like they should.
#
# Note that color map doesn't work yet for the 3d scatter plot
#
# Ben Axelrod
# Feb. 24, 2010


import numpy as np
import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D

xs = [1,2,3,4,5]
ys = [1,2,3,4,5]
zs = [1,2,3,4,5]
cs = [1,2,3,4,5]

cstrings = ['r', 'g', 'b', 'c', 'm']
carrays = [(1,0,0,1), (0,1,0,1), (0,0,1,1), (0,1,1,1), (1,0,1,1)]

########################


for n in [3, 4, 5]:
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xs[:n], ys[:n], c=cstrings[:n])
    ax.set_title("2D  using cstrings")

for n in [3, 4, 5]:
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xs[:n], ys[:n], c=carrays[:n])
    ax.set_title("2D  using carrays")

for n in [3, 4, 5]:
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xs[:n], ys[:n], c=cs[:n], cmap='jet')
    ax.set_title("2D  using cmap")
    

################################

for n in [3, 4, 5]:
    fig = plt.figure()
    ax = Axes3D(fig)
    ax.scatter(xs[:n], ys[:n], zs[:n], c=cstrings[:n])
    ax.set_title("3D  using cstrings")

for n in [3, 4, 5]:
    fig = plt.figure()
    ax = Axes3D(fig)
    ax.scatter(xs[:n], ys[:n], zs[:n], c=carrays[:n])
    ax.set_title("3D  using carrays")

for n in [3, 4, 5]:
    fig = plt.figure()
    ax = Axes3D(fig)
    ax.scatter(xs[:n], ys[:n], zs[:n], c=cs[:n], cmap='jet')
    ax.set_title("3D  using cmap")

##############################

plt.show()

Index: lib/mpl_toolkits/mplot3d/art3d.py
===================================================================
--- lib/mpl_toolkits/mplot3d/art3d.py	(revision 8150)
+++ lib/mpl_toolkits/mplot3d/art3d.py	(working copy)
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # art3d.py, original mplot3d version by John Porter
 # Parts rewritten by Reinier Heeres <rein...@heeres.eu>
+# Minor additions by Ben Axelrod <baxel...@coroware.com>
 
 '''
 Module containing 3D artist code and functions to convert 2D
@@ -52,8 +53,15 @@
     Text object with 3D position and (in the future) direction.
     '''
 
-    def __init__(self, x=0, y=0, z=0, text='', zdir='z'):
-        mtext.Text.__init__(self, x, y, text)
+    def __init__(self, x=0, y=0, z=0, text='', zdir='z', **kwargs):
+        '''
+        *x*, *y*, *z*  Position of text
+        *text*         Text string to display
+        *zdir*         Direction of text
+
+        Keyword arguments are passed onto :func:`~matplotlib.text.Text`.
+        '''
+        mtext.Text.__init__(self, x, y, text, **kwargs)
         self.set_3d_properties(z, zdir)
 
     def set_3d_properties(self, z=0, zdir='z'):
@@ -86,6 +94,10 @@
     '''
 
     def __init__(self, xs, ys, zs, *args, **kwargs):
+        '''
+
+        Keyword arguments are passed onto :func:`~matplotlib.lines.Line2D`.
+        '''
         lines.Line2D.__init__(self, [], [], *args, **kwargs)
         self._verts3d = xs, ys, zs
 
@@ -145,6 +157,10 @@
     '''
 
     def __init__(self, segments, *args, **kwargs):
+        '''
+
+        Keyword arguments are passed onto :func:`~matplotlib.collections.LineCollection`.
+        '''
         LineCollection.__init__(self, segments, *args, **kwargs)
 
     def set_segments(self, segments):
@@ -425,6 +441,253 @@
     def draw(self, renderer):
         return Collection.draw(self, renderer)
 
+
+class Cuboid3DCollection(Poly3DCollection):
+    '''
+    A collection of 3D cuboids rendered as polygons.
+    '''
+    
+    def __init__(self, verts, heuristic=None, *args, **kwargs):
+        '''
+        Create a Poly3DCollection.
+
+        *verts* should contain 3D coordinates.
+
+        Note that this class does a bit of magic with the _facecolors
+        and _edgecolors properties.
+        '''
+
+        if heuristic is None:
+            self._heuristic = self.get_heuristic_list()[0]
+        else:
+            self.set_heuristic(heuristic)
+        #end if
+        
+        Poly3DCollection.__init__(self, verts, *args, **kwargs)
+    #end def
+    
+    def get_heuristic(self):
+        '''
+        Returns the current heuristic to use for Z order sorting
+        '''
+        return self._heuristic
+    #end def
+    
+    def get_heuristic_list(self):
+        '''
+        Returns the list of available Z order sorting heuristics.
+        '''
+        return ['average', 'histogram']
+    #end def
+    
+    def set_heuristic(self, heuristic):
+        if heuristic in self.get_heuristic_list():
+            self._heuristic = heuristic
+        else:
+            print 'WARNING: Unknown heuristic'
+        #end if
+    #end def
+    
+    def _sort_polygons_avg(self, polys):
+        """
+        Sort a list of polygon faces.  The list of polygon faces will be in
+        the format:
+
+        ([face1, face2, face3, face4], facecolor, edgecolor)
+
+        where each face is: (x, y, z)
+        and each color is: array([r, g, b, a])
+
+        The list is returned in the same format.  However, it is sorted
+        according to some heuristic for z-buffering.  Polygons in the back
+        of the scene which will be drawn first should be at the front of the
+        list.
+
+        Note that when the list of polygons is passed in to this function,
+        it is grouped by box.  Meaning that the 6 faces for each box shape
+        are next to each other in the list.
+
+        This implements the 'average' z order sorting heuristic.  Where the
+        average Z value of the 4 corners of each face are used to sort the
+        faces.  Each face is considered independantly of the box in which it
+        belongs.  This heuristic may be best if you have bars that float in
+        space without a common ground plane.
+
+        There may be Z order issues if you have a very tall box next to a very
+        short box.
+        """
+        
+        #compute the average z value of each face
+        newpolys = []
+        for [p1, p2, p3, p4], fc, ec in polys:
+            average_z = np.average((p1[2], p2[2], p3[2], p4[2]))
+            newpolys.append((average_z, ([p1, p2, p3, p4], fc, ec)))
+        #end for
+        
+        # sort based on z value
+        newpolys.sort(cmp=lambda x, y: cmp(y[0], x[0]))
+
+        #strip z value out of array before returning
+        return [ b for (a, b) in newpolys]
+    #end def _sort_polygons_avg()
+
+    def _sort_polygons_hist(self, polys):
+        """
+        Sort a list of polygon faces.  The list of polygon faces will be in
+        the format:
+
+        ([face1, face2, face3, face4], facecolor, edgecolor)
+
+        where each face is: (x, y, z)
+        and each color is: array([r, g, b, a])
+
+        The list is returned in the same format.  However, it is sorted
+        according to some heuristic for z-buffering.  Polygons in the back
+        of the scene which will be drawn first should be at the front of the
+        list.
+
+        Note that when the list of polygons is passed in to this function,
+        it is grouped by box.  Meaning that the 6 faces for each box shape
+        are next to each other in the list.
+
+        This implements the 'histogram' z order sorting heuristic.  Where
+        the max Z value of each box (set of 6 faces) is used to sort the faces
+        on a per-box basis.  Then the faces within each box are sorted.  This
+        heuristic may be best if your boxes all have the same value for \'z\'.
+        i.e. you are visualizing a 2D histogram.
+
+        Note that this heuristic may render improperly if the histogram is
+        viewed 'from below'.  To fix this, the 'actual' XYZ coordinates of the
+        box vertecies should be passed in as well.  Then, the 4 corners of the
+        box that are on the ground plane should be found.  Then the *min* Z
+        value of these 4 points should be used to sort the boxes.
+
+        There are also slight Z order issues when viewed from directly above.
+        """
+
+        if len(polys) % 6 != 0:
+            print 'WARNING, number of polygons is not divisable by 6'
+        
+        nfaces = int(len(polys) / 6)
+
+        # Compute the max z value of each face
+        # Format of face_zs is a list of:
+        # (max_z_face, poly[i])
+        face_zs = []
+        for [p1, p2, p3, p4], fc, ec in polys:
+            max_z_face = max(p1[2], p2[2], p3[2], p4[2])
+            face_zs.append((max_z_face, ([p1, p2, p3, p4], fc, ec)))
+        #end for
+
+        # Compute the max Z value for each set of 6 polys
+        # Format of box_zs is a list of:
+        #
+        box_zs = []
+        for i in range(nfaces):
+            max_z_box = max(face_zs[6*i+0][0],
+                            face_zs[6*i+1][0],
+                            face_zs[6*i+2][0],
+                            face_zs[6*i+3][0],
+                            face_zs[6*i+4][0],
+                            face_zs[6*i+5][0])
+
+            # sort faces for this box
+            faces = [face_zs[6*i+0],
+                     face_zs[6*i+1],
+                     face_zs[6*i+2],
+                     face_zs[6*i+3],
+                     face_zs[6*i+4],
+                     face_zs[6*i+5]]
+            faces.sort(cmp=lambda x, y: cmp(y[0], x[0]))
+
+            box_zs.append((max_z_box, faces))
+        #end for
+
+        # sort boxes based on z value
+        box_zs.sort(cmp=lambda x, y: cmp(y[0], x[0]))
+
+        #now combine sorted boxes and faces into a single list again.
+        #stripping z values also
+        newpolys = []
+        for bz, faces in box_zs:
+            for fz, poly in faces:
+                newpolys.append(poly)
+        
+        #strip z value out of array before returning
+        return newpolys
+    #end def _sort_polygons_hist()
+    
+    def do_3d_projection(self, renderer):
+        '''
+        Perform the 3D projection for this object.
+        '''
+
+        if self._A is not None:
+            self.update_scalarmappable()
+            self._facecolors3d = self._facecolors
+
+        txs, tys, tzs = proj3d.proj_transform_vec(self._vec, renderer.M)
+        xyzlist = [(txs[si:ei], tys[si:ei], tzs[si:ei]) \
+                for si, ei in self._segis]
+
+        # This extra fuss is to re-order face / edge colors
+        cface = self._facecolors3d
+        cedge = self._edgecolors3d
+        if len(cface) != len(xyzlist):
+            cface = cface.repeat(len(xyzlist), axis=0)
+        if len(cedge) != len(xyzlist):
+            if len(cedge) == 0:
+                cedge = cface
+            cedge = cedge.repeat(len(xyzlist), axis=0)
+            
+        # if required sort by depth (furthest drawn first)
+        if self._zsort:
+            
+            # Put polygon info into a big list.  The format is a list of:
+            # ([face1, face2, face3, face4], facecolor, edgecolor)
+            # where each face is: (x, y, z)
+            # and each color is: array([r, g, b, a])
+            
+            polys = [(zip(xs, ys, zs), fc, ec) for
+                     (xs, ys, zs), fc, ec in zip(xyzlist, cface, cedge)]
+            
+            # sort the polygons however is best
+            if self._heuristic == 'average':
+                sorted_polys = self._sort_polygons_avg(polys)
+            elif self._heuristic == 'histogram':
+                sorted_polys = self._sort_polygons_hist(polys)
+            else:
+                print 'WARNING: Unknown heuristic'
+            #end if
+            
+        else:
+            raise ValueError, "whoops"
+
+        # Take out only the (X,Y) pairs and set the polygon vertices
+        onlyxy = []
+        for [p1, p2, p3, p4], fc, ec in sorted_polys:
+            onlyxy.append([p1[:2], p2[:2], p3[:2], p4[:2]])
+        #end for
+        PolyCollection.set_verts(self, onlyxy)
+
+        self._facecolors2d = [fc for s, fc, ec in sorted_polys]
+        if len(self._edgecolors3d) == len(cface):
+            self._edgecolors2d = [ec for s, fc, ec in sorted_polys]
+        else:
+            self._edgecolors2d = self._edgecolors3d
+            
+##        # Return zorder value
+##        if self._sort_zpos is not None:
+##           zvec = np.array([[0], [0], [self._sort_zpos], [1]])
+##           ztrans = proj3d.proj_transform_vec(zvec, renderer.M)
+##           return ztrans[2][0]
+##        else:
+##            return np.min(tzs)
+##        #end if
+    #end def
+#end class
+
+
 def poly_collection_2d_to_3d(col, zs=0, zdir='z'):
     """Convert a PolyCollection to a Poly3DCollection object."""
     segments_3d = paths_to_3d_segments(col.get_paths(), zs, zdir)
@@ -468,9 +731,14 @@
 
 def iscolor(c):
     try:
-        return (len(c) == 4 or len(c) == 3) and hasattr(c[0], '__float__')
-    except (IndexError):
+        if len(c) == 4 or len(c) == 3:
+            if iterable(c[0]):
+                return False
+            if hasattr(c[0], '__float__'):
+                return True
+    except:
         return False
+    return False
 
 def get_colors(c, num):
     """Stretch the color argument to provide the required number num"""
@@ -484,6 +752,8 @@
         return c
     elif iscolor(c):
         return [c] * num
+    elif len(c) == 0: #if edgecolor or facecolor is specified as 'none'
+        return [[0,0,0,0]] * num
     elif iscolor(c[0]):
         return [c[0]] * num
     else:
Index: lib/mpl_toolkits/mplot3d/axes3d.py
===================================================================
--- lib/mpl_toolkits/mplot3d/axes3d.py	(revision 8150)
+++ lib/mpl_toolkits/mplot3d/axes3d.py	(working copy)
@@ -2,12 +2,14 @@
 # axes3d.py, original mplot3d version by John Porter
 # Created: 23 Sep 2005
 # Parts fixed by Reinier Heeres <rein...@heeres.eu>
+# Minor additions by Ben Axelrod <baxel...@coroware.com>
 
 """
 Module containing Axes3D, an object which can plot 3D objects on a
 2D matplotlib figure.
 """
 
+import warnings
 from matplotlib.axes import Axes, rcParams
 from matplotlib import cbook
 from matplotlib.transforms import Bbox
@@ -147,7 +149,7 @@
 
         # Calculate projection of collections and zorder them
         zlist = [(col.do_3d_projection(renderer), col) \
-                for col in self.collections]
+                 for col in self.collections]
         zlist.sort()
         zlist.reverse()
         for i, (z, col) in enumerate(zlist):
@@ -156,6 +158,15 @@
         # Calculate projection of patches and zorder them
         zlist = [(patch.do_3d_projection(renderer), patch) \
                 for patch in self.patches]
+        #zlist = []
+        #for patch in self.patches:
+        #    try:
+        #        zlist.append((patch.do_3d_projection(renderer), patch))
+        #    except:
+        #        pass
+        #    #end try
+        ##end for
+        
         zlist.sort()
         zlist.reverse()
         for i, (z, patch) in enumerate(zlist):
@@ -322,7 +333,23 @@
         M = np.dot(perspM, M0)
         return M
 
-    def mouse_init(self):
+    def mouse_init(self, rotateBtn=1, zoomBtn=3):
+        """Initializes mouse button callbacks to enable 3D rotation of
+        the axes.  Also optionally sets the mouse buttons for 3D rotation
+        and zooming.
+
+        ==========  ================================================
+        Argument    Description
+        ==========  ================================================
+        *rotateBtn* The integer or list of integers specifying which mouse
+                    button or buttons to use for 3D rotation of the axes.
+                    Default = 1.
+
+        *zoomBtn*   The integer or list of integers specifying which mouse
+                    button or buttons to use to zoom the 3D axes.
+                    Default = 3.
+        ==========  ================================================
+        """
         self.button_pressed = None
         canv = self.figure.canvas
         if canv != None:
@@ -330,15 +357,40 @@
             c2 = canv.mpl_connect('button_press_event', self._button_press)
             c3 = canv.mpl_connect('button_release_event', self._button_release)
             self.cids = [c1, c2, c3]
+        else:
+            warnings.warn('Axes3D.figure.canvas is \'None\', mouse rotation disabled.  Set canvas then call Axes3D.mouse_init().')
+        #end if
 
+        if isinstance(rotateBtn, int):
+            self.rotateBtn = [rotateBtn]
+        else:
+            self.rotateBtn = rotateBtn
+        #end if
+        
+        if isinstance(zoomBtn, int):
+            self.zoomBtn = [zoomBtn]
+        else:
+            self.zoomBtn = zoomBtn
+        #end if
+        
+    #end def
+    
     def cla(self):
+        """Clear axes and disable mouse button callbacks. 
+        """
+        self.disable_mouse_rotation()
+        Axes.cla(self)
+        self.grid(rcParams['axes3d.grid'])
+
+    def disable_mouse_rotation(self):
+        """Disable mouse button callbacks.
+        """
         # Disconnect the various events we set.
         for cid in self.cids:
             self.figure.canvas.mpl_disconnect(cid)
+        #endfor
         self.cids = []
-        Axes.cla(self)
-        self.grid(rcParams['axes3d.grid'])
-
+    
     def _button_press(self, event):
         if event.inaxes == self:
             self.button_pressed = event.button
@@ -426,9 +478,10 @@
     def _on_move(self, event):
         """Mouse moving
 
-        button-1 rotates
-        button-3 zooms
+        button-1 rotates by default.  Can be set explicitly in mouse_init().
+        button-3 zooms by default.  Can be set explicitly in mouse_init().
         """
+
         if not self.button_pressed:
             return
 
@@ -447,7 +500,8 @@
         h = (y1-y0)
         self.sx, self.sy = x, y
 
-        if self.button_pressed == 1:
+        # Rotation
+        if self.button_pressed in self.rotateBtn:
             # rotate viewing point
             # get the x and y pixel coords
             if dx == 0 and dy == 0:
@@ -456,12 +510,15 @@
             self.azim = art3d.norm_angle(self.azim - (dx/w)*180)
             self.get_proj()
             self.figure.canvas.draw()
-        elif self.button_pressed == 2:
-            # pan view
-            # project xv,yv,zv -> xw,yw,zw
-            # pan
-            pass
-        elif self.button_pressed == 3:
+            
+##        elif self.button_pressed == 2:
+##            # pan view
+##            # project xv,yv,zv -> xw,yw,zw
+##            # pan
+##            pass
+        
+        # Zoom
+        elif self.button_pressed in self.zoomBtn:
             # zoom view
             # hmmm..this needs some help from clipping....
             minx, maxx, miny, maxy, minz, maxz = self.get_w_lims()
@@ -476,7 +533,7 @@
             self.figure.canvas.draw()
 
     def set_xlabel(self, xlabel, fontdict=None, **kwargs):
-        '''Set xlabel. '''
+        '''Set xlabel.'''
 
         label = self.w_xaxis.get_label()
         label.set_text(xlabel)
@@ -511,14 +568,16 @@
         '''
         self._draw_grid = on
 
-    def text(self, x, y, z, s, zdir=None):
+    def text(self, x, y, z, s, zdir=None, fontdict=None, withdash=False, **kwargs):
         '''Add text to the plot.'''
-        text = Axes.text(self, x, y, s)
+        text = Axes.text(self, x, y, s, fontdict=fontdict, withdash=withdash, **kwargs)
         art3d.text_2d_to_3d(text, z, zdir)
         return text
-
+    
     text3D = text
+    text2D = Axes.text
 
+    
     def plot(self, xs, ys, *args, **kwargs):
         '''
         Plot 2D or 3D data.
@@ -533,7 +592,7 @@
         *zdir*      Which direction to use as z ('x', 'y' or 'z')
                     when plotting a 2d set.
         ==========  ================================================
-
+        
         Other arguments are passed on to
         :func:`~matplotlib.axes.Axes.plot`
         '''
@@ -591,6 +650,9 @@
         *shade*     Whether to shade the facecolors, default:
                     false when cmap specified, true otherwise
         ==========  ================================================
+
+        Other arguments are passed on to
+        :func:`~mpl_toolkits.mplot3d.art3d.Poly3DCollection.__init__`
         '''
 
         had_data = self.has_data()
@@ -822,8 +884,9 @@
 
             colors = self._shade_colors(color, normals)
             colors2 = self._shade_colors(color, normals)
-            polycol = art3d.Poly3DCollection(polyverts, facecolors=colors,
-                edgecolors=colors2)
+            polycol = art3d.Poly3DCollection(polyverts,
+                                             facecolors=colors,
+                                             edgecolors=colors2)
             polycol.set_sort_zpos(z)
             self.add_collection3d(polycol)
 
@@ -848,6 +911,8 @@
 
         Other keyword arguments are passed on to
         :func:`~matplotlib.axes.Axes.contour`
+
+        Returns a :class:`~matplotlib.axes.Axes.contour`
         '''
 
         extend3d = kwargs.pop('extend3d', False)
@@ -881,7 +946,9 @@
         *X*, *Y*, *Z*: data points.
 
         Keyword arguments are passed on to
-        :func:`~matplotlib.axes.Axes.contour`
+        :func:`~matplotlib.axes.Axes.contourf`
+
+        Returns a :class:`~matplotlib.axes.Axes.contourf`
         '''
 
         had_data = self.has_data()
@@ -1005,25 +1072,72 @@
 
         return patches
 
-    def bar3d(self, x, y, z, dx, dy, dz, color='b'):
+    def bar3d(self, x, y, z, dx, dy, dz, color='b',
+              heuristic=None, *args, **kwargs):
         '''
         Generate a 3D bar, or multiple bars.
 
         When generating multiple bars, x, y, z have to be arrays.
-        dx, dy, dz can still be scalars.
+        dx, dy, dz can be arrays or scalars.
+
+        *color* can be:
+         - A single color value, to color all bars the same color.
+         - An array of colors of length N bars, to color each bar
+         independently.
+         - An array of colors of length 6, to color the faces of the bars
+         similarly.
+         - An array of colors of length 6 * N bars, to color each face
+         independently.
+
+         When coloring the faces of the boxes specifically, this is the order
+         of the coloring:
+          1. -Z (bottom of box)
+          2. +Z (top of box)
+          3. -Y
+          4. +Y
+          5. -X
+          6. +X
+
+        *heuristic* determines the algorithm to use for z-order sorting the
+        bar3d faces.  Can be one of:
+         - \'average\' - The average Z value of the 4 corners of each face are
+           used to sort the faces.  This heuristic may be best if you have bars
+           that float in space without a common ground plane.
+         - \'histogram\' - The max Z value of each box (set of 6 faces) is used
+           to sort the faces on a per-box basis.  Then the faces within each box
+           are sorted.  This heuristic may be best if your boxes all have the
+           same value for \'z\'.  i.e. you are visualizing a 2D histogram.
+         
+        Keyword arguments are passed onto
+        :func:`~mpl_toolkits.mplot3d.art3d.Cuboid3DCollection`
         '''
-
         had_data = self.has_data()
 
         if not cbook.iterable(x):
-            x, y, z = [x], [y], [z]
+            x = [x]
+        if not cbook.iterable(y):
+            y = [y]
+        if not cbook.iterable(z):
+            z = [z]
+            
         if not cbook.iterable(dx):
-            dx, dy, dz = [dx], [dy], [dz]
+            dx = [dx]
+        if not cbook.iterable(dy):
+            dy = [dy]
+        if not cbook.iterable(dz):
+            dz = [dz]
+            
         if len(dx) == 1:
             dx = dx * len(x)
-            dy = dy * len(x)
-            dz = dz * len(x)
+        if len(dy) == 1:
+            dy = dy * len(y)
+        if len(dz) == 1:
+            dz = dz * len(z)
 
+        if len(x) != len(y) or len(x) != len(z):
+            warnings.warn('x, y, and z must be the same length.')
+  
+            
         minx, miny, minz = 1e20, 1e20, 1e20
         maxx, maxy, maxz = -1e20, -1e20, -1e20
 
@@ -1052,16 +1166,46 @@
                 ((xi + dxi, yi, zi), (xi + dxi, yi + dyi, zi),
                     (xi + dxi, yi + dyi, zi + dzi), (xi + dxi, yi, zi + dzi)),
             ])
-
-        color = np.array(colorConverter.to_rgba(color))
+        #end for
+        
+        facecolors = []
+        if color is None:
+            # no color specified
+            facecolors = [None] * len(x)
+        elif len(color) == len(x):
+            # bar colors specified, need to expand to number of faces
+            for c in color:
+                for i in range(6):
+                    facecolors.append(c)
+                #end for
+            #end for
+        else:
+            # a single color specified, or face colors specified explicitly
+            facecolors = list(colorConverter.to_rgba_array(color))
+            if len(facecolors) < len(x):
+                facecolors *= len(x)
+            #endif
+        #endif
+        
         normals = self._generate_normals(polys)
-        colors = self._shade_colors(color, normals)
-
-        col = art3d.Poly3DCollection(polys, facecolor=colors)
+        sfacecolors = self._shade_colors(facecolors, normals)
+        col = art3d.Cuboid3DCollection(polys,
+                                       heuristic=heuristic,
+                                       facecolor=sfacecolors,
+                                       *args, **kwargs)
         self.add_collection(col)
 
         self.auto_scale_xyz((minx, maxx), (miny, maxy), (minz, maxz), had_data)
+    #end def
+    
+    def set_title(self, label, fontdict=None, **kwargs):
+        Axes.set_title(self, label, fontdict, **kwargs)
+        (x, y) = self.title.get_position()
+        self.title.set_y(0.92 * y)
+    #end def set_title
 
+#end class Axes3D
+
 def get_test_data(delta=0.05):
     '''
     Return a tuple X, Y, Z with a test data set.
Index: examples/mplot3d/hist3d_demo.py
===================================================================
--- examples/mplot3d/hist3d_demo.py	(revision 8150)
+++ examples/mplot3d/hist3d_demo.py	(working copy)
@@ -17,7 +17,7 @@
 dy = dx.copy()
 dz = hist.flatten()
 
-ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b')
+ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b', heuristic='histogram')
 
 plt.show()
 
Index: examples/mplot3d/text3d_demo.py
===================================================================
--- examples/mplot3d/text3d_demo.py	(revision 8150)
+++ examples/mplot3d/text3d_demo.py	(working copy)
@@ -13,6 +13,10 @@
     label = '(%d, %d, %d), dir=%s' % (x, y, z, zdir)
     ax.text(x, y, z, label, zdir)
 
+ax.text(1, 1, 1, "red", color='red')
+ax.text2D(0.05, 0.95, "2D Text", transform=ax.transAxes)
+
+
 ax.set_xlim3d(0, 10)
 ax.set_ylim3d(0, 10)
 ax.set_zlim3d(0, 10)
Index: examples/mplot3d/hist3d_demo2.py
===================================================================
--- examples/mplot3d/hist3d_demo2.py	(revision 0)
+++ examples/mplot3d/hist3d_demo2.py	(revision 0)
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+"""
+An example of how to color the bar3d plot.
+"""
+from mpl_toolkits.mplot3d import Axes3D
+import matplotlib.pyplot as plt
+import numpy as np
+
+# One color per box
+fig1 = plt.figure()
+ax1 = Axes3D(fig1)
+
+ax1.bar3d([0, 1, 2, 3], #x pos
+          [0, 0, 0, 0], #y pos
+          [0, 0, 0, 0], #z pos
+          0.5,          #dx
+          0.5,          #dy
+          [1, 2, 3, 4], #dz
+          color=['r', 'g', 'b', 'y'])
+
+
+# One color per face
+fig2 = plt.figure()
+ax2 = Axes3D(fig2)
+
+ax2.bar3d([0, 1, 2, 3], #x pos
+          [0, 0, 0, 0], #y pos
+          [0, 0, 0, 0], #z pos
+          0.5,          #dx
+          0.5,          #dy
+          [1, 2, 3, 4], #dz
+          color=['r', 'g', 'b', 'c', 'm', 'y']*6)
+
+
+# Thicker red edges
+fig3 = plt.figure()
+ax3 = Axes3D(fig3)
+
+ax3.bar3d([0, 1, 2, 3], #x pos
+          [0, 0, 0, 0], #y pos
+          [0, 0, 0, 0], #z pos
+          0.5,          #dx
+          0.5,          #dy
+          [1, 2, 3, 4], #dz
+          edgecolors='r',
+          linewidths=5)
+
+plt.show()
+
------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

Reply via email to