“Tube” Arrows in Python

老子叫甜甜 提交于 2021-02-07 10:58:27

问题


I am looking for a way to draw a few (O(1)) 3D arrows with python, which are "proper 3D", by which I mean something along the lines of what Mathematica yields, i.e. rod of the arrow looks like a tube.

I did not find anything in the matplotlib documentation, but surely there must be a way?


回答1:


You may create a solid of revolution from some path defined in 2D to create a 3D arrow. The following code wraps this into a function, which allows to specify some of the arrow parameters. This allows to create the path to be used. This is then revolved about the z axis, creating a 3D arrow. To allow for arbitrary orientations of the arrow in space, one may then rotate the complete arrow about the x axis and then about the z axis (theta_x, theta_z). Finally the arrow is plotted using a plot_surface. Note that the axes into which the arrow is placed should have an equal aspect, such that the arrow is not skewed.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D


def arrow3d(ax, length=1, width=0.05, head=0.2, headwidth=2,
                theta_x=0, theta_z=0, offset=(0,0,0), **kw):
    w = width
    h = head
    hw = headwidth
    theta_x = np.deg2rad(theta_x)
    theta_z = np.deg2rad(theta_z)

    a = [[0,0],[w,0],[w,(1-h)*length],[hw*w,(1-h)*length],[0,length]]
    a = np.array(a)

    r, theta = np.meshgrid(a[:,0], np.linspace(0,2*np.pi,30))
    z = np.tile(a[:,1],r.shape[0]).reshape(r.shape)
    x = r*np.sin(theta)
    y = r*np.cos(theta)

    rot_x = np.array([[1,0,0],[0,np.cos(theta_x),-np.sin(theta_x) ],
                      [0,np.sin(theta_x) ,np.cos(theta_x) ]])
    rot_z = np.array([[np.cos(theta_z),-np.sin(theta_z),0 ],
                      [np.sin(theta_z) ,np.cos(theta_z),0 ],[0,0,1]])

    b1 = np.dot(rot_x, np.c_[x.flatten(),y.flatten(),z.flatten()].T)
    b2 = np.dot(rot_z, b1)
    b2 = b2.T+np.array(offset)
    x = b2[:,0].reshape(r.shape); 
    y = b2[:,1].reshape(r.shape); 
    z = b2[:,2].reshape(r.shape); 
    ax.plot_surface(x,y,z, **kw)

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

arrow3d(ax)

arrow3d(ax, length=2, width=0.02, head=0.1, headwidth=1.5, offset=[1,1,0], 
        theta_x=40,  color="crimson")

arrow3d(ax, length=1.4, width=0.03, head=0.15, headwidth=1.8, offset=[1,0.1,0], 
        theta_x=-60, theta_z = 60,  color="limegreen")

ax.set_xlim(0,1)
ax.set_ylim(0,1)
ax.set_zlim(0,1)
plt.show()



来源:https://stackoverflow.com/questions/48587088/tube-arrows-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!