功能
有限元在后处理过程中,我们如果想获取某一个节点的属性数据值,最直接的方法就是点击这个节点,然后显示其属性数据。
代码实现
首先我们需要使用到VTK的点拾取类vtkPointPicker
类。
从需求可知,我们需要与窗口进行交互,所以先自定义一个继承自vtkInteractorStyleTrackballCamera的类(在类中定义了点拾取的交互类型)。我最开始参考了这篇文章VTK:交互与拾取——点拾取的代码,虽然运行成功了,但有些地方似乎不符合预期。第一,其中点拾取代码中有一行为actor->SetScale(0.05);
,即把选中点的圆点标记大小设为常值。带来的结果是,当在一个模型整体尺寸小于这一设定常值(0.05)的时候,标记点会变得特别大(甚至直接覆盖原模型),相反,当模型尺寸远大于这一常值,标记点会变得特别小(很难发现那种)。第二,对于一些不在模型上的点也会被选中并标记,在后处理中,我们希望选中的都是有限元模型节点。对于上述两个问题,我进行了一些优化。优化后的代码如下:
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
| 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) self.textActor = vtk.vtkTextActor() self.textActor.SetPosition(10, 10) self.textActor.GetTextProperty().SetFontSize(24) self.textActor.GetTextProperty().SetColor(241 / 255, 135 / 255, 184 / 255) self.tree = vtk.vtkKdTree() self.tree.BuildLocatorFromPoints(self.points)
def leftButtonPressEvent(self, obj, event): clickPos = self.GetInteractor().GetEventPosition() pointPicker = self.GetInteractor().GetPicker() pointPicker.Pick(clickPos[0], clickPos[1], 0, self.GetDefaultRenderer()) pickId = pointPicker.GetPointId() if pickId != -1: pickPos = pointPicker.GetPickPosition() self.marker_actor.SetPosition(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()
|
为了能根据模型尺寸改变标记点的大小,我目前的思路是:寻找到距离被拾取点最近的一个点(不包括拾取点本身),计算与其之间的距离distance
,然后设置标记对象的尺寸为distance / 5
,即设置标签点的直径为与其最近点的五分之一(反正比distance小就行),这样就实现了动态的尺寸调整。当然这种方法也不是唯一解,只要是能动态合理调节标记对象尺寸的方法都可。因为只有节点才具有ID,所以我们对其ID进行判断,实现只标记节点。
1 2 3
| pickId = pointPicker.GetPointId() if pickId != -1:
|
主干代码如下:
1 2 3 4 5 6 7 8
| def piontPick(self): dataset = self.main_actor.GetMapper().GetInput()
pointPicker = vtk.vtkPointPicker() self.interactor.SetPicker(pointPicker) style = PointPickerInteractorStyle(dataset = dataset) style.SetDefaultRenderer(self.renderer) self.interactor.SetInteractorStyle(style)
|
这里传入数据对象dataset
的原因是实现对不同的模型尺寸动态调节标记尺寸。
结果
下面测试结果演示了标记点随模型整体尺寸动态变化。
![GIF 2022-11-22 16-02-30](D:\OneDrive\桌面\GIF 2022-11-22 16-02-30.gif)
下面测试结果演示了对有限元节点的属性值查询。
![GIF 2022-11-22 15-57-08](D:\OneDrive\桌面\GIF 2022-11-22 15-57-08.gif)
参考
[1] VTK:交互与拾取——点拾取
[2] VTK: vtkKdTree Class Reference
[3] VTK: vtkMath Class Reference
[4] VTK: vtkPointPicker Class Reference