如何生成抛物面并使其响应事件

How to generate a paraboloid surface and make it respond to event

我是 vispy 和计算机图形学的新手。我必须根据某个方程生成一个抛物面,该方程的中心和参数会根据用户输入而变化。我浏览了 vispy 文档和示例,并对包有了一些了解。

我需要生成的抛物面应该是旋转对称的,如下图所示:

而我得到的是这里

我的代码如下。我修改了 vispy 示例中的 isosurface.py 示例。

import sys
import numpy as np

from vispy import app, scene

from matplotlib import pyplot as plt

# Create a canvas with a 3D viewport
canvas = scene.SceneCanvas(keys='interactive')
view = canvas.central_widget.add_view()


## Define a scalar field from which we will generate an isosurface
def psi3(i, j, k, offset=(25, 25, 25)):
    x = i-offset[0]
    y = j-offset[1]
    z = k-offset[2]
    r = (0.2*x**2 + 0.2*y**2 - 4*z)
    return r

# Create isosurface visual
data = np.fromfunction(psi3, (50, 50, 50))

surface = scene.visuals.Isosurface(data, level=data.max() / 4., color=(0.5, 0.6, 1, 1), shading='smooth', parent=view.scene)
surface.transform = scene.transforms.STTransform(translate=(-25, -25, -25))

# Add a 3D axis to keep us oriented
axis = scene.visuals.XYZAxis(parent=view.scene)

# Use a 3D camera
# Manual bounds; Mesh visual does not provide bounds yet
# Note how you can set bounds before assigning the camera to the viewbox
cam = scene.TurntableCamera(elevation=30, azimuth=30)
cam.set_range((-10, 10), (-10, 10), (-10, 10))
view.camera = cam

if __name__ == '__main__':
    canvas.show()
    if sys.flags.interactive == 0:
        app.run()

我的查询如下:

  1. 如何使抛物面看起来像第一张图片(边缘没有被剪掉)
  2. 有没有比使用等值面更好的方法来绘制抛物面。抛物面的系数应由用户改变。
  3. 如何使抛物面响应鼠标事件:悬停、拖放等。我从文档中了解到我必须将它耦合到节点 class。由于我是新手,我无法弄清楚执行此操作的确切方法。

编辑:

这是使用 matplotlib 生成所需抛物面的相应代码。我也可以在 matplotlib 中创建一个抛物线带。

import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np



# Create the surface
radius = 5
hole_radius = 4

# Generate the grid in cylindrical coordinates
r  = np.linspace(0, radius, 100)
theta = np.linspace(0, 2 * np.pi, 100)
R, THETA = np.meshgrid(r, theta)

X, Y = R * np.cos(THETA), R * np.sin(THETA)
a=0.6;b=0.6;c=0.6
Z1 = (X/a)**2+(Y/b)**2 # Elliptic paraboloid

# Do not plot the inner region
x = np.where(X**2+Y**2<=hole_radius**2,np.NAN,X)
y = np.where(X**2+Y**2<=hole_radius**2,np.NAN,Y)

# Plot the surface
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(x, y, Z1, cmap=cm.coolwarm, linewidth=0, antialiased=True, cstride=2, rstride=2)

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")

plt.show()

结果如下:

vispy 和 matplotlib 的曲面图之间的区别在于,后者通过接受 x 和 y 的二维数组来工作,而 vispy 的 SurfacePlot() 仅接受 x 和 y 中的一维向量。

由于圆柱坐标中的网格并将其转换为笛卡尔坐标进行绘图,因此无法通过复制一维 x 和 y 向量来生成网格。

更新: 正如@djhoesem 所指出的,等值面不是执行此操作的正确方法。

我是vispy的维护者,但是等面经验很少。让我们看看我能回答什么:

  1. 我看到最简单的做法是使 levels 更小(为了测试,我将其设为零)。我不确定这对性能或输出有何影响,但 isosurface 函数提到了它所基于的论文。也许这可以告诉你更多。
    See Paul Bourke, "Polygonising a Scalar Field"  
    (http://paulbourke.net/geometry/polygonise/)
  1. 要使参数可由用户控制,您可以子class 现有等值面class 并添加属性来控制这些。但是,如果您需要立即反馈,这可能会表现不佳,因为您必须重新生成 numpy 数组并重新运行 CPU 上的所有其他计算。 IsosurfaceVisual class 需要体积数据,然后将其转换为等值面。它生成 MeshVisual 可以理解的网格(IsosurfaceVisual 是 MeshVisual 的子class)。如果你想要更好的东西,你可能必须编写自己的着色器代码来完成它。取决于您的具体要求(是否必须接受 any 系数可控的公式?)。

  2. scene.visuals Visual classes 已经是 Node class 的子classes 所以你没有在那里做任何额外的事情。其他基于 SceneCanvas 的示例应该可以让您了解如何处理鼠标事件。也就是说,您提到了 "drag-drop",我不确定您是否会在 VisPy 领域处理这种情况,但更有可能在 PyQt5 领域(如果您使用的是后端)。

通过拉取请求添加了功能:https://github.com/vispy/vispy/pull/1863#event-3344873987

对代码进行了小幅修改并解决了问题。

对应的例子可以在这里找到: https://github.com/vispy/vispy/blob/master/examples/basics/visuals/axially_symmetric_surfaces.py