polydata surface border as polyline

Hello, I’d like to obtain the border of a polydata triangulated surface as a polyline.

With the following code (where tri_surf is the triangulated surface polydata), I get all the edges on the border, but they are not ordered, so I cannot build a proper closed polyline representing the border of the surface from these scattered edges.

edges = vtk.vtkFeatureEdges()
edges.BoundaryEdgesOn()
edges.NonManifoldEdgesOff()
edges.FeatureEdgesOff()
edges.ManifoldEdgesOff()
edges.SetInputData(tri_surf)
edges.Update()

Thanks very much!

Off the top of my head you might want to try the triangle strip / polyline stripper filter (vtkStripper).

Hello, thanks for quick answer! It looks like vtkStripper is what I need, however the order of the lines in the polyline is still messed up. In this example, what I expect as border is in yellow and the polyline I get is in one.

This is my code:

def get_boundary_edges(self):
    border_edges = vtk.vtkFeatureEdges()
    border_edges.BoundaryEdgesOn()
    border_edges.NonManifoldEdgesOff()
    border_edges.FeatureEdgesOff()
    border_edges.ManifoldEdgesOff()
    border_edges.SetInputData(self)
    border_edges.Update()
    clean_border_edges = vtk.vtkCleanPolyData()
    clean_border_edges.SetInputConnection(border_edges.GetOutputPort())
    border_strips = vtk.vtkStripper()
    border_strips.SetInputConnection(clean_border_edges.GetOutputPort())
    border_strips.Update()
    border_polyline = vtk.vtkPolyData()
    border_polyline.SetPoints(border_strips.GetOutput().GetPoints())
    border_polyline.SetPolys(border_strips.GetOutput().GetLines())
    return border_polyline

Thanks!!!

Update: like this it works!

def get_boundary_edges(self):
    border_edges = vtk.vtkFeatureEdges()
    border_edges.BoundaryEdgesOn()
    border_edges.NonManifoldEdgesOff()
    border_edges.FeatureEdgesOff()
    border_edges.ManifoldEdgesOff()
    border_edges.SetInputData(self)
    border_edges.Update()
    clean_border_edges = vtk.vtkCleanPolyData()
    clean_border_edges.SetInputConnection(border_edges.GetOutputPort())
    border_strips = vtk.vtkStripper()
    border_strips.SetInputConnection(clean_border_edges.GetOutputPort())
    border_strips.Update()
    border_polyline = vtk.vtkPolyData()
    border_polyline.SetPoints(border_strips.GetOutput().GetCell(0).GetPoints())
    border_polyline.SetPolys(border_strips.GetOutput().GetLines())
    return border_polyline

I just changed the line

border_polyline.SetPoints(border_strips.GetOutput().GetPoints())

into

border_polyline.SetPoints(border_strips.GetOutput().GetCell(0).GetPoints())

I guess that the reason is that the cell called here is a polyline, generated by vtkStripper, that combines all the single lines in the correct order.

However, if the surface is composed of two disconnected parts, I get two polylines, and in this case I am still not able to add all the points properly, and I end up with just the border of the first part.

I thought I can append the points from the second cell (or more cells) like this

    for i in range(border_polyline.GetNumberOfCells()-1):
        border_polyline.GetPoints().AppendPoints(border_strips.GetOutput().GetCell(i+1).GetPoints())

But for some reason I don’t understand it does not work. It appears it replaces the points of the first part with those of the second one.

Suggestions? Thanks very much!

Rather than trying to manually construct the polydata, how about:

r_polyline = r_strips.GetOutput() 

which will have both the polylines and associated points.

Thanks Will! Unfortunately this was the first option I tried, but this is the messed up, unordered border I get (in red).

That’s why I was experimenting with the code above, that works in case of single-part surfaces, but yields just the border of the first part in case of multiple parts.

For this reason I was trying to add also the other parts.

Thanks!

Hello, now it works. Basically I am building the polygons that correspond to borders, using points in the order with which they appear in the polyline cells issued by vtkStripper.

In this way I get proper borders even in case of multi-part surfaces.

This is the code:

def get_boundary_edges(self):
    border_edges = vtk.vtkFeatureEdges()
    border_edges.BoundaryEdgesOn()
    border_edges.NonManifoldEdgesOff()
    border_edges.FeatureEdgesOff()
    border_edges.ManifoldEdgesOff()
    border_edges.SetInputData(self)
    border_edges.Update()
    clean_border_edges = vtk.vtkCleanPolyData()
    clean_border_edges.SetInputConnection(border_edges.GetOutputPort())
    border_strips = vtk.vtkStripper()
    border_strips.SetJoinContiguousSegments(True)
    border_strips.SetInputConnection(clean_border_edges.GetOutputPort())
    border_strips.Update()
    border_polygons = vtk.vtkCellArray()
    border_polygons.SetNumberOfCells(border_strips.GetOutput().GetNumberOfCells())
    border_points = vtk.vtkPoints()
    points_in_border = 0
    for cell in range(border_strips.GetOutput().GetNumberOfCells()):
        polygon = vtk.vtkPolygon()
        polygon.GetPointIds().SetNumberOfIds(border_strips.GetOutput().GetCell(cell).GetNumberOfPoints())
        for point_in_cell in range(border_strips.GetOutput().GetCell(cell).GetNumberOfPoints()):
            point_in_border = point_in_cell + points_in_border
            border_points.InsertNextPoint(border_strips.GetOutput().GetCell(cell).GetPoints().GetPoint(point_in_cell))
            polygon.GetPointIds().SetId(point_in_cell, point_in_border)
        border_polygons.InsertNextCell(polygon)
        points_in_polygon = polygon.GetNumberOfPoints()
        points_in_border += points_in_polygon
    borders = vtk.vtkPolyData()
    borders.SetPoints(border_points)
    borders.SetPolys(border_polygons)
    borders.Modified()
    return borders

Please let me know if you find a more efficient or elegant solution!

Thanks!