VTK交互式分割三维网格

描述:

        用户用交互式的方法来生成一个包围盒,通过该包围盒来切割三维网格模型

功能:

        按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()

结果:

第一步:选点

第二步:生成包围盒

 

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

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值