ParametricObjectsDemo多种参数曲面展示及面上部分点法线展示

一:主要的知识点

1、说明

本文只是教程内容的一小段,因博客字数限制,故进行拆分。主教程链接:vtk教程------逐行解析官网所有Python示例-CSDN博客

2、知识点纪要

本段代码主要涉及的有①vtkMaskPoints点采样

二:代码及注释

python 复制代码
#!/usr/bin/env python3
 
"""
    Demonstrate all the parametric objects.
"""
 
from pathlib import Path
 
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingFreeType
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonComputationalGeometry import (
    vtkParametricBohemianDome,
    vtkParametricBour,
    vtkParametricBoy,
    vtkParametricCatalanMinimal,
    vtkParametricConicSpiral,
    vtkParametricCrossCap,
    vtkParametricDini,
    vtkParametricEllipsoid,
    vtkParametricEnneper,
    vtkParametricFigure8Klein,
    vtkParametricHenneberg,
    vtkParametricKlein,
    vtkParametricKuen,
    vtkParametricMobius,
    vtkParametricPluckerConoid,
    vtkParametricPseudosphere,
    vtkParametricRandomHills,
    vtkParametricRoman,
    vtkParametricSpline,
    vtkParametricSuperEllipsoid,
    vtkParametricSuperToroid,
    vtkParametricTorus
)
from vtkmodules.vtkCommonCore import (
    vtkMinimalStandardRandomSequence,
    vtkPoints
)
from vtkmodules.vtkFiltersCore import (
    vtkGlyph3D,
    vtkMaskPoints
)
from vtkmodules.vtkFiltersSources import (
    vtkArrowSource,
    vtkParametricFunctionSource
)
from vtkmodules.vtkIOImage import (
    vtkPNGWriter
)
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkActor2D,
    vtkPolyDataMapper,
    vtkProperty,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer,
    vtkTextMapper,
    vtkTextProperty,
    vtkWindowToImageFilter
)
 
 
def get_program_parameters():
    import argparse
    description = 'Display the parametric surfaces.'
    epilogue = '''
   '''
    parser = argparse.ArgumentParser(description=description, epilog=epilogue,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('-s', '--surface_name', default=None, help='The name of the surface.')
    parser.add_argument('-b', '--back_face', action='store_true', help='Color the back face.')
    parser.add_argument('-n', '--normals', action='store_true', help='Display normals.')
    parser.add_argument('-l', '--limits', action='store_true', help='Display the geometric bounds of the object..')
    args = parser.parse_args()
    return args.surface_name, args.back_face, args.normals, args.limits
 
 
def main():
    back_face = True
    normals = True
    # Get the parametric functions and build the pipeline
    pfn = get_parametric_functions()
 
    renderer_size = 200
    grid_column_dimensions = 5
    grid_row_dimensions = 5
 
    colors = vtkNamedColors()
 
    # Create one text property for all.
    text_property = vtkTextProperty()
    text_property.SetJustificationToCentered()
    text_property.SetFontSize(int(renderer_size / 12))
    text_property.SetColor(colors.GetColor3d("LavenderBlush"))
 
    # Create a parametric function source, renderer, mapper, and actor
    # for each object.
    pfn_srcs = []
    renderers = []
    mappers = []
    actors = []
    text_mappers = []
    text_actors = []
 
    # Glyph the normals.
    mask_pts = []
    arrow = []
    glyph = []
    glyph_mapper = []
    glyph_actor = []
 
    back_property = vtkProperty()
    if back_face:
        back_property.SetColor(colors.GetColor3d("Peru"))
 
    # Now decide on the surfaces to build.
 
    surfaces = pfn
 
    # The bounding boxes for each object.
    bounding_boxes = dict()
    indexed_names = dict()
    # The index of each parametric object.
    obj_idx = -1
    sorted_names = list()
    for obj in sorted(surfaces.keys()):
        obj_idx += 1
        indexed_names[obj_idx] = obj
        pfn_srcs.append(vtkParametricFunctionSource())
        pfn_srcs[obj_idx].SetParametricFunction(surfaces[obj])
        """
        下面三段代码是用来设置参数化曲面源的分辨率,会直接影响最终生成模型的光滑度和细节
        三个方法共同决定了从数学公式生成的网格密度
        """
        pfn_srcs[obj_idx].SetUResolution(51)
        pfn_srcs[obj_idx].SetVResolution(51)
        pfn_srcs[obj_idx].SetWResolution(51)
        pfn_srcs[obj_idx].Update()
 
        mappers.append(vtkPolyDataMapper())
        mappers[obj_idx].SetInputConnection(pfn_srcs[obj_idx].GetOutputPort())
 
        actors.append(vtkActor())
        actors[obj_idx].SetMapper(mappers[obj_idx])
        actors[obj_idx].GetProperty().SetColor(colors.GetColor3d("NavajoWhite"))
        if back_face:
            actors[obj_idx].SetBackfaceProperty(back_property)
 
        text_mappers.append(vtkTextMapper())
        text_mappers[obj_idx].SetInput(obj)
        text_mappers[obj_idx].SetTextProperty(text_property)
 
        text_actors.append(vtkActor2D())
        text_actors[obj_idx].SetMapper(text_mappers[obj_idx])
        text_actors[obj_idx].SetPosition(renderer_size / 2.0, 8)
 
        renderers.append(vtkRenderer())
        renderers[obj_idx].SetBackground(colors.GetColor3d("MidnightBlue"))
 
        bounds = pfn_srcs[obj_idx].GetOutput().GetBounds()
        bounding_boxes[obj] = bounds
 
        if normals:
            # Glyphing
            """
            vtkMaskPoints()
            主要作用是从一个数据集中选择(或"遮罩")一部分点,并将它们作为新的输出。
            简单来说,它就像一个"点选择器",能让你从海量点数据中挑出你想要的那一部分
            """
            mask_pts.append(vtkMaskPoints())
            """
            多种选择点的模式
            RandomModeOn  随机选择
            SetOnRatio(10)  均匀间隔选择   每隔10个点选择一个
            SetMaskOnBounds()  只选择边界框之内的点
            """
            mask_pts[obj_idx].RandomModeOn()
            mask_pts[obj_idx].SetMaximumNumberOfPoints(150)
            mask_pts[obj_idx].SetInputConnection(pfn_srcs[obj_idx].GetOutputPort())
 
            arrow.append(vtkArrowSource())
            arrow[obj_idx].SetTipResolution(16)  # 设置箭尖的分辨率,越大越平滑
            arrow[obj_idx].SetTipLength(0.3)  # 设置箭尖的长度, 0.3是一个相对于箭头总长度的值
            arrow[obj_idx].SetTipRadius(0.1)   # 设置箭杆的半径
 
            glyph_scale = get_maximum_length(bounding_boxes[obj])
 
            glyph.append(vtkGlyph3D())
            glyph[obj_idx].SetSourceConnection(arrow[obj_idx].GetOutputPort())
            glyph[obj_idx].SetInputConnection(mask_pts[obj_idx].GetOutputPort())
            # SetVectorModeToUseNormal  设置向量模式 使用输入点上的法线向量来控制字形的方向
            glyph[obj_idx].SetVectorModeToUseNormal()
            # SetScaleFactor  设置缩放因子,决定每个箭头的大小
            glyph[obj_idx].SetScaleFactor(glyph_scale / 10.0)
            # OrientOn 启用定向功能,是SetVectorModeToUseNormal的一个补充, 在放置箭头时,要根据法线向量的方向来旋转箭头
            glyph[obj_idx].OrientOn()
            glyph[obj_idx].Update()
 
            glyph_mapper.append(vtkPolyDataMapper())
            glyph_mapper[obj_idx].SetInputConnection(
                glyph[obj_idx].GetOutputPort())
 
            glyph_actor.append(vtkActor())
            glyph_actor[obj_idx].SetMapper(glyph_mapper[obj_idx])
            glyph_actor[obj_idx].GetProperty().SetColor(colors.GetColor3d("GreenYellow"))
 
    # Need a renderer even if there is no actor.
    for i in range(obj_idx + 1, grid_column_dimensions * grid_row_dimensions):
        renderers.append(vtkRenderer())
        renderers[i].SetBackground(colors.GetColor3d("MidnightBlue"))
        sorted_names.append(None)
 
    ren_win = vtkRenderWindow()
    ren_win.SetSize(renderer_size * grid_column_dimensions, renderer_size * grid_row_dimensions)
 
    for row in range(0, grid_row_dimensions):
        for col in range(0, grid_column_dimensions):
            index = row * grid_column_dimensions + col
            # (xmin, ymin, xmax, ymax)
            viewport = [
                float(col) * renderer_size / (grid_column_dimensions * renderer_size),
                float(grid_row_dimensions - (row + 1)) * renderer_size / (grid_row_dimensions * renderer_size),
                float(col + 1) * renderer_size / (grid_column_dimensions * renderer_size),
                float(grid_row_dimensions - row) * renderer_size / (grid_row_dimensions * renderer_size)]
            ren_win.AddRenderer(renderers[index])
            renderers[index].SetViewport(viewport)
            if index > obj_idx:
                continue
            renderers[index].AddActor(actors[index])
            # Normals can only be computed for polygons and triangle strips.
            # The Spline is a line.
            if normals and indexed_names[index] != 'Spline':
                renderers[index].AddActor(glyph_actor[index])
            renderers[index].AddActor(text_actors[index])
            renderers[index].ResetCamera()
            renderers[index].GetActiveCamera().Azimuth(30)
            renderers[index].GetActiveCamera().Elevation(-30)
            renderers[index].GetActiveCamera().Zoom(0.9)
            renderers[index].ResetCameraClippingRange()
 
    iren = vtkRenderWindowInteractor()
    iren.SetRenderWindow(ren_win)
 
    fn = 'ParametricObjectsDemo'
    ren_win.SetWindowName(fn)
 
    print_callback = PrintCallback(iren, fn, 1, False)
    iren.AddObserver('KeyPressEvent', print_callback)
 
    iren.Initialize()
    iren.Start()
 
 
def get_parametric_functions():
    pfn = dict()
    pfn['Boy'] = vtkParametricBoy()
    pfn['ConicSpiral'] = vtkParametricConicSpiral()
    pfn['CrossCap'] = vtkParametricCrossCap()
    pfn['Dini'] = vtkParametricDini()
    pfn['Ellipsoid'] = vtkParametricEllipsoid()
    pfn['Enneper'] = vtkParametricEnneper()
    pfn['Figure8Klein'] = vtkParametricFigure8Klein()
    pfn['Klein'] = vtkParametricKlein()
    pfn['Mobius'] = vtkParametricMobius()
    pfn['RandomHills'] = vtkParametricRandomHills()
    pfn['Roman'] = vtkParametricRoman()
    pfn['SuperEllipsoid'] = vtkParametricSuperEllipsoid()
    pfn['SuperToroid'] = vtkParametricSuperToroid()
    pfn['Torus'] = vtkParametricTorus()
    pfn['Spline'] = vtkParametricSpline()
    # Extra parametric surfaces.
    pfn['BohemianDome'] = vtkParametricBohemianDome()
    pfn['Bour'] = vtkParametricBour()
    pfn['CatalanMinimal'] = vtkParametricCatalanMinimal()
    pfn['Henneberg'] = vtkParametricHenneberg()
    pfn['Kuen'] = vtkParametricKuen()
    pfn['PluckerConoid'] = vtkParametricPluckerConoid()
    pfn['Pseudosphere'] = vtkParametricPseudosphere()
 
    # 设置某些参数
    pfn["Ellipsoid"].SetXRadius(0.5)  # 将椭球体在 X 方向的半径设置为 0.5
    pfn["Ellipsoid"].SetYRadius(2.0)  # 将椭球体在 Y 方向的半径设置为 2.0
 
    pfn["Mobius"].SetRadius(2.0)  # 设置莫比乌斯带的半径为 2.0
    pfn["Mobius"].SetMinimumV(-0.5)  # 设置 V 参数的最小值为 -0.5
    pfn["Mobius"].SetMaximumV(0.5)  # 设置 V 参数的最大值为 0.5
 
    pfn["RandomHills"].AllowRandomGenerationOn()  # 启用随机山丘曲面的随机生成功能
    pfn["RandomHills"].SetRandomSeed(1)
    pfn["RandomHills"].SetNumberOfHills(30)  # 设置生成的山丘数量为 30 个
 
    # 通过调整 N1 和 N2 参数,可以改变超椭球体的形状,使其从立方体、球体变为星形等
    pfn["SuperEllipsoid"].SetN1(0.5)
    pfn["SuperEllipsoid"].SetN2(0.4)
 
    # 调整超环面的参数 N1 和 N2,可以改变其横截面和主体的形状
    pfn["SuperToroid"].SetN1(0.5)
    pfn["SuperToroid"].SetN2(3.0)
    # The spline needs points
    spline_points = vtkPoints()
    rng = vtkMinimalStandardRandomSequence()
    rng.SetSeed(8775070)
    for p in range(0, 10):
        xyz = [None] * 3
        for idx in range(0, len(xyz)):
            xyz[idx] = rng.GetRangeValue(-1.0, 1.0)
            rng.Next()
        spline_points.InsertNextPoint(xyz)
 
    pfn["Spline"].SetPoints(spline_points)
    # 设置波西米亚圆顶的参数 A、B 和 C。这些参数控制了圆顶的形状和复杂性
 
    pfn["BohemianDome"].SetA(5.0)
    pfn["BohemianDome"].SetB(1.0)
    pfn["BohemianDome"].SetC(2.0)
 
    # 设置库恩曲面的一个参数,通常用于调整曲面在奇异点附近的平滑度
    pfn["Kuen"].SetDeltaV0(0.001)
 
    return pfn
 
 
def get_centre(bounds):
    """
    Get the centre of the object from the bounding box.
    :param bounds: The bounding box of the object.
    :return:
    """
    if len(bounds) != 6:
        return None
    return [bounds[i] - (bounds[i] - bounds[i - 1]) / 2.0 for i in range(1, len(bounds), 2)]
 
 
def get_maximum_length(bounds):
    """
    Calculate the maximum length of the side bounding box.
    :param bounds: The bounding box of the object.
    :return:
    """
    if len(bounds) != 6:
        return None
    return max([bounds[i] - bounds[i - 1] for i in range(1, len(bounds), 2)])
 
 
def display_bounding_box_and_center(name, bounds):
    """
    Display the dimensions of the bounding box, maximum diagonal length
     and coordinates of the centre.
    :param name: The name of the object.
    :param bounds: The bounding box of the object.
    :return:
    """
    if len(bounds) != 6:
        return
    max_len = get_maximum_length(bounds)
    centre = get_centre(bounds)
    s = '{:21s}\n'.format(name)
    s += '{:21s}{:1s}'.format('  Bounds (min, max)', ':')
    s += '{:s}({:6.2f}, {:6.2f})'.format(' x:', bounds[0], bounds[1])
    s += '{:s}({:6.2f}, {:6.2f})'.format(' y:', bounds[2], bounds[3])
    s += '{:s}({:6.2f}, {:6.2f})\n'.format(' z:', bounds[4], bounds[5])
    if max_len:
        s += '  Maximum side length: {:6.2f}\n'.format(max_len)
    if centre:
        s += '  Centre (x, y, z)   : ({:6.2f}, {:6.2f}, {:6.2f})\n'.format(centre[0], centre[1], centre[2])
    print(s)
 
 
class PrintCallback:
    def __init__(self, caller, file_name, image_quality=1, rgba=True):
        self.caller = caller
        self.image_quality = image_quality
        # rgba is the the buffer type,
        #  (if true, there is no background in the screenshot).
        self.rgba = rgba
        parent = Path(file_name).resolve().parent
        pth = Path(parent) / file_name
        self.path = Path(str(pth)).with_suffix('.png')
 
    def __call__(self, caller, ev):
        # Save the screenshot.
        if caller.GetKeyCode() == "k":
            w2if = vtkWindowToImageFilter()
            w2if.SetInput(caller.GetRenderWindow())
            w2if.SetScale(self.image_quality, self.image_quality)
            if self.rgba:
                w2if.SetInputBufferTypeToRGBA()
            else:
                w2if.SetInputBufferTypeToRGB()
            # Read from the front buffer.
            w2if.ReadFrontBufferOn()
            w2if.Update()
            writer = vtkPNGWriter()
            writer.SetFileName(self.path)
            writer.SetInputData(w2if.GetOutput())
            writer.Write()
            print('Screenshot saved to:', self.path.name)
 
 
if __name__ == '__main__':
    main()
相关推荐
Chiandra_Leong1 小时前
Python-Pandas、Numpy
python·pandas
深海潜水员2 小时前
OpenGL 学习笔记 第一章:绘制一个窗口
c++·笔记·学习·图形渲染·opengl
quikai19812 小时前
python练习第三组
开发语言·python
ULTRA??2 小时前
初学protobuf,C++应用例子(AI辅助)
c++·python
CHANG_THE_WORLD3 小时前
Python 字符串全面解析
开发语言·python
甄心爱学习4 小时前
CSP认证 备考(python)
数据结构·python·算法·动态规划
databook4 小时前
数据会说谎?三大推断方法帮你“审问”数据真相
后端·python·数据分析
是Dream呀4 小时前
Python圣诞特辑:打造一棵会唱歌、会下雪的魔法圣诞树
开发语言·python·pygame
威哥爱编程4 小时前
使用 TRAE SOLO 一分钟写个简易版飞机大战
python·trae·solo