描述:
用户用交互式的方法来生成一个包围盒,通过该包围盒来切割三维网格模型
功能:
按1加鼠标左键:拾取第一个点
按2加鼠标左键:拾取第二个点
按3键:生成一个球型包围盒
按4键:通过球型包围盒切割三维网格,并显示切割区域
代码:
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkMinimalStandardRandomSequence
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkPropPicker,
vtkProperty,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
import vtk
vtk.vtkOutputWindow.SetGlobalWarningDisplay(0)
# from data_utils import VTKLib
import math
''' 三维模型可视化'''
def polydata_visualize(poly):
colors = vtkNamedColors()
mapper = vtkPolyDataMapper()
mapper.SetInputData(poly)
actor = vtkActor()
actor.SetMapper(mapper)
ren = vtkRenderer()
ren.AddActor(actor)
# ren.SetBackground(colors.GetColor3d('DarkOliveGreen'))
ren.SetBackground(colors.GetColor3d('White'))
renWin = vtkRenderWindow()
renWin.AddRenderer(ren)
renWin.SetWindowName('ReadSTL')
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
iren.Initialize()
renWin.Render()
iren.Start()
''' 计算点1和点2的欧氏距离'''
def compute_distance(position1, position2):
xyz1 = list(position1)
xyz2 = list(position2)
return math.sqrt(sum([(a - b) ** 2 for (a, b) in zip(xyz1, xyz2)]))
''' 使用球对球型包围盒进行可视化'''
def GreateBall(center_point):
sphere = vtk.vtkSphereSource()
sphere.SetCenter(center_point)
sphere.SetRadius(1)
sphere.Update()
Mapper = vtkPolyDataMapper()
Mapper.SetInputConnection(sphere.GetOutputPort())
actor = vtkActor()
actor.SetMapper(Mapper)
return actor
''' 计算点1和点2的中心点 '''
def compute_center(position1 ,position2):
point1 = list(position1)
point2 = list(position2)
center_point = []
center_point.append((point1[0] + point2[0]) / 2)
center_point.append((point1[1] + point2[1]) / 2)
center_point.append((point1[2] + point2[2]) / 2)
center_point = tuple(center_point)
return center_point
''' 对Actor的属性进行设置 '''
def SetupActor(actor, color=None, opa=None, pos=None, bVis=None, renderer=None):
if color != None: # 设置颜色
actor.GetProperty().SetColor(color[0], color[1], color[2])
if opa != None: # 设置透明度
actor.GetProperty().SetOpacity(opa)
if pos != None: # 设置位置
actor.SetPosition(pos)
if bVis != None: # 设置可见性
actor.SetVisibility(bVis)
if renderer != None: # 加入渲染器
renderer.AddActor(actor)
''' 使用小球对坐标进行可视化 '''
def MarkWithSphere(actor, color=None, opa=None, pos=None, bVis=None, renderer=None):
sphere = vtk.vtkSphereSource()
sphere.Update()
sphereMapper = vtkPolyDataMapper()
sphereMapper.SetInputConnection(sphere.GetOutputPort())
actor.SetMapper(sphereMapper)
SetupActor(actor, color, opa, pos, bVis, renderer)
''' 创建球型裁剪网格 '''
def CreateBall(actor, distance, color=None, opa=None, pos=None, bVis=None, renderer=None):
sphere = vtk.vtkSphereSource()
sphere.SetRadius(distance)
sphere.SetThetaResolution(1000) # 设置球表面精度,值越大球的光滑程度越高
sphere.Update()
sphereMapper = vtkPolyDataMapper()
sphereMapper.SetInputConnection(sphere.GetOutputPort())
actor.SetMapper(sphereMapper)
SetupActor(actor, color, opa, pos, bVis, renderer)
class MouseInteractorHighLightActor(vtkInteractorStyleTrackballCamera):
def __init__(self, renderer, mesh):
self.AddObserver("LeftButtonPressEvent", self.leftButtonPressEvent) # 鼠标左键消息
# self.AddObserver("RightButtonPressEvent", self.RightButtonPressEvent) # 鼠标左键消息
self.AddObserver('KeyPressEvent', self.key_press_event) # 按键消息
self.renderer = renderer
self.mesh = mesh
self.sphereActor1 = vtkActor() # 定义一个球形,用来标记第一个拾取点
self.sphereActor2 = vtkActor() # 定义一个球形,用来标记第二个拾取点
self.sphereActor3 = vtkActor() # 定义一个球形,用来标记中心点
self.BallActor = vtkActor() # 定义一个球形,用来显示裁剪网格的球
self.mode = 0 # mode=0的时候表示不选点, mode=1的时候表示选第一个点, mode=2的选第二个点
MarkWithSphere(self.sphereActor1, color=[1, 1, 0], renderer=self.renderer, bVis=False)
MarkWithSphere(self.sphereActor2, color=[0, 1, 0], renderer=self.renderer, bVis=False)
MarkWithSphere(self.sphereActor3, color=[1, 1, 1], renderer=self.renderer, bVis=False)
self.posistion1 = () # 点1的坐标
self.posistion2 = () # 点2的坐标
self.center_point = [] # 中点坐标
self.distance = 0 # 点1和点2的欧式距离
self.seg_teeth = None # 被切割的区域
def leftButtonPressEvent(self, obj, event):
if self.mode != 0:
picker = vtk.vtkPointPicker() # 指定拾取器
pos = self.GetInteractor().GetEventPosition() # 获取事件二维屏幕坐标
ren = self.GetDefaultRenderer() # 记得预先通过SetDefaultRenderer指定渲染器
picker.Pick(pos[0], pos[1], 0, ren)
world_position = picker.GetPickPosition() # 获取拾取点的三维坐标
print(world_position)
id = picker.GetPointId()
print(id)
if self.mode == 1:
self.sphereActor1.VisibilityOn() # 显示球型
self.sphereActor1.SetPosition(world_position) # 将球型的中心设为拾取点的坐标
self.posistion1 = world_position
self.mode = 0
elif self.mode == 2:
self.sphereActor2.VisibilityOn() # 显示球型
self.sphereActor2.SetPosition(world_position) # 将球型的中心设为拾取点的坐标
self.posistion2 = world_position
self.mode = 0
self.OnLeftButtonDown()
return
def key_press_event(self, obj, event):
key = self.GetInteractor().GetKeySym()
# if key.lower() == "1" and self.mode == 0: # 指定第一个点
if key.lower() == "1":
self.mode = 1
elif key.lower() == "2":
self.mode = 2
elif key.lower() == "3":
# 计算两个点的中心点
self.center_point = compute_center(self.posistion1, self.posistion2)
print("中心点:", self.center_point)
self.sphereActor3.VisibilityOn() # 显示球型
self.sphereActor3.SetPosition(self.center_point) # 将球型的中心设为拾取点的坐标
# 计算两个点的欧式距离
self.distance = compute_distance(self.posistion1, self.posistion2)
CreateBall(self.BallActor, self.distance, color=[1, 1, 1], renderer=self.renderer, bVis=False)
self.BallActor.GetProperty().SetOpacity(0.4)
self.BallActor.VisibilityOn() # 显示球型
self.BallActor.SetPosition(self.center_point) # 将球型的中心设为拾取点的坐标
elif key.lower() == "4":
sphere = vtk.vtkSphere()
sphere.SetCenter(list(self.center_point))
sphere.SetRadius(self.distance)
clipper = vtk.vtkClipPolyData()
clipper.SetClipFunction(sphere)
clipper.SetInputData(self.mesh)
clipper.GenerateClippedOutputOn()
clipper.Update()
self.seg_teeth = clipper.GetClippedOutput()
polydata_visualize(self.seg_teeth)
self.BallActor.VisibilityOff()
def main():
colors = vtkNamedColors()
reader = vtk.vtkOBJReader()
reader.SetFileName("00OMSZGW_upper.obj")
reader.Update()
mesh = reader.GetOutput()
# 渲染和窗口
renderer = vtkRenderer()
renderer.SetBackground(colors.GetColor3d('SteelBlue'))
renwin = vtkRenderWindow()
renwin.AddRenderer(renderer)
renwin.SetSize(640, 480)
renwin.SetWindowName('HighlightPickedActor')
# 交互器
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(renwin)
# add the custom style
style = MouseInteractorHighLightActor(renderer, mesh)
style.SetDefaultRenderer(renderer)
interactor.SetInteractorStyle(style)
mapper = vtkPolyDataMapper()
mapper.SetInputData(mesh)
actor = vtkActor()
actor.SetMapper(mapper)
renderer.AddActor(actor)
# Start
interactor.Initialize()
renwin.Render()
interactor.Start()
if __name__ == '__main__':
main()
结果:
第一步:选点

第二步:生成包围盒

第三步:切割网格模型并可视化

189

被折叠的 条评论
为什么被折叠?



