3d matplotlib二维面片如何转换为具有任意法线的三维面片? 简短问题
matplotlib二维面片如何转换为具有任意法线的三维面片 长问题 我想用3d投影绘制轴。但是,提供的方法仅提供沿主轴具有法线的面片的方法。如何向具有任意法线的三维轴添加面片?简短回答 将下面的代码复制到项目中并使用3d matplotlib二维面片如何转换为具有任意法线的三维面片? 简短问题,3d,matplotlib,3d,Matplotlib,matplotlib二维面片如何转换为具有任意法线的三维面片 长问题 我想用3d投影绘制轴。但是,提供的方法仅提供沿主轴具有法线的面片的方法。如何向具有任意法线的三维轴添加面片?简短回答 将下面的代码复制到项目中并使用 def pathpatch_2d_to_3d(pathpatch, z = 0, normal = 'z'): """ Transforms a 2D Patch to a 3D patch using the given normal vector.
def pathpatch_2d_to_3d(pathpatch, z = 0, normal = 'z'):
"""
Transforms a 2D Patch to a 3D patch using the given normal vector.
The patch is projected into they XY plane, rotated about the origin
and finally translated by z.
"""
将二维面片转换为具有任意法线的三维面片
from mpl_toolkits.mplot3d import art3d
def rotation_matrix(d):
"""
Calculates a rotation matrix given a vector d. The direction of d
corresponds to the rotation axis. The length of d corresponds to
the sin of the angle of rotation.
Variant of: http://mail.scipy.org/pipermail/numpy-discussion/2009-March/040806.html
"""
sin_angle = np.linalg.norm(d)
if sin_angle == 0:
return np.identity(3)
d /= sin_angle
eye = np.eye(3)
ddt = np.outer(d, d)
skew = np.array([[ 0, d[2], -d[1]],
[-d[2], 0, d[0]],
[d[1], -d[0], 0]], dtype=np.float64)
M = ddt + np.sqrt(1 - sin_angle**2) * (eye - ddt) + sin_angle * skew
return M
def pathpatch_2d_to_3d(pathpatch, z = 0, normal = 'z'):
"""
Transforms a 2D Patch to a 3D patch using the given normal vector.
The patch is projected into they XY plane, rotated about the origin
and finally translated by z.
"""
if type(normal) is str: #Translate strings to normal vectors
index = "xyz".index(normal)
normal = np.roll((1.0,0,0), index)
normal /= np.linalg.norm(normal) #Make sure the vector is normalised
path = pathpatch.get_path() #Get the path and the associated transform
trans = pathpatch.get_patch_transform()
path = trans.transform_path(path) #Apply the transform
pathpatch.__class__ = art3d.PathPatch3D #Change the class
pathpatch._code3d = path.codes #Copy the codes
pathpatch._facecolor3d = pathpatch.get_facecolor #Get the face color
verts = path.vertices #Get the vertices in 2D
d = np.cross(normal, (0, 0, 1)) #Obtain the rotation vector
M = rotation_matrix(d) #Get the rotation matrix
pathpatch._segment3d = np.array([np.dot(M, (x, y, 0)) + (0, 0, z) for x, y in verts])
def pathpatch_translate(pathpatch, delta):
"""
Translates the 3D pathpatch by the amount delta.
"""
pathpatch._segment3d += delta
长话短说
查看art3d.pathpatch_2d_to_3d的源代码可以得到以下调用层次结构
art3d.pathpatch_2d_至_3d
art3d.PathPatch3D.set_3d_属性
art3d.Patch3D.set\u 3d\u属性
art3d.摆弄轴
art3d.juggle_axes
时。修改最后一步,我们可以获得具有任意法线的3D面片
我们分四步进行
pathpatch_2d_到_3d
)旋转矩阵
)pathpatch\u 2d\u到\u 3d
)pathpatch_2d_到_3d
)from mpl_toolkits.mplot3d import proj3d
from matplotlib.patches import Circle
from itertools import product
ax = axes(projection = '3d') #Create axes
p = Circle((0,0), .2) #Add a circle in the yz plane
ax.add_patch(p)
pathpatch_2d_to_3d(p, z = 0.5, normal = 'x')
pathpatch_translate(p, (0, 0.5, 0))
p = Circle((0,0), .2, facecolor = 'r') #Add a circle in the xz plane
ax.add_patch(p)
pathpatch_2d_to_3d(p, z = 0.5, normal = 'y')
pathpatch_translate(p, (0.5, 1, 0))
p = Circle((0,0), .2, facecolor = 'g') #Add a circle in the xy plane
ax.add_patch(p)
pathpatch_2d_to_3d(p, z = 0, normal = 'z')
pathpatch_translate(p, (0.5, 0.5, 0))
for normal in product((-1, 1), repeat = 3):
p = Circle((0,0), .2, facecolor = 'y', alpha = .2)
ax.add_patch(p)
pathpatch_2d_to_3d(p, z = 0, normal = normal)
pathpatch_translate(p, 0.5)
这段代码非常有用,但有一点需要注意:它无法处理向下的法线,因为它只使用角度的正弦 您还需要使用余弦:
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d import art3d
from mpl_toolkits.mplot3d import proj3d
import numpy as np
def rotation_matrix(v1,v2):
"""
Calculates the rotation matrix that changes v1 into v2.
"""
v1/=np.linalg.norm(v1)
v2/=np.linalg.norm(v2)
cos_angle=np.dot(v1,v2)
d=np.cross(v1,v2)
sin_angle=np.linalg.norm(d)
if sin_angle == 0:
M = np.identity(3) if cos_angle>0. else -np.identity(3)
else:
d/=sin_angle
eye = np.eye(3)
ddt = np.outer(d, d)
skew = np.array([[ 0, d[2], -d[1]],
[-d[2], 0, d[0]],
[d[1], -d[0], 0]], dtype=np.float64)
M = ddt + cos_angle * (eye - ddt) + sin_angle * skew
return M
def pathpatch_2d_to_3d(pathpatch, z = 0, normal = 'z'):
"""
Transforms a 2D Patch to a 3D patch using the given normal vector.
The patch is projected into they XY plane, rotated about the origin
and finally translated by z.
"""
if type(normal) is str: #Translate strings to normal vectors
index = "xyz".index(normal)
normal = np.roll((1,0,0), index)
path = pathpatch.get_path() #Get the path and the associated transform
trans = pathpatch.get_patch_transform()
path = trans.transform_path(path) #Apply the transform
pathpatch.__class__ = art3d.PathPatch3D #Change the class
pathpatch._code3d = path.codes #Copy the codes
pathpatch._facecolor3d = pathpatch.get_facecolor #Get the face color
verts = path.vertices #Get the vertices in 2D
M = rotation_matrix(normal,(0, 0, 1)) #Get the rotation matrix
pathpatch._segment3d = np.array([np.dot(M, (x, y, 0)) + (0, 0, z) for x, y in verts])
def pathpatch_translate(pathpatch, delta):
"""
Translates the 3D pathpatch by the amount delta.
"""
pathpatch._segment3d += delta
这里有一种更通用的方法,它允许以比普通方法更复杂的方式嵌入:
class EmbeddedPatch2D(art3d.PathPatch3D):
def __init__(self, patch, transform):
assert transform.shape == (4, 3)
self._patch2d = patch
self.transform = transform
self._path2d = patch.get_path()
self._facecolor2d = patch.get_facecolor()
self.set_3d_properties()
def set_3d_properties(self, *args, **kwargs):
# get the fully-transformed path
path = self._patch2d.get_path()
trans = self._patch2d.get_patch_transform()
path = trans.transform_path(path)
# copy across the relevant properties
self._code3d = path.codes
self._facecolor3d = self._patch2d.get_facecolor()
# calculate the transformed vertices
verts = np.empty(path.vertices.shape + np.array([0, 1]))
verts[:,:-1] = path.vertices
verts[:,-1] = 1
self._segment3d = verts.dot(self.transform.T)[:,:-1]
def __getattr__(self, key):
return getattr(self._patch2d, key)
要按照问题中的要求使用它,我们需要一个helper函数
def matrix_from_normal(normal):
"""
given a normal vector, builds a homogeneous rotation matrix such that M.dot([1, 0, 0]) == normal
"""
normal = normal / np.linalg.norm(normal)
res = np.eye(normal.ndim+1)
res[:-1,0] = normal
if normal [0] == 0:
perp = [0, -normal[2], normal[1]]
else:
perp = np.cross(normal, [1, 0, 0])
perp /= np.linalg.norm(perp)
res[:-1,1] = perp
res[:-1,2] = np.cross(self.dir, perp)
return res
总而言之:
circ = Circle((0,0), .2, facecolor = 'y', alpha = .2)
# the matrix here turns (x, y, 1) into (0, x, y, 1)
mat = matrix_from_normal([1, 1, 0]).dot([
[0, 0, 0],
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
])
circ3d = EmbeddedPatch2D(circ, mat)
你怎么能这么快回答自己的问题?我只是想分享我的解决方案()。每次你问问题时,底部都有一个小按钮;如果你想马上回答你的问题,你可以勾选它。@TillHoffmann你能用这种方法把绿色的圆环变成没有填充物的圆柱体吗?我想做一些类似的事情,在曲面图上绘制3d面片形状。@代码新手:是的,这在原则上是可能的,但在实践中很棘手。你必须画两个圆圈,以圆柱体的高度分开,然后用线连接两个端盖。但是,线条的位置取决于您的视角,并且必须在交互设置中不断更新。