有限元在后处理过程中,我们如果想获取某一个节点的属性数据值,最直接的方法就是点击这个节点,然后显示其属性数据。
代码实现首先我们需要使用到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 Attribute Value: %.2f" % ( pickPos[0], pickPos[1], pickPos[2], scalars.GetValue(pickId))) else: self.textActor.SetInput("Picked Point: %.2f %.2f %.2f " % (pickPos[0], pickPos[1], pickPos[2])) self.GetDefaultRenderer().AddActor2D(self.textActor) self.OnLeftButtonDown()为了能根据模型尺寸改变标记点的大小,我目前的思路是:寻找到距离被拾取点最近的一个点(不包括拾取点本身),计算与其之间的距离distance,然后设置标记对象的尺寸为distance / 5,即设置标签点的直径为与其最近点的五分之一(反正比distance小就行),这样就实现了动态的尺寸调整。当然这种方法也不是唯一解,只要是能动态合理调节标记对象尺寸的方法都可。因为只有节点才具有ID,所以我们对其ID进行判断,实现只标记节点。
pickId = pointPicker.GetPointId() # 获取拾取点的ID,无ID返回-1 if pickId != -1: # 标记代码主干代码如下:
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)这里传入数据对象dataset的原因是实现对不同的模型尺寸动态调节标记尺寸。
结果下面测试结果演示了标记点随模型整体尺寸动态变化。 下面测试结果演示了对有限元节点的属性值查询。
参考[1] VTK:交互与拾取——点拾取 [2] VTK: vtkKdTree Class Reference [3] VTK: vtkMath Class Reference [4] VTK: vtkPointPicker Class Reference