• 【VTK+有限元后处理】节点属性值查询


    功能

    有限元在后处理过程中,我们如果想获取某一个节点的属性数据值,最直接的方法就是点击这个节点,然后显示其属性数据。

    代码实现

    首先我们需要使用到VTK的点拾取类vtkPointPicker类。

    从需求可知,我们需要与窗口进行交互,所以先自定义一个继承自vtkInteractorStyleTrackballCamera的类(在类中定义了点拾取的交互类型)。我最开始参考了这篇文章VTK:交互与拾取——点拾取的代码,虽然运行成功了,但有些地方似乎不符合预期。第一,其中点拾取代码中有一行为actor->SetScale(0.05);,即把选中点的圆点标记大小设为常值。带来的结果是,当在一个模型整体尺寸小于这一设定常值(0.05)的时候,标记点会变得特别大(甚至直接覆盖原模型),相反,当模型尺寸远大于这一常值,标记点会变得特别小(很难发现那种)。第二,对于一些不在模型上的点也会被选中并标记,在后处理中,我们希望选中的都是有限元模型节点。对于上述两个问题,我进行了一些优化。优化后的代码如下:

    class PointPickerInteractorStyle(vtk.vtkInteractorStyleTrackballCamera):
    
        def __init__(self, parent = None, dataset = None):
            self.AddObserver("LeftButtonPressEvent", self.leftButtonPressEvent)
            self.dataset = dataset
            self.points = dataset.GetPoints()
            # 对拾取点进行标记
            sphereSource = vtk.vtkSphereSource()
            sphereSource.Update()
            self.mapper = vtk.vtkPolyDataMapper()
            self.mapper.SetInputConnection(sphereSource.GetOutputPort())
            self.marker_actor = vtk.vtkActor()
            self.marker_actor.SetMapper(self.mapper)
            # Setup the text and add it to the renderer
            self.textActor = vtk.vtkTextActor()
            self.textActor.SetPosition(10, 10)
            self.textActor.GetTextProperty().SetFontSize(24)
            self.textActor.GetTextProperty().SetColor(241 / 255, 135 / 255, 184 / 255)
            # kd-tree
            self.tree = vtk.vtkKdTree()
            self.tree.BuildLocatorFromPoints(self.points)
    
        def leftButtonPressEvent(self, obj, event):
            clickPos = self.GetInteractor().GetEventPosition()
            # 打印鼠标左键像素位置
            # print(f"Picking pixel: {clickPos[0]} {clickPos[1]}")
            # 注册拾取点函数
            pointPicker = self.GetInteractor().GetPicker()
            pointPicker.Pick(clickPos[0], clickPos[1], 0, self.GetDefaultRenderer())
            # 打印拾取点空间位置
            pickId = pointPicker.GetPointId()  # 获取拾取点的ID,无ID返回-1
            if pickId != -1:
                # 显示模型上被拾取的点
                pickPos = pointPicker.GetPickPosition()
                # print(f"Picked value: {pickPos[0]} {pickPos[1]} {pickPos[2]}")
                self.marker_actor.SetPosition(pickPos)
                # Find the 2 closest points to pickPos
                ClosestIdList = vtk.vtkIdList()
                self.tree.FindClosestNPoints(2, pickPos, ClosestIdList)
                pt1 = self.points.GetPoint(ClosestIdList.GetId(0))
                pt2 = self.points.GetPoint(ClosestIdList.GetId(1))
                distance = np.sqrt(vtk.vtkMath.Distance2BetweenPoints(pt1, pt2))
                self.marker_actor.SetScale(distance / 5)
                self.marker_actor.GetProperty().SetColor(0.0, 1.0, 0.0)
                self.GetDefaultRenderer().AddActor(self.marker_actor)
                # 打印节点信息
                if self.dataset.GetPointData().GetScalars():
                    scalars = self.dataset.GetPointData().GetScalars()
                    self.textActor.SetInput("Picked Point: %.2f %.2f %.2f\nAttribute Value: %.2f" % (
                        pickPos[0], pickPos[1], pickPos[2], scalars.GetValue(pickId)))
                else:
                    self.textActor.SetInput("Picked Point: %.2f %.2f %.2f\n" % (pickPos[0], pickPos[1], pickPos[2]))
                self.GetDefaultRenderer().AddActor2D(self.textActor)
            self.OnLeftButtonDown()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    为了能根据模型尺寸改变标记点的大小,我目前的思路是:寻找到距离被拾取点最近的一个点(不包括拾取点本身),计算与其之间的距离distance,然后设置标记对象的尺寸为distance / 5,即设置标签点的直径为与其最近点的五分之一(反正比distance小就行),这样就实现了动态的尺寸调整。当然这种方法也不是唯一解,只要是能动态合理调节标记对象尺寸的方法都可。因为只有节点才具有ID,所以我们对其ID进行判断,实现只标记节点。

    pickId = pointPicker.GetPointId()  # 获取拾取点的ID,无ID返回-1
            if pickId != -1:
            	# 标记代码
    
    • 1
    • 2
    • 3

    主干代码如下:

    def piontPick(self):
        dataset = self.main_actor.GetMapper().GetInput()
    
        pointPicker = vtk.vtkPointPicker()
        self.interactor.SetPicker(pointPicker)  # 设置pointPicker
        style = PointPickerInteractorStyle(dataset = dataset)  # 设置自定义的点拾取交互类型
        style.SetDefaultRenderer(self.renderer)
        self.interactor.SetInteractorStyle(style)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这里传入数据对象dataset的原因是实现对不同的模型尺寸动态调节标记尺寸。

    结果

    下面测试结果演示了标记点随模型整体尺寸动态变化。
    在这里插入图片描述
    下面测试结果演示了对有限元节点的属性值查询。
    在这里插入图片描述

    参考

    [1] VTK:交互与拾取——点拾取
    [2] VTK: vtkKdTree Class Reference
    [3] VTK: vtkMath Class Reference
    [4] VTK: vtkPointPicker Class Reference

  • 相关阅读:
    TinyEngine 开源低代码引擎首次直播答疑Q&A合集
    移动硬盘raw怎么办?一招教你解决RAW格式的文件
    pytorch nn.Embedding 读取gensim训练好的词/字向量(有例子)
    SQALE 是什么
    【前端】调试技巧
    数据治理的数字画像
    【每日力扣】240. 搜索二维矩阵 II与48. 旋转图像
    牛客SQL实战
    [R]第二节 练习一关于数值向量
    Xenomai 再探
  • 原文地址:https://blog.csdn.net/qq_39784672/article/details/127984852