Plot3d in Python - python

I have an OBJ File Generated by Meshlab with Vertices and Faces data.
In MATLAB i used the function ''patch'' with Vertices data in 1 array (5937x3) and Faces (11870x3) data in another and the result is this:
Simplified version of the code
[V,F] = read_vertices_and_faces_from_obj_file(filename);
patch('Vertices',V,'Faces',F,'FaceColor','r','LineStyle','-')
axis equal
Result
The question is,how can I do that in Python ? There's a simple way like in Matlab??
I'll really appreciate any help.

Your best bet would be to make use of the mplot3d toolkit from the matplotlib library.
A similar question was asked here. Perhaps this slightly edited code excerpt from that question will help you.
The Code:
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
# Specify 4 vertices
x = [0,1,1,0] # Specify x-coordinates of vertices
y = [0,0,1,1] # Specify y-coordinates of vertices
z = [0,1,0,1] # Specify z-coordinates of vertices
verts = [zip(x, y, z)] # [(0,0,0), (1,0,1), (1,1,0), (0,1,1)]
tri = Poly3DCollection(verts) # Create polygons by connecting all of the vertices you have specified
tri.set_color(colors.rgb2hex(sp.rand(3))) # Give the faces random colors
tri.set_edgecolor('k') # Color the edges of every polygon black
ax.add_collection3d(tri) # Connect polygon collection to the 3D axis
plt.show()

Related

Matplotlib 3D workaround for plot order

I know that matplotlib 3D is not reliable for plotting multiple 3D objects (planes, lines, points) in the right order: please see Matplotlib 3D plot zorder issue and How to draw intersecting planes?.
However these questions seem quite old, so the proposed solutions. Thus, I would like to know if there are some new developments, tools, workarounds or hard-coded solutions for the following specific simple scenario:
import mpl_toolkits.mplot3d as a3
import matplotlib.pylab as plt
import numpy as np
fig = plt.figure()
ax = a3.Axes3D(fig)
# create an orizontal plane
corners = [[0,0,0],[0,5,0],[5,5,0],[5,0,0]]
tri = a3.art3d.Poly3DCollection([corners], alpha=1)
tri.set_color('w')
tri.set_edgecolor('k')
ax.add_collection3d(tri)
# plot a vector
ax.plot([2,2],[2,2],[0,4], c = 'r')
# plot some points
ax.scatter([1,3],[1,3],[1,3], c = 'r')
ax.set_xlim([0, 5.0])
ax.set_ylim([0, 5.0])
ax.set_zlim([0, 2.5]);
plt.show()
In this image you can see the visualization issues: the vector it is not starting from the plane, as it should since his intiali point is (2,2,0)

Projection of 3D convex hull onto xy plane with a colour map

What I'd like at the end is the smoothed colour map with contours plotted on top of it. The idea is to preserve as much as possible information from the 3D convex hull.
The problem is that the code I developed so far doesn't work for all the inputs.
Example
If I set tricontourf() integer parameter let say to 8 and provide 10 input files I will get 8 plots which are OK but 2 will be solid colour.
Next if I change parameter to 9 I'll get 7 good and 3 odd. Some of the good ones from the first step are now wrong!
Ideally I'd like to have this parameter fixed at ~25 so the colour map is smoothed.
Have look at the pictures:
This is wrong, int parameter = 9
this is what I want but smoother, int parameter 8
What is important to me is to have triangulation based on the convex hull.
import matplotlib.pyplot as plt
import numpy as np
import sys, os, time, math
from scipy.spatial import ConvexHull
from matplotlib.tri import Triangulation
import matplotlib.cm as cm
# get covex hull data and save them to an array
cvx = []
dataX = []
for filename in sys.argv[1:]:
X = np.genfromtxt(filename,delimiter="", skip_header=2)
dataX.append(X)
hull = ConvexHull(X)
cvx.append(hull)
for idx,filename in enumerate(sys.argv[1:]):
# start plotting data
x, y, z = dataX[idx].T
# triangulation based on a convex hull
simpl = cvx[idx].simplices
tri = Triangulation(x, y, triangles=simpl)
# plot lines (triangles)
plt.triplot(tri, color='k')
# plot contour lines based on convex hull facets
plt.tricontour(x, y, z, 5, linewidths=0.5, colors='k', triangles=simpl)
# plot colour map
plt.tricontourf(x, y, z, 8, cmap=plt.cm.rainbow, triangles=simpl)
plt.show()

Removing wireframe without gaps in matplotlib plot_trisurf

I want to create a smooth cylinder using matplotlib/pyplot. I've adapted a tutorial online and produced the following minimal example:
from numpy import meshgrid,linspace,pi,sin,cos,shape
from matplotlib import pyplot
import matplotlib.tri as mtri
from mpl_toolkits.mplot3d import Axes3D
u,v = meshgrid(linspace(0,10,10),linspace(0,2*pi,20))
u = u.flatten()
v = v.flatten()
x = u
z = sin(v)
y = cos(v)
tri = mtri.Triangulation(u, v)
fig = pyplot.figure()
ax = fig.add_axes([0,0,1,1],projection='3d')
ax.plot_trisurf(x,y,z,triangles=tri.triangles,linewidth=0)
pyplot.show()
which produces a cylinder. I set linewidth=0 to remove the wireframe, however, there is now the "ghost" of the wireframe because the triangulation has (presumably) been spaced assuming the wireframe is there to fill in the gaps. This looks to be specific to plot_trisurf, because there are other 3d plotting examples (e.g., using plot_surface) which set linewidth=0 without these gaps showing up.
Doing an mtri.Triangulation?, it seems like it might not be possible to "perfectly" fill in the gaps, since it states
>Notes
> -----
> For a Triangulation to be valid it must not have duplicate points,
> triangles formed from colinear points, or overlapping triangles.
One partial solution is to just color the wireframe the same shade of blue, but after I've fixed this problem I also want to add a light source/shading on the surface, which would put me back at square one.
Is there a way to make this work? Or can someone suggest a different approach? Thanks for any help.
ax.plot_trisurf(x,y,z,triangles=tri.triangles,linewidth=0, antialiased=False)

Make matplotlib colormap from numpy array

I'm making a surface plot on matplotlib. My axes are x, y, and depth. I have a two dimensional array which has RGB values, and the index corresponds to the (x,y) coordinate. How can I make the colormap from this 2D array? Thanks.
Code that makes numpy array:
import Image
import numpy as np
def makeImageArray(filename):
img = Image.open(filename)
a = np.array(img).astype("float32")
return a
Image is in greyscale.
From what I gather for every point (x,y) you have two pieces of information, the height and the color. You want to have a surface plot using the height, and colored according to the color at each location.
While you can easily specify custom color maps I don't think this will help you.
What you are thinking of is not that the same as a colormap which maps the height at (x,y) to a color.
The result is most evident in the Surface plots example here
I believe what you want is beyond the scope of matplotlib and can only be done with some kind of hack which I doubt you will wish to use.
Still here is my suggestion:
import pylab as py
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
X = np.arange(-5, 5, 0.1)
Y = np.arange(-5, 5, 0.1)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
colorise = [((5.0 + X[i][i])/10.0, 0.5, 0.0) for i in xrange((len(X)))]
ax = py.subplot(111, projection='3d')
for i in xrange(len(X)):
ax.plot(X[i], Y[i], Z[i], "o", color=colorise[i])
py.show()
This produces the following:
Importantly this displayed a 3D surface with the colouring not dependant on the height (it is a gradient in on direction). The most obvious issue is that coloring individual points looses matplotlibs surfaces making it painfully clear why the 3d plotting is called a projection!
Sorry this isn't very helpful, hopefully better software exists or I am unaware of matplotlibs full features.

how to plot a x-y grid of e.g. squares with colours read from an array

I have separate arrays of x and y coordinates, and a z-array of corresponding values. I wish to make a plot that has squares at each x and y coordinate that have colours set from the z array - something similar to this. I have searched quite hard on google to find how I can do this, but to no avail. The matplotlib.pyplot.scatter function needs the color array scaled from 0-1, so I can't see how that could be used in this circumstance. Any help is much appreciated.
Thanks Andrew. I see how that works now. The thing is my z-array is just one column of numbers. Since they are not in any sensible order, it would be difficult to just re-shape the array into 2D to use pcolor.
I have come up with a much better solution using a for loop to append rectangle patches to a patch collection, then assign a colour map to the whole collection and plot.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
import matplotlib.patches as mpatches
fig = plt.figure(figsize=(9,5))
ax = plt.axes([0.1,0.1,0.7,0.7])
cmap = matplotlib.cm.jet
patches = []
data=np.array([4.5,8.6,2.4,9.6,11.3])
data_id_nos=np.array([5,6,9,8,7])
x_coords=np.array([3.12,2.6,2.08,1.56,1.04])
y_coords=np.array([6.76,6.24,5.72,5.20,4.68])
coord_id_nos=np.array([7,9,6,5,8])
for i in range(len(data_id_nos)):
coords=(x_coords[np.where(coord_id_nos == data_id_nos[i])],y_coords[np.where(coord_id_nos == data_id_nos[i])])
art = mpatches.Rectangle(coords,0.50,0.50,ec="none")
patches.append(art)
#create collection of patches for IFU position
IFU1 = PatchCollection(patches, cmap=cmap)
#set the colours = data values
IFU1.set_array(np.array(data))
ax.add_collection(IFU1)
plt.axis('scaled')
plt.xlabel('x (arcsecs)')
plt.ylabel('y (arcsecs)')
I guess you want pcolor, as shown here.
You need to do something like this
x = np.arange(10)
y = np.arange(10)
z = np.zeros([10,10])
z[1,5] = 10
z[2,7] = 20
z[3,9] = 30
pcolor(x,y,z)
with this precise code the last point will be off the axis, but it should give you the idea.

Categories

Resources