Vấn đề là matplotlib không có dấu vết tia và nó không thực sự được thiết kế để trở thành một thư viện có khả năng vẽ 3D. Như vậy nó làm việc với một hệ thống các lớp trong không gian 2D, và các đối tượng có thể ở trong một lớp nhiều hơn ở phía trước hoặc nhiều hơn ở phía sau. Điều này có thể được đặt bằng đối số từ khóa zorder
cho hầu hết các chức năng vẽ đồ thị. Tuy nhiên không có nhận thức trong matplotlib về việc liệu một vật thể ở phía trước hay phía sau một vật thể khác trong không gian 3D. Vì vậy, bạn có thể có dòng hoàn toàn có thể nhìn thấy (ở phía trước của hình cầu) hoặc ẩn (đằng sau nó).
Giải pháp sẽ là tính toán các điểm sẽ được hiển thị một mình. Tôi đang nói về các điểm ở đây vì một đường thẳng sẽ kết nối các điểm nhìn thấy được "thông qua" hình cầu, không mong muốn. Do đó tôi hạn chế bản thân để vẽ điểm - nhưng nếu bạn có đủ điểm, chúng trông giống như một dòng :-).
Việc tính toán trong đó điểm nên được hiển thị không phải là quá khó khăn cho một lĩnh vực hoàn hảo, và ý tưởng là như sau:
- Lấy góc nhìn của cốt truyện 3D
- Từ đó, tính toán vector bình thường với mặt phẳng của tầm nhìn trong các tọa độ dữ liệu theo hướng của khung nhìn.
- Tính sản phẩm vô hướng giữa vectơ thông thường này (được gọi là
X
trong mã bên dưới) và các điểm dòng để sử dụng sản phẩm vô hướng này làm điều kiện để hiển thị điểm hay không. Nếu sản phẩm vô hướng nhỏ hơn 0
thì điểm tương ứng nằm ở phía bên kia của mặt phẳng xem như được nhìn từ người quan sát và do đó sẽ không được hiển thị.
- Lọc điểm theo điều kiện.
Một nhiệm vụ tùy chọn khác sau đó là điều chỉnh các điểm được hiển thị cho trường hợp khi người dùng xoay chế độ xem. Điều này được thực hiện bằng cách kết nối motion_notify_event
với chức năng cập nhật dữ liệu bằng cách sử dụng quy trình từ trên, dựa trên góc nhìn mới được thiết lập.
Xem mã bên dưới về cách triển khai tính năng này.
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()
Vào cuối người ta có thể phải chơi một chút với markersize
, alpha
và số lượng các điểm để có được kết quả trực quan hấp dẫn nhất trong số này.
Nó không thực sự có thể trong matplotlib. Xem [câu hỏi này] (http://stackoverflow.com/questions/14824893/how-to-draw-intersecting-planes/14825951#14825951) hoặc thảo luận trong các bình luận [ở đây] (http: // stackoverflow.Ví dụ: com/questions/16960126/python-matplotlib-3d-line-xuất-qua-bề mặt). – tom
@tom Theo quan điểm của các câu hỏi được liên kết và cũng là giải pháp của tôi bên dưới, tôi không đồng ý với tuyên bố rằng "nó không thực sự khả thi". Tôi thà nói "nó là có thể, nhưng - tùy thuộc vào trường hợp thực tế - có thể là rất nhiều công việc". – ImportanceOfBeingErnest