How can I split a polyline into its linear segments?

A simple question that hopefully can be answered with a single link:

How to split a polyline into its n linear segments?

Let’s be given a polyline like the one that results from the PolyLine example. Is there a vtk filter producing a vtkPolyData object with n lines from this input? It’s sort of the inverse filter of vtkStripper that is able to join multiple line segments into a single polyline.

Thanks!

Here’s what I came up with, though I’m pretty convinced that there’re better approaches:

def polylineToMultiline(polyline):
    # Assumption: points appear in the correct order: p0->p1->p2->p3...
    points = polyline.GetPoints()
    nPoints = points.GetNumberOfPoints()
    cells = vtk.vtkCellArray()
    for i in range(nPoints-1):
        line = vtk.vtkLine()
        line.GetPointIds().SetId(0,i)
        line.GetPointIds().SetId(1,i+1)
        cells.InsertNextCell(line)
    poly = vtk.vtkPolyData()
    poly.SetPoints(points)
    poly.SetLines(cells)
    return poly

The above code assumes that the points from the polyline appear in the correct order. Note that this is not the case in general! To extract the points in the correct order, you would have to do something like the following:

def extractConsecutivePoints(poly):
    # Travers the line(s) and add points while keeping their order.
    cells = poly.GetLines()
    cells.InitTraversal()
    idList = vtk.vtkIdList()
    points = vtk.vtkPoints()
    while cells.GetNextCell(idList):
        for i in range(0, idList.GetNumberOfIds()):
            pId = idList.GetId(i)
            points.InsertNextPoint(poly.GetPoint(pId))
    return points

Here is a routine to do it with PyVista since you are using Python. This will work regardless of point ordering.

import pyvista as pv
import numpy as np

def segment_poly_cells(mesh):
    """`mesh` is a PyVista PolyData object 
    (wrapped vtkPolyData)
    """
    if not pv.is_pyvista_dataset(mesh):
        mesh = pv.wrap(mesh)
    polylines = []
    i, offset = 0, 0
    cc = mesh.lines # fetch up front
    while i < mesh.n_cells:
        nn = cc[offset]
        polylines.append(cc[offset+1:offset+1+nn])
        offset += nn + 1
        i += 1
    #
    lines = []
    for poly in polylines:
        lines.append(np.column_stack((poly[:-1], poly[1:])))
    lines = np.vstack(lines)
    cells = np.column_stack((np.full(len(lines), 2), lines))

    segmented = pv.PolyData()
    segmented.points = mesh.points
    segmented.lines = cells
    return segmented

So if you have some vtkPolyData already, just do the folling and a new vtkPolyData mesh will be returned.

segmented = segment_poly_cells(polyline)
1 Like

PyVista is nice, thanks!

What exactly does mesh.lines represent? A container of cells/points that belong to a line?

mesh.lines = [n0, c00, c01, ..., n1, c10, c11, ... ]

The lines property on pyvista.PolyData objects is just a NumPy wrapper around vtk.vtkPolyData.GetLines().GetData() and it is exactly what you have listed above. As a way to get all of the point IDs for each line in the mesh

[n0, c00, c01, ..., n1, c10, c11, ... ]
1 Like

Here’s the best answer to this question. Just use a triangle filter:

def polylineToMultiline(source):
    triangles = vtk.vtkTriangleFilter()
    triangles.SetInputData(source)
    triangles.Update()
    return ensurePolyData(triangles)

The documentation of vtkTriangleFilter states:

… [vtkTriangleFilter] also generates line segments from polylines unless PassLines is off, and generates individual vertex cells from vtkVertex point lists unless PassVerts is off.

The remaining question: vtkTriangleFilter to apply an operation on a polyline, what the hack?! It’s sometimes so difficult to find the right method in vtk. :confused: :sleepy:

1 Like

Huh! I didn’t realize that, good find!

FYI: PyVista has direct access to that filter:

import pyvista as pv
from pyvista import examples

# Example mesh with a single PolyLine cell
polyline = examples.load_spline()
# use vtk.vtkTriangleFilter to create lines
lines = polyline.triangulate()
1 Like