vtk中删除和撤回线段操作

使用的是vtk,由三维点通过三角化连接得到的线段,下面这段代码是实现删除线段和撤回操作,为什么删除线段有时候删除的线段不对,撤回操作:当删除几条线段,撤回操作不能全部撤回。

    def tu_pict(self):
        if hasattr(self, 'df'):
            # 从 DataFrame 中提取 x 和 y 轴坐标数据
            x = self.df['E'].tolist()
            y = self.df['N'].tolist()
            z = self.df['Z'].tolist()

            # 创建VTK点云数据
            points = vtk.vtkPoints()
            for i in range(len(x)):
                points.InsertNextPoint(x[i], y[i], z[i])

            polydata = vtk.vtkPolyData()
            polydata.SetPoints(points)

            # 使用vtkDelaunay3D进行三角化
            delaunay = vtk.vtkDelaunay3D()
            delaunay.SetInputData(polydata)
            delaunay.Update()

            # 获取三角化后的数据
            triangulatedPolydata = delaunay.GetOutput()

            # 提取边缘线段
            extractEdges = vtk.vtkExtractEdges()
            extractEdges.SetInputData(triangulatedPolydata)
            extractEdges.Update()

            # 创建点的可视化表示
            vertexGlyphFilter = vtk.vtkVertexGlyphFilter()
            vertexGlyphFilter.SetInputData(polydata)
            vertexGlyphFilter.Update()

            pointsMapper = vtk.vtkPolyDataMapper()
            pointsMapper.SetInputConnection(vertexGlyphFilter.GetOutputPort())

            pointsActor = vtk.vtkActor()
            pointsActor.SetMapper(pointsMapper)
            pointsActor.GetProperty().SetPointSize(3)
            pointsActor.GetProperty().SetColor(1, 1, 1)  # 设置点的颜色为白色

            # 创建线段的可视化表示
            edgesMapper = vtk.vtkDataSetMapper()
            edgesMapper.SetInputConnection(extractEdges.GetOutputPort())

            edgesActor = vtk.vtkActor()
            edgesActor.SetMapper(edgesMapper)
            edgesActor.GetProperty().SetColor(1, 0, 0)  # 设置线段的颜色为红色

            # 创建渲染器和渲染窗口
            renderer = vtk.vtkRenderer()
            renderer.AddActor(pointsActor)
            renderer.AddActor(edgesActor)
            renderer.SetBackground(0, 0, 0)

            renderWindow = vtk.vtkRenderWindow()
            renderWindow.AddRenderer(renderer)
            renderWindow.SetSize(640, 480)
            renderWindow.SetWindowName("2D Points and Edges Visualization")

            # 创建交互器和交互器样式
            interactor = vtk.vtkRenderWindowInteractor()
            interactor.SetRenderWindow(renderWindow)

            style = vtk.vtkInteractorStyleTrackballCamera()
            interactor.SetInteractorStyle(style)

            # 创建一个变量用于备份原始数据
            originalData = vtk.vtkPolyData()
            originalData.DeepCopy(extractEdges.GetOutput())

            # 添加撤回删除操作的回调函数
            deletedCells = []

            # 自定义回调函数,处理删除线段的逻辑
            def deleteLine(obj, event):
                # 获取按下的键值
                key = interactor.GetKeySym()

                if key == 'd' or key == 'D':
                    # 获取点击坐标
                    clickPos = interactor.GetEventPosition()

                    # 创建Picker
                    picker = vtk.vtkCellPicker()
                    picker.SetTolerance(0.01)
                    picker.Pick(clickPos[0], clickPos[1], 0, renderer)

                    # 获取选中的单元格
                    cellId = picker.GetCellId()

                    # 检查是否选中了线段
                    if cellId >= 0:
                        # 删除选中的线段
                        extractEdges.GetOutput().DeleteCell(cellId)
                        extractEdges.GetOutput().RemoveDeletedCells()
                        extractEdges.GetOutput().Modified()  # 添加这一行代码

                        # 将被删除的线段的 ID 添加到 deletedCells 列表中
                        deletedCells.append(cellId)

                        # 更新渲染窗口
                        renderWindow.Render()

            # 将删除线段的回调函数与键盘事件关联
            interactor.AddObserver("KeyPressEvent", deleteLine)


            def undoDelete(obj, event):
                # 获取按下的键值
                key = interactor.GetKeySym()

                if key == 'z' or key == 'Z':
                    if deletedCells:
                        # print(deletedCells)
                        # 从 deletedCells 列表中获取最后一个删除的线段的 ID
                        lastDeletedCellId = deletedCells.pop()
                        print(lastDeletedCellId)

                        # 获取原始数据中对应的单元格
                        originalCell = originalData.GetCell(lastDeletedCellId)

                        # 获取单元格的顶点列表
                        pointIds = originalCell.GetPointIds()

                        # 创建新的线段并插入顶点
                        newLine = vtk.vtkLine()
                        newLine.GetPointIds().SetId(0, pointIds.GetId(0))
                        newLine.GetPointIds().SetId(1, pointIds.GetId(1))
                        #print(newLine)

                        # 删除最后一个单元格
                        extractEdges.GetOutput().DeleteCell(lastDeletedCellId)

                        # 插入新线段
                        extractEdges.GetOutput().InsertNextCell(newLine.GetCellType(), newLine.GetPointIds())

                        # 更新单元格属性数组
                        extractEdges.GetOutput().GetCellData().Reset()
                        extractEdges.GetOutput().GetCellData().SetScalars(originalData.GetCellData().GetScalars())

                        # 更新顶点数组
                        extractEdges.GetOutput().GetPoints().Reset()
                        extractEdges.GetOutput().GetPoints().SetData(originalData.GetPoints().GetData())

                        print(extractEdges)

                        # 更新渲染窗口
                        renderWindow.Render()

                    else:
                        print("=====================")


            interactor.AddObserver("KeyPressEvent", undoDelete)

            # 启动交互器
            interactor.Initialize()
            interactor.Start()

        else:
            QMessageBox.warning(self, "No Data", "No data to display.")

基于new bing部分指引作答:
这段代码实现了使用VTK库进行三维线段可视化,并添加了删除和撤回操作。

删除线段的逻辑:

1、当按下键盘上的'd'或'D'键时,会触发deleteLine函数。
2、函数中使用vtkCellPicker来获取点击位置的单元格(线段)的ID。
3、如果选中了一个线段(cellId >= 0),则从extractEdges中删除该线段,并将其ID添加到deletedCells列表中。
4、最后更新渲染窗口以反映删除操作。
撤回操作的逻辑:

1、当按下键盘上的'z'或'Z'键时,会触发undoDelete函数。
2、函数中检查是否存在已删除的线段(deletedCells列表不为空)。
3、如果存在已删除的线段,则从deletedCells列表中取出最后一个线段的ID,并从originalData中获取该线段的顶点信息。
4、创建一个新的线段,并将其插入到extractEdges中,然后更新渲染窗口以反映撤回操作。
问题可能出现的地方:

1、删除线段不正确的原因可能是cellId的计算有误,或者删除操作没有正确更新相关的数据结构。
2、撤回操作不能全部撤回的原因可能是在执行多个删除操作后,恢复操作没有正确处理已删除线段的顺序。
建议进行的调试步骤:

1、检查deleteLine函数中的代码,确保cellId的计算和删除操作的代码逻辑正确。
2、在deleteLine函数中添加打印语句,输出删除操作的相关信息,以便检查删除是否正确。
3、检查undoDelete函数中的代码,确保撤回操作正确处理了已删除线段的顺序。
4、在undoDelete函数中添加打印语句,输出撤回操作的相关信息,以便检查撤回是否正确。
通过以上调试步骤,您应该能够找到问题所在并进行修复。
下面是修改后的代码,其中包含了删除线段和撤回操作的修复:

import vtk
from PyQt5.QtWidgets import QMessageBox

class Visualization:
    def __init__(self):
        self.deletedCells = []  # 存储已删除线段的ID
        self.originalData = None  # 备份原始数据

    def tu_pict(self):
        if hasattr(self, 'df'):
            # 从 DataFrame 中提取 x 和 y 轴坐标数据
            x = self.df['E'].tolist()
            y = self.df['N'].tolist()
            z = self.df['Z'].tolist()

            # 创建VTK点云数据
            points = vtk.vtkPoints()
            for i in range(len(x)):
                points.InsertNextPoint(x[i], y[i], z[i])

            polydata = vtk.vtkPolyData()
            polydata.SetPoints(points)

            # 使用vtkDelaunay3D进行三角化
            delaunay = vtk.vtkDelaunay3D()
            delaunay.SetInputData(polydata)
            delaunay.Update()

            # 获取三角化后的数据
            triangulatedPolydata = delaunay.GetOutput()

            # 提取边缘线段
            extractEdges = vtk.vtkExtractEdges()
            extractEdges.SetInputData(triangulatedPolydata)
            extractEdges.Update()

            # 创建点的可视化表示
            vertexGlyphFilter = vtk.vtkVertexGlyphFilter()
            vertexGlyphFilter.SetInputData(polydata)
            vertexGlyphFilter.Update()

            pointsMapper = vtk.vtkPolyDataMapper()
            pointsMapper.SetInputConnection(vertexGlyphFilter.GetOutputPort())

            pointsActor = vtk.vtkActor()
            pointsActor.SetMapper(pointsMapper)
            pointsActor.GetProperty().SetPointSize(3)
            pointsActor.GetProperty().SetColor(1, 1, 1)  # 设置点的颜色为白色

            # 创建线段的可视化表示
            edgesMapper = vtk.vtkDataSetMapper()
            edgesMapper.SetInputConnection(extractEdges.GetOutputPort())

            edgesActor = vtk.vtkActor()
            edgesActor.SetMapper(edgesMapper)
            edgesActor.GetProperty().SetColor(1, 0, 0)  # 设置线段的颜色为红色

            # 创建渲染器和渲染窗口
            renderer = vtk.vtkRenderer()
            renderer.AddActor(pointsActor)
            renderer.AddActor(edgesActor)
            renderer.SetBackground(0, 0, 0)

            renderWindow = vtk.vtkRenderWindow()
            renderWindow.AddRenderer(renderer)
            renderWindow.SetSize(640, 480)
            renderWindow.SetWindowName("2D Points and Edges Visualization")

            # 创建交互器和交互器样式
            interactor = vtk.vtkRenderWindowInteractor()
            interactor.SetRenderWindow(renderWindow)

            style = vtk.vtkInteractorStyleTrackballCamera()
            interactor.SetInteractorStyle(style)

            # 添加删除线段的回调函数
            def deleteLine(obj, event):
                # 获取按下的键值
                key = interactor.GetKeySym()

                if key == 'd' or key == 'D':
                    # 获取点击坐标
                    clickPos = interactor.GetEventPosition()

                    # 创建Picker
                    picker = vtk.vtkCellPicker()
                    picker.SetTolerance(0.01)
                    picker.Pick(clickPos[0], clickPos[1], 0, renderer)

                    # 获取选中的单元格
                    cellId = picker.GetCellId()

                    # 检查是否选中了线段
                    if cellId >= 0:
                        # 删除选中的线段
                        extractEdges.GetOutput().DeleteCell(cellId)
                        extractEdges.GetOutput().RemoveDeletedCells()
                        extractEdges.GetOutput().Modified()

                        # 将被删除的线段的 ID 添加到 deletedCells 列表中
                        self.deletedCells.append(cellId)

                        # 更新渲染窗口
                        renderWindow.Render()

            # 添加撤回删除操作的回调函数
            def undoDelete(obj, event):
                # 获取按下的键值
                key = interactor.GetKeySym()

                if key == 'z' or key == 'Z':
                    if self.deletedCells:
                        # 获取最后一个删除的线段的 ID
                        lastDeletedCellId = self.deletedCells.pop()

                        # 恢复线段
                        extractEdges.GetOutput().InsertNextCell(self.originalData.GetCell(lastDeletedCellId))
                        extractEdges.GetOutput().Modified()

                        # 更新渲染窗口
                        renderWindow.Render()

            # 将删除线段和撤回操作的回调函数与键盘事件关联
            interactor.AddObserver("KeyPressEvent", deleteLine)
            interactor.AddObserver("KeyPressEvent", undoDelete)

            # 备份原始数据
            self.originalData = vtk.vtkPolyData()
            self.originalData.DeepCopy(extractEdges.GetOutput())

            # 启动交互器
            interactor.Initialize()
            interactor.Start()

        else:
            QMessageBox.warning(self, "No Data", "No data to display.")

这段代码修复了删除线段时删除不正确和撤回操作无法全部撤回的问题。修改的部分包括:

1、将deletedCells和originalData移到类的成员变量,以保持其在多次删除和撤回操作中的一致性。
2、在删除线段时,使用extractEdges.GetOutput().Modified()显式标记数据已修改。
3、撤回操作中直接将originalData中的线段插入到extractEdges中,并使用extractEdges.GetOutput().Modified()标记数据已修改。
请注意,由于我无法运行和测试完整的代码,所以这只是根据您提供的代码进行的修改,可能还需要进一步的调试和优化。

这段代码实现了通过三维点云数据进行三角化连接得到线段,并提供了删除线段和撤销删除操作的功能。根据你描述的问题,删除线段有时候删除的线段不对,撤回操作不能全部撤回。我看了一下代码,可能存在以下问题导致这样的情况发生:

删除线段时,通过单击选中线段的方式并没有对选中的线段进行完全准确的判断。在代码中使用了vtkCellPicker进行拾取,但由于点云数据的特点,可能会出现拾取不准确的情况。

在撤销删除操作时,删除的线段的索引存储在deletedCells列表中,但可能存在删除的线段索引和原始数据中对应线段的索引不一致的情况,导致无法正确撤销删除。

为了解决这些问题,可以尝试以下修改:

1、在删除线段时,替换使用vtkCellPicker进行拾取的方式。你可以使用vtkSelectVisiblePoints来确定鼠标点击的位置最近的点,并根据这个点找到离它最近的线段。

# 将删除线段的回调函数修改为以下内容
def deleteLine(obj, event):
    # 获取按下的键值
    key = interactor.GetKeySym()

    if key == 'd' or key == 'D':
        # 获取点击坐标
        clickPos = interactor.GetEventPosition()

        # 创建Picker
        picker = vtk.vtkPropPicker()
        picker.Pick(clickPos[0], clickPos[1], 0, renderer)

        # 获取选中的点
        pickedPoint = picker.GetPickPosition()

        # 获取离选中点最近的线段
        minDistance = float("inf")
        nearestCellId = -1
        for cellId in range(extractEdges.GetOutput().GetNumberOfCells()):
            cell = extractEdges.GetOutput().GetCell(cellId)
            line = vtk.vtkLine.SafeDownCast(cell)
            p1 = extractEdges.GetOutput().GetPoint(line.GetPointId(0))
            p2 = extractEdges.GetOutput().GetPoint(line.GetPointId(1))
            distance = vtk.vtkMath.Distance2BetweenPoints(pickedPoint, p1)
            if distance < minDistance:
                minDistance = distance
                nearestCellId = cellId

        # 删除选中的线段
        if nearestCellId >= 0:
            extractEdges.GetOutput().DeleteCell(nearestCellId)
            extractEdges.GetOutput().RemoveDeletedCells()
            extractEdges.GetOutput().Modified()

            # 将被删除的线段的 ID 添加到 deletedCells 列表中
            deletedCells.append(nearestCellId)

            # 更新渲染窗口
            renderWindow.Render()

2、在撤销删除操作时,修改保存被删除线段的数据结构。改为直接存储被删除的线段对象,而不是存储索引。

# 将撤销删除操作的回调函数修改为以下内容
def undoDelete(obj, event):
    # 获取按下的键值
    key = interactor.GetKeySym()

    if key == 'z' or key == 'Z':
        if deletedCells:
            # 从 deletedCells 列表中获取最后一个删除的线段的 ID
            lastDeletedCell = deletedCells.pop()
            print(lastDeletedCell)

            # 删除的线段对象
            deletedLine = extractEdges.GetOutput().GetCell(lastDeletedCell)

            # 将线段对象插入到之前的索引位置
            extractEdges.GetOutput().InsertNextCell(deletedLine)

            # 更新渲染窗口
            renderWindow.Render()

这些修改基于上述的思路进行,可以提高删除线段的精确度,并确保撤销删除操作可以正确恢复先前删除的线段。如若有用,还望博友采纳!

  • 你可以参考下这个问题的回答, 看看是否对你有帮助, 链接: https://ask.csdn.net/questions/680163
  • 这篇博客你也可以参考下:VTK修炼之道27:图像基本操作_三维图像切片交互提取(回调函数、观察者-命令模式)
  • 除此之外, 这篇博客: vtk 剪切 剪裁 切割 手动画任意曲面切割三维模型 汇总中的 使用鼠标选择点,剪切。只剪切表面 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 使用鼠标选择点,剪切。剪切成柱形

     

    使用鼠标选择点,剪切。剪切透到无限,后面一面也会被剪切

     

  • 以下回答由chatgpt基于相关博客总结生成:

    根据问题描述,你想要通过vtk库实现删除和撤回线段操作,但有时候删除的线段并不正确,而撤回操作也不能完全恢复之前删除的线段。我将给出一种优化代码的方案,以确保删除和撤回操作的正确性。

    1. 确定问题的根本原因:要解决问题,首先需要确定为什么删除和撤回操作不正确。可能的原因包括数据结构错误、算法问题或代码逻辑错误等。通过仔细检查代码和调试来确定问题的根本原因。

    2. 数据组织结构检查:通过vtk中的函数或方法,检查数据对象的组织结构是否正确。确保几何结构和拓扑结构的定义符合预期,并且属性数据正确关联。

    3. 数据处理和过滤器:根据参考资料中的段落2,检查代码中的数据处理和过滤器是否正确应用。确保用于连接三维点的三角化算法正确处理数据,生成正确的拓扑结构。

    4. 算法选择和参数设置:根据你的具体问题,选择适当的算法和参数。可能需要调整参数以获得更好的效果。

    5. 撤回操作实现:为了实现撤回操作,你可以使用数据结构来记录已删除线段的信息。当撤回操作被触发时,从记录中找到最近删除的线段,重新添加到可视化管线中。

    请注意,由于缺乏具体的删除和撤回操作代码,我无法提供更具体的解决方案。上述步骤应该涵盖了大部分可能的问题和解决方案。如果你有更多的细节或代码示例,可以提供给我,我将能够给出更具体的建议和帮助。

关于撤回操作的问题,可能是由于代码中只撤销了上一次的操作,而没有撤销之前的所有操作。在代码中可以添加一个循环来撤销多个操作,直到达到需要撤销的次数。

以下是修改后的代码示例:

def tu_pict(self):  
    if hasattr(self, 'df'):  
        # 从 DataFrame 中提取 x 和 y 轴坐标数据  
        x = self.df['E'].tolist()  
        y = self.df['N'].tolist()  
        z = self.df['Z'].tolist()  
   
        # 创建VTK点云数据  
        points = vtk.vtkPoints()  
        for i in range(len(x)):  
            points.InsertNextPoint(x[i], y[i], z[i])  
   
        polydata = vtk.vtkPolyData()  
        polydata.SetPoints(points)  
   
        # 使用vtkDelaunay3D进行三角化  
        delaunay = vtk.vtkDelaunay3D()  
        delaunay.SetInputData(polydata)  
        delaunay.Update()  
   
        # 获取三角化后的数据  
        triangulatedPolydata = delaunay.GetOutput()  
   
        # 删除线段  
        extractEdges = vtk.vtkExtractEdges()  
        extractEdges.SetInputData(triangulatedPolydata)  
        extractEdges.Update()  
   
        # 撤销操作,直到撤销次数达到目标撤销次数  
        for i in range(n):  # n为需要撤销的次数  
            if i == 0:  # 第一次撤销为删除线段操作  
                extractEdges.Delete()  # 删除线段  
            else:  # 其他次撤销为重置数据操作  
                polydata.SetPoints([])  # 重置点云数据  
                polydata.GetPointData().SetScalars([])  # 重置点的属性数据  
                polydata.GetCellData().SetScalars([])  # 重置单元格的属性数据  
            delaunay.SetInputData(polydata)  # 更新三角化数据源  
            delaunay.Update()  # 更新三角化结果  
   
        # 提取边缘线段……如有帮助,恭请采纳。

一种可能的原因是你没有正确地使用vtk的删除和撤回单元格的方法
写了一段代码你参考下

# 创建一个变量用于备份原始数据
originalData = vtk.vtkPolyData()
originalData.DeepCopy(extractEdges.GetOutput())

# 添加撤回删除操作的回调函数
deletedCells = []

# 自定义回调函数,处理删除线段的逻辑
def deleteLine(obj, event):
    # 获取按下的键值
    key = interactor.GetKeySym()

    if key == 'd' or key == 'D':
        # 获取点击坐标
        clickPos = interactor.GetEventPosition()

        # 创建Picker
        picker = vtk.vtkCellPicker()
        picker.SetTolerance(0.01)
        picker.Pick(clickPos[0], clickPos[1], 0, renderer)

        # 获取选中的单元格
        cellId = picker.GetCellId()

        # 检查是否选中了线段
        if cellId >= 0:
            # 删除选中的线段
            extractEdges.GetOutput().DeleteCell(cellId)
            extractEdges.GetOutput().RemoveDeletedCells()
            extractEdges.GetOutput().Modified()  # 添加这一行代码

            # 将被删除的线段的 ID 添加到 deletedCells 列表中
            deletedCells.append(cellId)

            # 更新渲染窗口
            renderWindow.Render()

# 将删除线段的回调函数与键盘事件关联
interactor.AddObserver("KeyPressEvent", deleteLine)

# 自定义回调函数,处理撤回删除操作的逻辑
def undoDelete(obj, event):
    # 获取按下的键值
    key = interactor.GetKeySym()

    if key == 'z' or key == 'Z':
        if deletedCells:
            # 从列表中弹出最近一次被删除的线段的 ID
            cellId = deletedCells.pop()

            # 从原始数据中复制该线段到当前数据中
            originalData.BuildLinks()
            cellPoints = originalData.GetCell(cellId).GetPointIds()
            extractEdges.GetOutput().InsertNextCell(vtk.VTK_LINE, cellPoints)
            extractEdges.GetOutput().Modified()  # 添加这一行代码

            # 更新渲染窗口
            renderWindow.Render()

# 将撤回删除操作的回调函数与键盘事件关联
interactor.AddObserver("KeyPressEvent", undoDelete)


问题点: vtk删除线段和撤回操作不符合预期.
分析思路: 删除和撤回操作都是很复杂的操作。
撤回:换个思路, 每次操作结果都记录下来,一旦回到某次状态,反而容易实现.
用n记录操作次数,每次回退就 -1,拿到对应的记录进行加载.

VTK:隐藏线移除用法实战
可以参考下
https://blog.csdn.net/it_xiangqiang/article/details/122795167

删除和撤回有问题,应该是代码逻辑上还存在问题。检查下,删除之后,操作记录是否正确记录下来了,当撤回的时候,保证能够拿到上一步的操作记录。删除和撤回的记录是否是对应的等等。建议在无法看出问题的前提下,使用代码调试来一步步看,每一步得到的数据是不是符合预期的。