在Matplotlib中以平面为中心绘制实心圆柱 [英] Plotting a solid cylinder centered on a plane in Matplotlib

查看:550
本文介绍了在Matplotlib中以平面为中心绘制实心圆柱的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在3d中将平面拟合到一堆点上,并最初使用np.meshgrid为其赋予了任意大小,但是现在我试图绘制以该平面为中心并以相同方式定向的圆柱体(以使该平面fit会将圆柱体的高度减半),但具有指定的半径和高度。我可以找到在matplotlib中绘制的圆柱体的唯一示例是中空的,通常在顶部和底部都是敞开的。我希望绘制的图是实心的,这样我才能清楚地看到它所包围的点。



这是一个随机生成的平面的最小工作示例。由于我正在使用的平面始终由一个点和一个法线向量给定,因此圆柱体也应基于这些东西(加上提供的半径和高度,以在该平面的上方和下方延伸)。

__future__进口部门的

 #启用新型部门
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

来源= 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 =普通[0]
b =普通[1]
c =普通[2]

#一个平面的等式为a * x + b * y + c * z + d = 0,其中[a,b,c]是法线
#因此请根据法线
d计算d = -origin.dot(正常)

#创建x,y网格
xx,yy = np.meshgrid(np.arange(cen_x-1,cen_x + 1,0.01),np .arange(cen_y-1,cen_y + 1,0.01))

#计算对应的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)
plt3d.set_xlim3d(cen_x-3,cen_x + 3)
plt3d.set_ylim3d(cen_y-3,cen_y + 3)
plt3d.set_zlim3d(cen_z-3,cen_z + 3)
plt3d.set_xlabel('X')
plt3d.set_ylabel('Y')
plt3d.set_zlabel('Z')
plt.show()


解决方案

我已修改问题的解决方案


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[0]
b = normal[1]
c = normal[2]

#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)
plt3d.set_xlim3d(cen_x-3,cen_x+3)
plt3d.set_ylim3d(cen_y-3,cen_y+3)
plt3d.set_zlim3d(cen_z-3,cen_z+3)
plt3d.set_xlabel('X')
plt3d.set_ylabel('Y')
plt3d.set_zlabel('Z')
plt.show()

解决方案

I have 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 some such 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 https://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()

The result:

这篇关于在Matplotlib中以平面为中心绘制实心圆柱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆