首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在matplotlib中遮挡曲面图后面的一条线?

如何在matplotlib中遮挡曲面图后面的一条线?
EN

Stack Overflow用户
提问于 2017-01-17 22:19:11
回答 1查看 3.8K关注 0票数 13

我想使用Matplotlib通过球体表面的颜色映射来绘制数据。另外,我想添加一个3D线条图。到目前为止,我拥有的代码是:

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


NPoints_Phi         = 30
NPoints_Theta       = 30

radius              = 1
pi                  = np.pi
cos                 = np.cos
sin                 = np.sin

phi_array           = ((np.linspace(0, 1, NPoints_Phi))**1) * 2*pi
theta_array         = (np.linspace(0, 1, NPoints_Theta) **1) * pi


phi, theta          = np.meshgrid(phi_array, theta_array) 


x_coord             = radius*sin(theta)*cos(phi)
y_coord             = radius*sin(theta)*sin(phi)
z_coord             = radius*cos(theta)


#Make colormap the fourth dimension
color_dimension     = x_coord 
minn, maxx          = color_dimension.min(), color_dimension.max()
norm                = matplotlib.colors.Normalize(minn, maxx)
m                   = plt.cm.ScalarMappable(norm=norm, cmap='jet')
m.set_array([])
fcolors             = m.to_rgba(color_dimension)



theta2              = np.linspace(-np.pi,  0, 1000)
phi2                = np.linspace( 0 ,  5 * 2*np.pi , 1000)


x_coord_2           = radius * np.sin(theta2) * np.cos(phi2)
y_coord_2           = radius * np.sin(theta2) * np.sin(phi2)
z_coord_2           = radius * np.cos(theta2)

# plot
fig                 = plt.figure()

ax                  = fig.gca(projection='3d')
ax.plot(x_coord_2, y_coord_2, z_coord_2,'k|-', linewidth=1 )
ax.plot_surface(x_coord,y_coord,z_coord, rstride=1, cstride=1, facecolors=fcolors, vmin=minn, vmax=maxx, shade=False)
fig.show()

此代码生成如下所示的图像:

这几乎就是我想要的。但是,黑线在背景中时应被曲面打印遮挡,而在前景中时应可见。换句话说,黑线不应该“照亮”球体。

这可以在Matplotlib中完成,而不使用Mayavi吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-01-18 05:00:29

问题是matplotlib不是光线跟踪器,它并不是真正设计成一个3D绘图库。因此,它与2D空间中的层系统一起工作,对象可以位于更前面或更后面的层中。这可以通过大多数绘图函数的zorder关键字参数来设置。然而,matplotlib没有意识到一个对象在3D空间中是在另一个对象的前面还是后面。因此,您既可以让完整的线可见(在球体前面),也可以隐藏(在球体后面)。

解决方案是计算你自己应该是可见的点,我在这里说的是点,因为一条线将“穿过”球体连接可见的点,这是不需要的。因此,我将自己局限于绘制点-但如果您有足够的点,它们看起来就像一条线:-)。或者,可以通过在不连接的点之间使用额外的nan坐标来隐藏直线;在这里,我将自己限制在点上,以避免使解决方案比需要的更复杂。

对于一个完美的球体来说,计算哪些点应该是可见的并不是太难,想法如下:

  1. 从中获得3D plot
  2. 的视角,在view.
  3. Calculate方向的数据坐标中计算到视觉平面的法向量这个法向量(在下面的代码中称为X )和线点之间的标量积,以便使用这个标量积作为是否显示点的条件。如果标量积小于条件,则相应的点在观察者所看到的观察平面的另一侧,因此不应通过条件来shown.
  4. Filter 0点。

另一个可选任务是调整所显示的点,以适应用户旋转视图时的情况。这是通过将motion_notify_event连接到基于新设置的视角使用上述过程更新数据的功能来实现的。

有关如何实现这一点,请参阅下面的代码。

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


NPoints_Phi         = 30
NPoints_Theta       = 30

phi_array           = ((np.linspace(0, 1, NPoints_Phi))**1) * 2*np.pi
theta_array         = (np.linspace(0, 1, NPoints_Theta) **1) * np.pi

radius=1
phi, theta          = np.meshgrid(phi_array, theta_array) 

x_coord             = radius*np.sin(theta)*np.cos(phi)
y_coord             = radius*np.sin(theta)*np.sin(phi)
z_coord             = radius*np.cos(theta)

#Make colormap the fourth dimension
color_dimension     = x_coord 
minn, maxx          = color_dimension.min(), color_dimension.max()
norm                = matplotlib.colors.Normalize(minn, maxx)
m                   = plt.cm.ScalarMappable(norm=norm, cmap='jet')
m.set_array([])
fcolors             = m.to_rgba(color_dimension)

theta2              = np.linspace(-np.pi,  0, 1000)
phi2                = np.linspace( 0, 5 * 2*np.pi , 1000)

x_coord_2           = radius * np.sin(theta2) * np.cos(phi2)
y_coord_2           = radius * np.sin(theta2) * np.sin(phi2)
z_coord_2           = radius * np.cos(theta2)

# plot
fig = plt.figure()

ax = fig.gca(projection='3d')
# plot empty plot, with points (without a line)
points, = ax.plot([],[],[],'k.', markersize=5, alpha=0.9)
#set initial viewing angles
azimuth, elev = 75, 21
ax.view_init(elev, azimuth )

def plot_visible(azimuth, elev):
    #transform viewing angle to normal vector in data coordinates
    a = azimuth*np.pi/180. -np.pi
    e = elev*np.pi/180. - np.pi/2.
    X = [ np.sin(e) * np.cos(a),np.sin(e) * np.sin(a),np.cos(e)]  
    # concatenate coordinates
    Z = np.c_[x_coord_2, y_coord_2, z_coord_2]
    # calculate dot product 
    # the points where this is positive are to be shown
    cond = (np.dot(Z,X) >= 0)
    # filter points by the above condition
    x_c = x_coord_2[cond]
    y_c = y_coord_2[cond]
    z_c = z_coord_2[cond]
    # set the new data points
    points.set_data(x_c, y_c)
    points.set_3d_properties(z_c, zdir="z")
    fig.canvas.draw_idle()

plot_visible(azimuth, elev)
ax.plot_surface(x_coord,y_coord,z_coord, rstride=1, cstride=1, 
            facecolors=fcolors, vmin=minn, vmax=maxx, shade=False)

# in order to always show the correct points on the sphere, 
# the points to be shown must be recalculated one the viewing angle changes
# when the user rotates the plot
def rotate(event):
    if event.inaxes == ax:
        plot_visible(ax.azim, ax.elev)

c1 = fig.canvas.mpl_connect('motion_notify_event', rotate)

plt.show()

在最后,人们可能不得不玩一点markersizealpha和点数,以获得最具视觉吸引力的结果。

票数 13
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41699494

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档