首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在PyVista中将参数对象绘制为网格

在PyVista中将参数对象绘制为网格
EN

Stack Overflow用户
提问于 2020-04-28 06:30:34
回答 1查看 966关注 0票数 2

我可能被一个简单的问题困住了,但是在阅读了pyvista文档之后,我仍然在寻找答案。我试图绘制一个网格,其中每个单元将是一个网格,定义为一个参数形状,即超级环面。在早期版本的pyvista中,我将“我自己的”超级环抱定义如下:

代码语言:javascript
复制
def supertorus(yScale, xScale, Height, InternalRadius, Vertical, Horizontal,
           deltaX=0, deltaY=0, deltaZ=0):

#  initial range for values used in parametric equation
n = 100
u = np.linspace(-np.pi, np.pi, n)
t = np.linspace(-np.pi, np.pi, n)
u, t = np.meshgrid(u, t)

# a1: Y Scale <0, 2>
a1 = yScale
# a2: X Scale <0, 2>
a2 = xScale
# a3: Height <0, 5>
a3 = Height
# a4: Internal radius <0, 5>
a4 = InternalRadius
# e1: Vertical squareness <0.25, 1>
e1 = Vertical
# e2: Horizontal squareness <0.25, 1>
e2 = Horizontal

# Definition of parametric equation for supertorus
x = a1 * (a4 + np.sign(np.cos(u)) * np.abs(np.cos(u)) ** e1) *\
    np.sign(np.cos(t)) * np.abs(np.cos(t)) ** e2
y = a2 * (a4 + np.sign(np.cos(u)) * np.abs(np.cos(u)) ** e1) *\
    np.sign(np.sin(t)) * np.abs(np.sin(t)) ** e2
z = a3 * np.sign(np.sin(u)) * np.abs(np.sin(u)) ** e1

grid = pyvista.StructuredGrid(x + deltaX + 5, y + deltaY + 5, z + deltaZ)
return grid 

我可以使用deltaXdeltaYdeltaZ来在我选择的位置上定位超级托利。不幸的是,这种方法没有效率,我计划使用PyVista提供的超环面网格(https://docs.pyvista.org/examples/00-load/create-parametric-geometric-objects.html?highlight=supertoroid)。我的问题是:如何在坐标xyz定义的位置放置多个网格(如超级网格)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-05-15 17:31:22

我相信你要找的是字形。您可以将自己的数据集作为字形几何图形传递,从而在超级网格的每个点绘制数据集。没有详细介绍如何定位您的字形,而是根据标量和诸如此类的内容对它们进行着色,这里有一个简单的“外星入侵”场景作为示例:

代码语言:javascript
复制
import numpy as np
import pyvista as pv

# get dataset for the glyphs: supertoroid in xy plane
saucer = pv.ParametricSuperToroid(ringradius=0.5, n2=1.5, zradius=0.5)
saucer.rotate_y(90)
# saucer.plot()  #  <-- check how a single saucer looks like

# get dataset where to put glyphs
x,y,z = np.mgrid[-1:2, -1:2, :2]
mesh = pv.StructuredGrid(x, y, z)

# construct the glyphs on top of the mesh
glyphs = mesh.glyph(geom=saucer, factor=0.3)
# glyphs.plot()  #  <-- simplest way to plot it

# create Plotter and add our glyphs with some nontrivial lighting
plotter = pv.Plotter(window_size=(1000, 800))
plotter.add_mesh(glyphs, color=[0.2, 0.2, 0.2], specular=1, specular_power=15)

plotter.show()

我加入一些强镜面照明是为了让碟子看起来更有威胁性:

但是,问题的关键是通过将其作为mesh.glyph传递给您的超级网格来创建图形。其他关键字(如orientscale )对于类似箭头的象形文字很有用,您可以在这里使用字形来表示数据集的矢量信息。

您已经在注释中询问是否可以沿数据集更改字形。我确信这是不可能的,但是VTK文档明确提到了定义一组要使用的符号集合的可能性:

可以通过创建一个源对象表来使用多个字形,每个对象定义一个不同的字形。如果定义了符号表,则可以使用标量值或向量大小将表编入索引。

事实证明,PyVista还没有公开这个功能(目前还没有),但是基本的vtk包可以让我们弄脏我们的手。这里有一个概念DataSetFilters.glyph的证明,我将通过PyVista开发程序来验证是否有兴趣公开这个功能。

代码语言:javascript
复制
import numpy as np
import pyvista as pv
from pyvista.core.filters import _get_output  # just for this standalone example
import vtk
pyvista = pv  # just for this standalone example

# below: adapted from core/filters.py
def multiglyph(dataset, orient=True, scale=True, factor=1.0,
          tolerance=0.0, absolute=False, clamping=False, rng=None,
          geom_datasets=None, geom_values=None):
    """Copy a geometric representation (called a glyph) to every point in the input dataset.
    The glyphs may be oriented along the input vectors, and they may be scaled according to scalar
    data or vector magnitude.
    Parameters
    ----------
    orient : bool
        Use the active vectors array to orient the glyphs
    scale : bool
        Use the active scalars to scale the glyphs
    factor : float
        Scale factor applied to sclaing array
    tolerance : float, optional
        Specify tolerance in terms of fraction of bounding box length.
        Float value is between 0 and 1. Default is 0.0. If ``absolute``
        is ``True`` then the tolerance can be an absolute distance.
    absolute : bool, optional
        Control if ``tolerance`` is an absolute distance or a fraction.
    clamping: bool
        Turn on/off clamping of "scalar" values to range.
    rng: tuple(float), optional
        Set the range of values to be considered by the filter when scalars
        values are provided.
    geom_datasets : tuple(vtk.vtkDataSet), optional
        The geometries to use for the glyphs in table mode
    geom_values : tuple(float), optional
        The value to assign to each geometry dataset, optional
    """
    # Clean the points before glyphing
    small = pyvista.PolyData(dataset.points)
    small.point_arrays.update(dataset.point_arrays)
    dataset = small.clean(point_merging=True, merge_tol=tolerance,
                          lines_to_points=False, polys_to_lines=False,
                          strips_to_polys=False, inplace=False,
                          absolute=absolute)
    # Make glyphing geometry
    if not geom_datasets:
        arrow = vtk.vtkArrowSource()
        arrow.Update()
        geom_datasets = arrow.GetOutput(),
        geom_values = 0,
    # check if the geometry datasets are consistent
    if not len(geom_datasets) == len(geom_values):
        raise ValueError('geom_datasets and geom_values must have the same length!')
        # TODO: other kinds of sanitization, e.g. check for sequences etc.
    # Run the algorithm
    alg = vtk.vtkGlyph3D()
    if len(geom_values) == 1:
        # use a single glyph
        alg.SetSourceData(geom_datasets[0])
    else:
        alg.SetIndexModeToScalar()
        # TODO: index by vectors?
        # TODO: SetInputArrayToProcess for arbitrary arrays, maybe?
        alg.SetRange(min(geom_values), max(geom_values))
        # TODO: different Range?
        for val, geom in zip(geom_values, geom_datasets):
            alg.SetSourceData(val, geom)
    if isinstance(scale, str):
        dataset.active_scalars_name = scale
        scale = True
    if scale:
        if dataset.active_scalars is not None:
            if dataset.active_scalars.ndim > 1:
                alg.SetScaleModeToScaleByVector()
            else:
                alg.SetScaleModeToScaleByScalar()
    else:
        alg.SetScaleModeToDataScalingOff()
    if isinstance(orient, str):
        dataset.active_vectors_name = orient
        orient = True
    if rng is not None:
        alg.SetRange(rng)
    alg.SetOrient(orient)
    alg.SetInputData(dataset)
    alg.SetVectorModeToUseVector()
    alg.SetScaleFactor(factor)
    alg.SetClamping(clamping)
    alg.Update()
    return _get_output(alg)

def example():
    """Small glyph example"""

    rng = np.random.default_rng()

    # get dataset for the glyphs: supertoroid in xy plane
    # use N random kinds of toroids over a mesh with 27 points
    N = 5
    values = np.arange(N)  # values for scalars to look up glyphs by
    geoms = [pv.ParametricSuperToroid(n1=n1, n2=n2) for n1,n2 in rng.uniform(0.5, 2, size=(N, 2))]
    for geom in geoms:
        # make the disks horizontal for aesthetics
        geom.rotate_y(90)

    # get dataset where to put glyphs
    x,y,z = np.mgrid[-1:2, -1:2, -1:2]
    mesh = pv.StructuredGrid(x, y, z)

    # add random scalars
    mesh.point_arrays['scalars'] = rng.integers(0, N, size=x.size)

    # construct the glyphs on top of the mesh; don't scale by scalars now
    glyphs = multiglyph(mesh, geom_datasets=geoms, geom_values=values, scale=False, factor=0.3)

    # create Plotter and add our glyphs with some nontrivial lighting
    plotter = pv.Plotter(window_size=(1000, 800))
    plotter.add_mesh(glyphs, specular=1, specular_power=15)

    plotter.show()

if __name__ == "__main__":
    example()

上面的multiglyph函数与mesh.glyph基本相同,但我已经用两个关键字geom_datasetsgeom_values替换了geom关键字。它们定义了一个索引->几何映射,然后用于根据数组标量查找每个字形。

你问你是否能独立地给这些字形着色:你可以。在上述概念证明中,字形的选择与标量有关(选择向量同样容易;我对任意数组不太确定)。但是,当您调用pv.Plotter.add_mesh时,您可以很容易地选择要着色的数组,所以我的建议是使用适当的标量以外的其他东西来为您的象形文字着色。

下面是一个典型的输出:

我保留了标量的颜色,以使它更容易看出不同的图形。你可以看到,有五种不同的符号是根据随机标量随机选择的。如果设置非整数标量,它仍将工作;我怀疑vtk会选择最近的标量或类似的标量进行查找。

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

https://stackoverflow.com/questions/61474010

复制
相关文章

相似问题

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