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()
相关推荐
m0_748554816 小时前
golang如何实现用户订阅偏好管理_golang用户订阅偏好管理实现总结
jvm·数据库·python
smj2302_796826527 小时前
解决leetcode第3911题.移除子数组元素后第k小偶数
数据结构·python·算法·leetcode
阿正呀8 小时前
Redis怎样实现本地缓存的高效失效通知
jvm·数据库·python
2501_901200538 小时前
mysql如何设置InnoDB引擎参数_优化innodb_buffer_pool
jvm·数据库·python
_.Switch8 小时前
东方财富股票数据JS逆向:secids字段和AES加密实战
开发语言·前端·javascript·网络·爬虫·python·ecmascript
Mr_sst8 小时前
Claude Code 部署与使用保姆级教程(2026 最新)
python·ai
瞎某某Blinder8 小时前
DFT学习记录[6]基于 HES06的能带计算+有效质量计算
python·学习·程序人生·数据挖掘·云计算·学习方法
m0_495496419 小时前
mysql处理复杂SQL性能_InnoDB优化器与MyISAM差异
jvm·数据库·python
forEverPlume10 小时前
PHP怎么使用Eloquent Attribute Composition属性组合_Laravel通过组合构建复杂属性【方法】
jvm·数据库·python
Aleeeeex10 小时前
RAG 那点事:从 8 份企业文档到能用的问答系统,全过程拆给你看
人工智能·python·ai编程