Matplotlib like matlab's trisurf
Solution 1
Depending on your performance needs, mayavi is likely to be best suited for this - as per Davis comment.
However, matplotlib comes with plot_trisurf to which you can perfectly pass generic UVW
, X
, Y
, Z
as you describe.
Exemple with a torus mesh:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
R = 1.
r = 0.8
n = 50
m = 50
def torus_triangles(n, m):
""" Returns triangles to mesh a (n, m) torus """
tri = []
for i in range(n):
for j in range(m):
a = i + j*(n)
b = ((i+1) % n) + j*n
d = i + ((j+1) % m) * n
c = ((i+1) % n) + ((j+1) % m) * n
tri += [[a, b, d], [b, c, d]]
return np.array(tri, dtype=np.int32)
theta0 = np.linspace(0, (2*np.pi), n, endpoint=False)
phi0 = np.linspace(0, (2*np.pi), m, endpoint=False)
theta, phi = np.meshgrid(theta0, phi0)
x = (R + r * np.sin(phi)) * np.cos(theta)
y = (R + r * np.sin(phi)) * np.sin(theta)
z = r * np.cos(phi)
triangles = torus_triangles(n , m)
triang = mtri.Triangulation(x.ravel(), y.ravel(), triangles)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(triang, z.ravel(), lw=0.2, edgecolor="black", color="grey",
alpha=0.5)
plt.show()
Solution 2
I was also looking for a solution to this problem and this discussion helped me to succeed. Here is how it works:
A solution to very similar problem was already given as a link here in the comment from GBy (see above: Colouring the surface of a sphere with a set of scalar values in matplotlib)
-
Transfering the knowledge to the problem here it results in creating an additional array containing the amplitudes and assigning it to the "underlying ScalarMappable through
set_array
method". The corresponding python code looks like this:from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm from matplotlib import pyplot as plt import numpy as np fig = plt.figure() ax = fig.gca(projection='3d') colors = np.mean(CorticalImage[Face], axis=1) collec = ax.plot_trisurf(Xcoordinates, Ycoordinates, Zcoordinates, triangles=Face, cmap=cm.jet, linewidth=0.2) collec.set_array(colors) collec.autoscale() ax.view_init(30, 0) cbar = fig.colorbar(collec)
The arrays Xcoordinates
, Ycoordinates
, Zcoordinates
contain the X, Y and Z coordinates of the mesh nodes. When checking their shape with e.g. Xcoordinates.shape
it should look like this (750,)
, where 750 is the number of mesh nodes. The matrix Face
is the same as the matrix UVW in the original question asked by Larry. It is "a 2D m-x-3 matrix in which each row is a triplet of indices into the point cloud". If you check the shape of the matrix Face
it should be something like (1496, 3)
, where 1496 is the number of triangles in the mesh and 3 is the number of nodes in one triangle. Finally, the array CorticalImage
contains the amplitudes for every node in the mesh and these are the values, which we want to use for the colors of the mesh (and not the Z values). The shape of that array should be like the shapes of the coordinates arrays, i.e. (750,)
.
IMPORTANT!!! You can see that the number of nodes and the number of triangles are not equal. This is almost always the case. Additionally, the amplitudes are usually given for the nodes and not for the triangles. Consequently, the amplitudes should be calculated for the triangles in order to get the right colors in the plot. This is done in the line colors = np.mean(CorticalImage[Face], axis=1)
.
Solution 3
Plotly has an open-source trisurf Python implementation that is closer to MATLAB's trisurf().
Python code and examples here:
https://plot.ly/python/tri-surf/
Solution 4
Thought for completeness sake I would add a mayavi
example here, using the mesh from GBy's answer.
import numpy as np
from mayavi import mlab
from tvtk.api import tvtk
R = 1.
r = 0.8
n = 50
m = 50
def torus_triangles(n, m):
""" Returns triangles to mesh a (n, m) torus """
tri = []
for i in range(n):
for j in range(m):
a = i + j*(n)
b = ((i+1) % n) + j*n
d = i + ((j+1) % m) * n
c = ((i+1) % n) + ((j+1) % m) * n
tri += [[a, b, d], [b, c, d]]
return np.array(tri, dtype=np.int32)
theta0 = np.linspace(0, (2*np.pi), n, endpoint=False)
phi0 = np.linspace(0, (2*np.pi), m, endpoint=False)
theta, phi = np.meshgrid(theta0, phi0)
x = (R + r * np.sin(phi)) * np.cos(theta)
y = (R + r * np.sin(phi)) * np.sin(theta)
z = r * np.cos(phi)
triangles = torus_triangles(n , m)
mesh = mlab.triangular_mesh(x,y,z, triangles, representation='wireframe',color=(0,0,1) )
mlab.show()
Yielding:
Larry
Updated on June 04, 2022Comments
-
Larry about 2 years
To make a long story short, I'd like to plot a generic 3D triangle mesh in python. Matplotlib seems to be the ideal candidate, but I'd go with any 3D rendering that can do what I'm about to describe.
Suppose I have a triangle mesh defined by X, Y, and Z, the 3D coordinates of a point cloud, each a vector of length n, and UVW, a 2D m-x-3 matrix in which each row is a triplet of indices into the point cloud. This triplet represents an individual triangle. In other words, I have m triangles over n points. In Matlab, to generated a 3D plot, I just do:
trisurf(UVW, X, Y, Z)
Does anyone have any experience with this? In particular, can mplots trisurf be shoehorned to work?
-
Larry almost 10 yearsThanks! That's pretty close, but it looks like it requires X, Y, and Z to exist on a rectangular grid, which, in my case, they do not.
-
GBy almost 10 yearsNo need to be on a rectangular mesh, in fact if you look at the mesh I had to split each rectangle into 2 triangles in order to be able to use ´plot_trisurf´.
-
Andrea Lazzarotto over 7 yearsWhilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.