代码之家  ›  专栏  ›  技术社区  ›  ngunha02

闭合网格和平面之间的间隙

vtk
  •  0
  • ngunha02  · 技术社区  · 6 年前

    我已将.obj网格文件转换为.stl。我还创建了一个平面几何图形作为网格对象的保持器,然后合并它们。但是,它包含一个缺口。 有什么建议,如何垂直闭合间隙,使其成为一个固体物体,我已经尝试过vtkfillholesfilter和手动方式,但它不会填补这个间隙?

    我附上了截图,截图上的红线就是我想要关闭它的地方。 enter image description here

    创建平面的代码:

    implicitPolyDataDistance = vtk.vtkImplicitPolyDataDistance()
    implicitPolyDataDistance.SetInput(stl_poly_data)
    
    #create a grid
    xCoords = vtk.vtkFloatArray()
    for x, i in enumerate(np.linspace(xmin, xmax,50)):
        xCoords.InsertNextValue(i)
    
    yCoords = vtk.vtkFloatArray()
    for y, i in enumerate(np.linspace(ymin, ymax, 50)):
        yCoords.InsertNextValue(i)
    
    zCoords = vtk.vtkFloatArray()
    for z, i in enumerate(np.linspace(zmin, zmin + 1, 50)):
        zCoords.InsertNextValue(i)
    
    rgrid = vtk.vtkRectilinearGrid()
    rgrid.SetDimensions(x + 1, y + 1 , z + 1)
    rgrid.SetXCoordinates(xCoords)
    rgrid.SetYCoordinates(yCoords)
    rgrid.SetZCoordinates(zCoords)
    signedDistances = vtk.vtkFloatArray()
    signedDistances.SetNumberOfComponents(1)
    signedDistances.SetName("SignedDistances")
    
    # Evaluate the signed distance function at all of the grid points
    for pointId in range(rgrid.GetNumberOfPoints()):
        p = rgrid.GetPoint(pointId)
        signedDistance = implicitPolyDataDistance.EvaluateFunction(p)
        signedDistances.InsertNextValue(signedDistance)
    
    # add the SignedDistances to the grid
    rgrid.GetPointData().SetScalars(signedDistances)
    
    # geometry filter to view the background grid
    geometryFilter = vtk.vtkRectilinearGridGeometryFilter()
    geometryFilter.SetInputData(rgrid)
    geometryFilter.SetExtent(0, x + 1, 0, y + 1, (z + 1) // 2, (z + 1) // 2)
    geometryFilter.Update()
    # ================ END creating a plane =======================
    

    合并stl poly数据和平面的代码:

    meshAppend = vtk.vtkAppendPolyData()
    meshAppend.AddInputData(stl_poly_data)
    meshAppend.AddInputData(geometryFilter.GetOutput())
    meshAppend.Update()
    boundaryClean = vtk.vtkCleanPolyData()
    boundaryClean.SetInputData(meshAppend.GetOutput())
    boundaryClean.Update()
    out = vtk.vtkPolyData()
    out.DeepCopy(boundaryClean.GetOutput())
    
    triangleTrans = vtk.vtkTriangleFilter()
    triangleTrans.SetInputData(out)
    triangleTrans.Update()
    
    fill = vtk.vtkFillHolesFilter()
    fill.SetInputData(out)
    fill.SetHoleSize(1000000.0)
    fill.Update()
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   normanius    6 年前

    vtkFillHolesFilter 设计用于填充孔,而不是填充两个可能断开的曲面补片之间的任意间隙。

    可能对你有用的是在你的自由曲面和平面补丁之间缝合一个“接缝”,正如我在这篇文章中所描绘的: How to triangulate two non-intersecting polylines in 3D . 其思想是沿着边缘走,并根据一些简单的启发式方法创建新的接缝边缘。虽然缝合工作良好,如果边缘不是在一个平面上,它可能会失败,如果边缘分叉太强烈或太突然。另外,我建议沿边的点的数量大致是相同的数量级。

    enter image description here


    更新 :我添加了一个函数示例,演示了示例的用法。

    重要 :缝合算法要求边缘方向相同!这对于我们为你的表面建造底部的方式来说是很自然的。

    enter image description here

    # This code has been written by normanius under the CC BY-SA 4.0 license.
    # License:    https://creativecommons.org/licenses/by-sa/4.0/
    # Author:     normanius: https://stackoverflow.com/users/3388962/normanius
    # Date:       July 2018
    # Reference:  https://stackoverflow.com/questions/51321415
    
    import os
    import vtk
    import numpy as np
    
    def extract_points(source):
        # Travers the cells and add points while keeping their order.
        points = source.GetPoints()
        cells = source.GetLines()
        cells.InitTraversal()
        idList = vtk.vtkIdList()
        pointIds = []
        while cells.GetNextCell(idList):
            for i in range(0, idList.GetNumberOfIds()):
                pId = idList.GetId(i)
                # Only add the point id if the previously added point does not
                # have the same id. Avoid p->p duplications which occur for example
                # if a poly-line is traversed. However, other types of point
                # duplication currently are not avoided: a->b->c->a->d
                if len(pointIds)==0 or pointIds[-1]!=pId:
                    pointIds.append(pId)
        result = []
        for i in pointIds:
            result.append(points.GetPoint(i))
        return result
    
    def reverse_lines(source):
        strip = vtk.vtkStripper()
        strip.SetInputData(source)
        strip.Update()
        reversed = vtk.vtkReverseSense()
        reversed.SetInputConnection(strip.GetOutputPort())
        reversed.Update()
        return reversed.GetOutput()
    
    def find_closest_point(points, samplePoint):
        points = np.asarray(points)
        assert(len(points.shape)==2 and points.shape[1]==3)
        nPoints = points.shape[0]
        diff = np.array(points) - np.tile(samplePoint, [nPoints, 1])
        pId = np.argmin(np.linalg.norm(diff, axis=1))
        return pId
    
    def stitch(edge1, edge2):
        # Extract points along the edge line (in correct order).
        # The following further assumes that the polyline has the
        # same orientation (clockwise or counterclockwise).
        points1 = extract_points(edge1)
        points2 = extract_points(edge2)
        n1 = len(points1)
        n2 = len(points2)
    
        # Prepare result containers.
        # Variable points concatenates points1 and points2.
        # Note: all indices refer to this targert container!
        points = vtk.vtkPoints()
        cells = vtk.vtkCellArray()
        points.SetNumberOfPoints(n1+n2)
        for i, p1 in enumerate(points1):
            points.SetPoint(i, p1)
        for i, p2 in enumerate(points2):
            points.SetPoint(i+n1, p2)
    
        # The following code stitches the curves edge1 with (points1) and
        # edge2 (with points2) together based on a simple growing scheme.
    
        # Pick a first stitch between points1[0] and its closest neighbor
        # of points2.
        i1Start = 0
        i2Start = find_closest_point(points2, points1[i1Start])
        i2Start += n1 # offset to reach the points2
    
        # Initialize
        i1 = i1Start
        i2 = i2Start
        p1 = np.asarray(points.GetPoint(i1))
        p2 = np.asarray(points.GetPoint(i2))
        mask = np.zeros(n1+n2, dtype=bool)
        count = 0
        while not np.all(mask):
            count += 1
            i1Candidate = (i1+1)%n1
            i2Candidate = (i2+1+n1)%n2+n1
            p1Candidate = np.asarray(points.GetPoint(i1Candidate))
            p2Candidate = np.asarray(points.GetPoint(i2Candidate))
            diffEdge12C = np.linalg.norm(p1-p2Candidate)
            diffEdge21C = np.linalg.norm(p2-p1Candidate)
    
            mask[i1] = True
            mask[i2] = True
            if diffEdge12C < diffEdge21C:
                triangle = vtk.vtkTriangle()
                triangle.GetPointIds().SetId(0,i1)
                triangle.GetPointIds().SetId(1,i2)
                triangle.GetPointIds().SetId(2,i2Candidate)
                cells.InsertNextCell(triangle)
                i2 = i2Candidate
                p2 = p2Candidate
            else:
                triangle = vtk.vtkTriangle()
                triangle.GetPointIds().SetId(0,i1)
                triangle.GetPointIds().SetId(1,i2)
                triangle.GetPointIds().SetId(2,i1Candidate)
                cells.InsertNextCell(triangle)
                i1 = i1Candidate
                p1 = p1Candidate
    
        # Add the last triangle.
        i1Candidate = (i1+1)%n1
        i2Candidate = (i2+1+n1)%n2+n1
        if (i1Candidate <= i1Start) or (i2Candidate <= i2Start):
            if i1Candidate <= i1Start:
                iC = i1Candidate
            else:
                iC = i2Candidate
            triangle = vtk.vtkTriangle()
            triangle.GetPointIds().SetId(0,i1)
            triangle.GetPointIds().SetId(1,i2)
            triangle.GetPointIds().SetId(2,iC)
            cells.InsertNextCell(triangle)
    
        poly = vtk.vtkPolyData()
        poly.SetPoints(points)
        poly.SetPolys(cells)
        poly.BuildLinks()
    
        return poly
    
    def add_to_renderer(renderer, item, color, opacity=1., linewidth=None):
        colors = vtk.vtkNamedColors()
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetScalarVisibility(False)
        if hasattr(item, 'GetOutputPort'):
            mapper.SetInputConnection(item.GetOutputPort())
        elif isinstance(item, vtk.vtkPolyData):
            mapper.SetInputData(item)
        actor = vtk.vtkActor()
        actor.SetMapper(mapper)
        actor.GetProperty().SetColor(colors.GetColor3d(color))
        actor.GetProperty().SetOpacity(opacity)
        if linewidth:
            actor.GetProperty().SetLineWidth(linewidth)
        renderer.AddActor(actor)
        return mapper, actor
    
    ################################################################################
    def run():
        # Retrieve the original stl file.
        reader = vtk.vtkSTLReader()
        reader.SetFileName('improve.stl')
        reader.Update()
    
        # Extract the boundary edge in a continuous order.
        edge1 = vtk.vtkFeatureEdges()
        edge1.SetInputData(reader.GetOutput())
        edge1.SetBoundaryEdges(1)
        edge1.SetFeatureEdges(0)
        edge1.SetNonManifoldEdges(0)
        edge1.SetManifoldEdges(0)
        edge1.Update()
        boundaryStrips = vtk.vtkStripper()
        boundaryStrips.SetInputConnection(edge1.GetOutputPort())
        boundaryStrips.Update()
        edge1 = boundaryStrips.GetOutput()
    
        # Project the boundary edge to xy-plane.
        edge2 = vtk.vtkPolyData()
        edge2.DeepCopy(edge1)
        points2 = edge2.GetPoints()
        for i in range(edge2.GetNumberOfPoints()):
            p = list(points2.GetPoint(i))
            p[2] = 1125
            points2.SetPoint(i, p)
    
        bottom = vtk.vtkPolyData()
        bottom.DeepCopy(reader.GetOutput())
        points = bottom.GetPoints()
        for i in range(points.GetNumberOfPoints()):
            p = list(points.GetPoint(i))
            p[2] = 1125
            points.SetPoint(i, p)
    
        # Perform the stitching.
        # Requirement: edge1 and edge2 must be oriented the same way!
        #edge2 = reverse_lines(edge2)
        stitching = stitch(edge1, edge2)
    
        # Combine everything into one polydata object.
        combo = vtk.vtkAppendPolyData()
        combo.AddInputData(reader.GetOutput())
        combo.AddInputData(stitching)
        combo.AddInputData(bottom)
        combo.Update()
    
        writerFinal = vtk.vtkSTLWriter()
        writerFinal.SetFileTypeToBinary()
        writerFinal.SetInputData(combo.GetOutput())
        writerFinal.SetFileName('output/test2.stl')
        writerFinal.Update()
        writerFinal.Write()
    
        # Visualize.
        renderer = vtk.vtkRenderer()
        opacity=1.0
        add_to_renderer(renderer=renderer, item=stitching, color='blue', opacity=1.)
        add_to_renderer(renderer=renderer, item=bottom, color='red', opacity=1.)
        add_to_renderer(renderer=renderer, item=reader, color='white')
    
        render_window = vtk.vtkRenderWindow()
        render_window.AddRenderer(renderer)
        render_window.SetWindowName("Overlapping cylinders example")
        render_window.SetSize(1000,1000)
        renderer.SetBackground([0.5]*3)
        render_window_interactor = vtk.vtkRenderWindowInteractor()
        render_window_interactor.SetRenderWindow(render_window)
        render_window.Render()
        render_window_interactor.Start()
    
    if __name__ == '__main__':
        run()