QuadraticHexahedronDemo 非线性单元的展示与窗口交互

一:主要的知识点

1、说明

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

2、知识点纪要

本段代码主要涉及的有①vtkSliderslight滑块,②事件响应

二:代码及注释

python 复制代码
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkCommand, vtkPoints, vtkMinimalStandardRandomSequence
from vtkmodules.vtkCommonDataModel import vtkGenericCell, vtkQuadraticHexahedron, vtkUnstructuredGrid
from vtkmodules.vtkFiltersCore import vtkGlyph3D
from vtkmodules.vtkFiltersGeneral import vtkTessellatorFilter
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkInteractionWidgets import (
    vtkSliderRepresentation2D,
    vtkSliderWidget
)
from vtkmodules.vtkRenderingCore import vtkActor, vtkActor2D, vtkDataSetMapper, vtkRenderWindow, \
    vtkRenderWindowInteractor, vtkRenderer, vtkTextMapper, vtkTextProperty
 
 
class SliderCallbackChordError():
    def __init__(self, tessellate, textMapper):
        self.tessellate = tessellate
        self.textMapper = textMapper
 
    def __call__(self, caller, ev):
        """
        :param caller: 触发的对象,这里就是slider widget
        :param ev: 触发事件 一个整数常量,表示触发的事件类型
        VTK 内部用枚举 vtkCommand.EventIds 定义了常见事件,例如:
                            vtkCommand.InteractionEvent
                            vtkCommand.StartInteractionEvent
                            vtkCommand.EndInteractionEvent
                            vtkCommand.MouseMoveEvent
                            vtkCommand.KeyPressEvent
        目前代码只监听了 InteractionEvent。
        所以 ev 每次都会是 vtkCommand.InteractionEvent(对应的整数 ID)。
        虽然你代码里没用 ev,但如果你监听多个事件,就能通过 ev 区分:
        如果想监听多个事件,就需要在sliderWidget中添加多个监听事件
        """
        sliderWidget = caller
        value = sliderWidget.GetRepresentation().GetValue()
        """
        SetChordError 和 SetMaximumNumberOfSubdivisions 
        两个方法都用于控制细分(tessellation)过程,但它们从不同的角度来限制细分,通常是共同作用的
        它们之间的区别在于:
            SetChordError() 是基于几何精确度的限制。
            SetMaximumNumberOfSubdivisions() 是基于迭代次数的限制。
        SetChordError(value)
        这个方法关注的是细分后曲面与原始曲面之间的最大允许误差。
        工作方式: 过滤器会不断地细分弯曲的单元格,直到细分后的小平面多边形与原始曲面之间的**弦误差(chord error)**小于你设置的 value。
        优点: 它能确保最终渲染的模型达到你所需的视觉精确度。
        缺点: 如果原始曲面非常复杂或有很多尖锐的弯曲,为了达到这个误差阈值,过滤器可能会进行无限次的细分,导致性能问题或程序崩溃。
        SetMaximumNumberOfSubdivisions()
        这个方法关注的是细分的最大迭代次数。
        工作方式: 过滤器在细分时,会计算每个单元格需要细分的次数。但无论需要多少次,它都不会超过你设置的 n 次。
        优点: 这是一个安全网。它能防止细分过程因为 SetChordError() 的要求而变得无限或过于昂贵,从而保证程序的性能。
        缺点: 如果 n 设置得太小,即使 SetChordError() 要求更高的精度,细分也会提前停止,导致最终模型不够平滑。
        """
        self.tessellate.SetChordError(value)
        self.tessellate.SetMaximumNumberOfSubdivisions(4)
        self.tessellate.Update()
 
        cellMap = dict()
 
        numTets = 0
        cell = vtkGenericCell()
        it = self.tessellate.GetOutput().NewCellIterator()
        it.InitTraversal()
        while not it.IsDoneWithTraversal():
            it.GetCell(cell)
            cellMap[cell.GetRepresentativeCell().GetClassName()] = numTets
            numTets += 1
            it.GoToNextCell()
        ss = '# of Tetras: ' + str(numTets)
        self.textMapper.SetInput(ss)
 
 
def main():
    colors = vtkNamedColors()
    # 创建一个grid
    aHexahedron = vtkQuadraticHexahedron()
    points = vtkPoints()
 
    pcoords = aHexahedron.GetParametricCoords()
    rng = vtkMinimalStandardRandomSequence()
    rng.SetSeed(5070)
    points.SetNumberOfPoints(aHexahedron.GetNumberOfPoints())
    for i in range(0, aHexahedron.GetNumberOfPoints()):
        perturbation = [0] * 3
        for j in range(3):
            rng.Next()
            perturbation[j] = rng.GetRangeValue(-0.1, 0.1)
 
        aHexahedron.GetPointIds().SetId(i, i)
        points.SetPoint(i, pcoords[3 * i] + perturbation[0],
                        pcoords[3 * i + 1] + perturbation[1],
                        pcoords[3 * i + 2] + perturbation[2])
    ug = vtkUnstructuredGrid()
    ug.SetPoints(points)
    ug.InsertNextCell(aHexahedron.GetCellType(), aHexahedron.GetPointIds())
 
    tessellate = vtkTessellatorFilter()
    tessellate.SetInputData(ug)
    """
    SetChordError  控制曲面细分(tessellation)的精确度
    较小的值:会导致更多的细分,生成更多的小多边形
    较大的值:更少的细分
    """
    tessellate.SetChordError(0.035)
    tessellate.Update()
 
    # 统计经过细分过后的网格面情况
    cellMap = {}
    numTests = 0
    """
    vtkGenericCell 是一个通用容器,能装的了任何类型的单元
    遍历网格时,你需要一个"临时盒子"来装当前单元,它就是这个作用
    """
    cell = vtkGenericCell()
    """
    NewCellIterator 返回一个单元迭代器
    """
    it = tessellate.GetOutput().NewCellIterator()
    """
    InitTraversal  把迭代器重置到第一个单元
    """
    it.InitTraversal()
    while not it.IsDoneWithTraversal():
        it.GetCell(cell)  # 把当前单元拷贝到vtkGenericCell
        # 单元类型当作 key,存储当前单元的编号
        cellMap[cell.GetRepresentativeCell().GetClassName()] = numTests
        numTests += 1
        it.GoToNextCell()
 
    mapper = vtkDataSetMapper()
    mapper.SetInputConnection(tessellate.GetOutputPort())
    """
    ScalarVisibilityOff  关闭标量数据的可见性
    """
    mapper.ScalarVisibilityOff()
 
    actor = vtkActor()
    actor.SetMapper(mapper)
    actor.GetProperty().SetDiffuseColor(  # 设置漫反射颜色为番茄红
        colors.GetColor3d('Tomato'))
    actor.GetProperty().SetEdgeColor(  # 设置边线颜色为象牙黑
        colors.GetColor3d('IvoryBlack'))
    actor.GetProperty().EdgeVisibilityOn()  # 打开边线的可见性
 
    # 显示图形上的关键顶点
    sphereSource = vtkSphereSource()
    sphereSource.SetRadius(0.02)
    glyph3d = vtkGlyph3D()
    glyph3d.SetInputData(ug)
    glyph3d.SetSourceConnection(sphereSource.GetOutputPort())
    """
    ScalingOff 关闭三维字形(glyph)的缩放功能
    在默认情况下,vtkGlyph3DMapper 会根据每个点的标量数据来改变字形的大小。
    如果一个点的标量值很大,那么在这个点上绘制的字形就会更大;
    如果标量值很小,字形就会更小。这种功能非常有用,可以用来可视化数据的分布,
    例如显示网格上不同点的压力或温度大小
    """
    glyph3d.ScalingOff()
    glyph3d.Update()
 
    glyph3DMapper = vtkDataSetMapper()
    glyph3DMapper.SetInputConnection(glyph3d.GetOutputPort())
    glyph3DMapper.ScalarVisibilityOff()
 
    glyph3DActor = vtkActor()
    glyph3DActor.SetMapper(glyph3DMapper)
    glyph3DActor.GetProperty().SetColor(colors.GetColor3d('Banana'))
 
    textProperty = vtkTextProperty()
    textProperty.SetFontSize(24)
    ss = '# of Tetras: ' + str(numTests)
    textMapper = vtkTextMapper()
    textMapper.SetInput(ss)
    textMapper.SetTextProperty(textProperty)
 
    textActor = vtkActor2D()
    textActor.SetMapper(textMapper)
    textActor.SetPosition(10, 400)
 
    renderer = vtkRenderer()
    renderWindow = vtkRenderWindow()
    renderWindow.SetWindowName("QuadraticHexahedronDemo")
    renderWindow.AddRenderer(renderer)
    renderWindow.SetSize(640, 512)
 
    interactor = vtkRenderWindowInteractor()
    interactor.SetRenderWindow(renderWindow)
 
    # 设定滑块
    """
    vtkSliderWidget 是 滑块交互控件,它和 vtkSliderRepresentation2D/3D 搭配使用,
    用来在渲染窗口里添加一个可拖动的滑块(slider),并且可以通过回调函数与应用逻辑交互
    vtkSliderRepresentation2D/3D 定义滑块的外观和布局(轨道、端点、滑块大小、数值范围等)。
    vtkSliderWidget  控制交互逻辑,处理鼠标拖动、事件分发,并绑定回
    """
    widget = vtkSliderWidget()
    sliderRepChordError = vtkSliderRepresentation2D()
    sliderRepChordError.SetMinimumValue(0.0)
    sliderRepChordError.SetMaximumValue(0.07)
    sliderRepChordError.SetValue(tessellate.GetChordError())
    sliderRepChordError.SetTitleText("Chord error")
 
    # 设置滑块部件的具体位置
    """
    下面两行SetCoordinateSystemToNormalizedDisplay代码
    作用是将滑块的两个端点设置为归一化显示坐标系
    在这个坐标系中,渲染窗口的左下角是 (0,0),右上角是 (1,1)。这种坐标系与窗口大小无关,
    使得界面元素可以随着窗口的缩放而自动调整位置。
    """
    sliderRepChordError.GetPoint1Coordinate().SetCoordinateSystemToNormalizedDisplay()
    sliderRepChordError.GetPoint2Coordinate().SetCoordinateSystemToNormalizedDisplay()
    sliderRepChordError.GetPoint1Coordinate().SetValue(0.1, 0.1)
    sliderRepChordError.GetPoint2Coordinate().SetValue(0.9, 0.1)
 
    tubeWidth = 0.008
    sliderLength = 0.008
    titleHeight = 0.04
    labelHeight = 0.04
    """
    SetTubeWidth 设置轨道的宽度,也是一个归一化大小 这些值都是相对于滑块的长度
    SetSliderLength() 的参数虽然是归一化比例(0--1),但是 它的"参照物"不是整个 slider 的管道长度
    而是 slider representation 内部定义的归一化范围
    在 VTK 的实现里:
    SetSliderLength(x) 控制的是 滑块手柄沿管道方向所占的比例。
    这个比例再乘上 representation 里定义的 有效范围 (end points -- tube 两端的 margin),
    才是最终手柄能占的长度。
    换句话说,即使你设成 1.0,也不会真的覆盖整个 slider,
    因为 VTK 在两端给手柄留了边界(手柄中心不能超过 slider 的 min/max 端点)
    """
    sliderRepChordError.SetTubeWidth(tubeWidth)
    # SetSliderLength 设置滑块手柄的长度,是用户可以拖动的部分
    sliderRepChordError.SetSliderLength(sliderLength)
    # SetTitleHeight 设置滑块标题的高度,是字体的高度,而非字体在窗口位置的高度  位置通常在滑块上方
    sliderRepChordError.SetLabelHeight(titleHeight)
    # SetLabelHeight  设置滑块标签的高度,通常用来显示当前滑块的值,
    # 设置的是字体的高度,而非字体在窗口位置的高度
    # 默认在滑块的下方
    sliderRepChordError.SetLabelHeight(labelHeight)
 
    """
    SetInteractor   把 slider widget 绑定到某个交互器(vtkRenderWindowInteractor)
    没有这一步,widget 不会接收鼠标事件(点不到、拖不动) 
    相当于告诉 VTK:"这个滑块要监听这个窗口的交互事件。"
    """
    widget.SetInteractor(interactor)
    """
    指定这个 widget 的"外观表示"(representation)
    """
    widget.SetRepresentation(sliderRepChordError)
    """
    设置交互模式为"动画更新"
    VTK 里有两种常见模式:
    Animate:拖动过程中,滑块值会 连续更新,实时触发回调(适合做实时可视化)。
    Jump:拖动结束时(鼠标松开),才更新数值。
    """
    widget.SetAnimationModeToAnimate()
    """
    启用 widget
    默认 widget 是禁用状态,调用 EnabledOn() 之后它才会真正出现并响应事件
    也可以用 widget.EnabledOff() 来隐藏/禁用它
    """
    widget.EnabledOn()
 
    """
    为滑块部件添加一个事件监听器
    当用户与滑块进行交互(interaction)时,这个监听器就会被触发,
    然后执行一个特定的函数(SliderCallbackChordError),从而实现模型的动态更新
    
    参数:event  时间类型
    这个参数指定了你想要监听的事件类
    vtkCommand.InteractionEvent 表示任何用户与部件(如滑块)进行的交互,比如拖动滑块手柄
    
    参数:command 回调函数
    当事件发生时,VTK会调用这个回调函数
    SliderCallbackChordError 是一个自定义的类或函数,它包含了你想要执行的逻辑
    """
    widget.AddObserver(vtkCommand.InteractionEvent, SliderCallbackChordError(tessellate, textMapper))
    # 若想多个事件的监听,则继续添加Observer
 
    renderer.AddActor(actor)
    renderer.AddActor(glyph3DActor)
    renderer.AddViewProp(textActor)
    renderer.SetBackground(colors.GetColor3d('SlateGray'))
 
    renderWindow.Render()
 
    interactor.Start()
 
 
if __name__ == '__main__':
    main()
相关推荐
Q_Q19632884751 小时前
python+django/flask+vue的个性化电影推荐系统
spring boot·python·django·flask·node.js
BD_Marathon1 小时前
【Java】集合里面的数据结构
java·数据结构·python
ULTRA??1 小时前
JPS路径规划(python AI实现)
开发语言·人工智能·python
San30.1 小时前
从 Mobile First 到 AI First:用 Python 和大模型让数据库“开口说话”
数据库·人工智能·python
计算机学姐1 小时前
基于Python的旅游数据分析及可视化系统【2026最新】
vue.js·python·数据挖掘·数据分析·django·旅游·推荐算法
红队it1 小时前
【机器学习】python旅游数据分析可视化协同过滤算法推荐系统(完整系统源码+数据库+开发笔记+详细部署教程)✅
python·mysql·算法·机器学习·数据分析·旅游
曲幽1 小时前
Flask项目结构详解:用蓝图实现优雅的模块化开发
python·web·route·blueprint·register
weixin_421133411 小时前
PyInstaller& Nuitka & 项目 (如 django)
后端·python·django
weixin_462446231 小时前
使用 Python + Tkinter + openpyxl 实现 Excel 文本化转换
开发语言·python·excel