I am trying to draw some objects with the fabulous Matplotlib package for Python. These objects consist of points implemented with plt.scatter()
and patches imp
I found a nice workaround: After plotting the data, do another plot on top with the same color and lighter line style. Instead of Poly3DCollection
I use Line3DCollection
, so no faces are plotted. The result looks very much as anticipated.
See below the new plot and the script creating it.
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollection
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = [0, 2, 1, 1]
y = [0, 0, 1, 0]
z = [0, 0, 0, 1]
vertices = [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]
tupleList = list(zip(x, y, z))
poly3d = [[tupleList[vertices[ix][iy]] for iy in range(len(vertices[0]))] for ix in range(len(vertices))]
ax.scatter(x,y,z)
ax.add_collection3d(Poly3DCollection(poly3d, facecolors='w', linewidths=1, alpha=0.5))
ax.add_collection3d(Line3DCollection(poly3d, colors='k', linewidths=0.2, linestyles=':'))
plt.show()
Thanks a lot Chilichiller and Julian. Your examples are very useful to me at present, because I am working on a little project about 3D representation of matrices with matplotlib, and Poly3DCollection seems suitable for the task.
A little note, that maybe can be useful to future readers. Running your examples in Python 3 gives TypeError: 'zip' object is not subscriptable.
The simplest solution is to wrap the return value of zip in a call to list() (as indicated by "Dive Into Python 3": http://www.diveintopython3.net/porting-code-to-python-3-with-2to3.html).
Here is a version that uses only one call to Poly3DCollection
, where edgecolors='k'
controls the color of the line and facecolors='w'
controls the color of the faces. Note how Matplotlib colors the edges behind the polygon in a lighter gray color.
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollection
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = [0, 2, 1, 1]
y = [0, 0, 1, 0]
z = [0, 0, 0, 1]
vertices = [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]
tupleList = list(zip(x, y, z))
poly3d = [[tupleList[vertices[ix][iy]] for iy in range(len(vertices[0]))] for ix in range(len(vertices))]
ax.scatter(x,y,z)
ax.add_collection3d(Poly3DCollection(poly3d, edgecolors='k', facecolors='w', linewidths=1, alpha=0.5))
plt.show()
Caution The API for Poly3DCollection
is rather confusing: the accepted keyword arguments are all of colors
, edgecolor
, edgecolors
, facecolor
, and facecolors
(using aliases and decorators to define multiple kwargs to mean the same thing, where the "s" is optional for facecolor
and edgecolor
).
This bug has been fixed in the new matplotlib. I'm running version 1.5.1.
You can see your version by running python, then doing:
import matplotlib
matplotlib.__version__
You can get matplotlib using pip. If you're on Ubuntu, run this from a terminal:
sudo apt-get install python-pip
sudo pip install matplotlib
I made a slight modification to the OP code and got the transparency working. It appears that the facecolors argument of Poly3DCollection overrides the transparency argument, so the solution was to set the color in a separate call to either Poly3DCollection.set_color
or Poly3DCollection.set_facecolor
:
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = [0, 2, 1, 1]
y = [0, 0, 1, 0]
z = [0, 0, 0, 1]
vertices = [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]
tupleList = zip(x, y, z)
poly3d = [[tupleList[vertices[ix][iy]] for iy in range(len(vertices[0]))] for ix in range(len(vertices))]
ax.scatter(x,y,z)
collection = Poly3DCollection(poly3d, linewidths=1, alpha=0.2)
face_color = [0.5, 0.5, 1] # alternative: matplotlib.colors.rgb2hex([0.5, 0.5, 1])
collection.set_facecolor(face_color)
ax.add_collection3d(collection)
plt.show()
Interestingly, if you explicitly set the edge color with collection.set_edgecolor('k')
, the edges will also honor the transparency setting.