I fit a plane to a bunch of points in 3d and initially gave it an arbitrary size using np.meshgrid, but now I'm trying to plot a cylinder centered on that plane and oriented the same way (such that the plane fit would cut the height of the cylinder in half), but with a specified radius and height. The only examples of cylinders plotted in matplotlib I can find are hollow and usually open at the top and bottom. I want the one I plot to be solid so I can clearly see what points it's enclosing.
Here's a minimum working example with a randomly generated plane. Since the plane I'm using is always given by a point and a normal vector, the cylinder should be based off of those things as well (plus a provided radius, and height to extend above and below the plane).
from __future__ import division #Enables new-style division
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import seaborn as sns
import numpy as np
cen_x = 0
cen_y = 0
cen_z = 0
origin = np.array([cen_x,cen_y,cen_z])
normal = np.array([np.random.uniform(-1,1),np.random.uniform(-1,1),np.random.uniform(0,1)])
a = normal
b = normal
c = normal
#equation for a plane is a*x+b*y+c*z+d=0 where [a,b,c] is the normal
#so calculate d from the normal
d = -origin.dot(normal)
# create x,y meshgrid
xx, yy = np.meshgrid(np.arange(cen_x-1,cen_x+1,0.01),np.arange(cen_y-1,cen_y+1,0.01))
# calculate corresponding z
zz = (-a * xx - b * yy - d) * 1./c
halo_x = [-0.3, -0.9, 0.8, 1.3, -0.1, 0.5]
halo_y = [0.8, 1.1, -0.5, -0.7, -1.2, 0.1]
halo_z = [1.0, -0.4, 0.3, -1.2, 0.9, 1.2]
fig = plt.figure(figsize=(9,9))
plt3d = fig.gca(projection='3d')
plt3d.plot_surface(xx, yy, zz, color='r', alpha=0.4)
I've modified a solution to a question How to add colors to each individual face of a cylinder using matplotlib, removing the fancy shading and adding end caps. If you want to show the enclosed points, you can use alpha=0.5 or somesuch to make the cylinder semi-transparent.
The orientation of the cylinder is defined by a unit vector v with length mag, which could be the normal to your surface.
#!/usr/bin/env python2 # -*- coding: utf-8 -*- """ Created on Sun Oct 2 18:33:10 2016 Modified from http://stackoverflow.com/questions/38076682/how-to-add-colors-to-each-individual-face-of-a-cylinder-using-matplotlib to add "end caps" and to undo fancy coloring. @author: astrokeat """ import numpy as np from matplotlib import pyplot as plt from scipy.linalg import norm #axis and radius p0 = np.array([1, 3, 2]) #point at one end p1 = np.array([8, 5, 9]) #point at other end R = 5 #vector in direction of axis v = p1 - p0 #find magnitude of vector mag = norm(v) #unit vector in direction of axis v = v / mag #make some vector not in the same direction as v not_v = np.array([1, 0, 0]) if (v == not_v).all(): not_v = np.array([0, 1, 0]) #make vector perpendicular to v n1 = np.cross(v, not_v) #normalize n1 n1 /= norm(n1) #make unit vector perpendicular to v and n1 n2 = np.cross(v, n1) #surface ranges over t from 0 to length of axis and 0 to 2*pi t = np.linspace(0, mag, 2) theta = np.linspace(0, 2 * np.pi, 100) rsample = np.linspace(0, R, 2) #use meshgrid to make 2d arrays t, theta2 = np.meshgrid(t, theta) rsample,theta = np.meshgrid(rsample, theta) #generate coordinates for surface # "Tube" X, Y, Z = [p0[i] + v[i] * t + R * np.sin(theta2) * n1[i] + R * np.cos(theta2) * n2[i] for i in [0, 1, 2]] # "Bottom" X2, Y2, Z2 = [p0[i] + rsample[i] * np.sin(theta) * n1[i] + rsample[i] * np.cos(theta) * n2[i] for i in [0, 1, 2]] # "Top" X3, Y3, Z3 = [p0[i] + v[i]*mag + rsample[i] * np.sin(theta) * n1[i] + rsample[i] * np.cos(theta) * n2[i] for i in [0, 1, 2]] ax=plt.subplot(111, projection='3d') ax.plot_surface(X, Y, Z, color='blue') ax.plot_surface(X2, Y2, Z2, color='blue') ax.plot_surface(X3, Y3, Z3, color='blue') plt.show()