create cylinders from csv file with start and end locations

Hello to all,

I have a .csv file with (x,y,z) positions of start and end positions of cylinders.

I would like to import this into paraview and create the cylinders givent the (x,y,z) of start points, and (x,y,z) of end points and specify the radius.

Is this possible?

I will leave an example .csv file:
foo.csv (1.2 KB)

Best regards

The easiest way is to use a programmable source and to write the reading part in python.

Note that the Tube filter creates a cylinder from lines, so you may just create a simple polydata and apply this filter.

1 Like

In Programmable Source, I tried to use Cylinder Source and it became very complicated. nicolas.vuaille is right, it is much better to use Tube filter.

Just for reference, here is a dirty script for Programmable Source.

import numpy as np
import vtk

# CSV file to define the cylinders
csv_file = 'C:/Users/icem/Desktop/cyl/foo.csv'
# cylinder radius
r = 0.025

# CSV file reading
data = np.genfromtxt(csv_file, skip_header=2, dtype=None, names=['start_x','start_y','start_z','end_x','end_y','end_z'], delimiter=',', autostrip=True)
# obtain starting and ending coordinates of cylinders
start_pnts = np.vstack([data['start_x'],data['start_y'],data['start_z']]).T
end_pnts = np.vstack([data['end_x'],data['end_y'],data['end_z']]).T

appender = vtk.vtkAppendPolyData()

for start_pnt, end_pnt in zip(start_pnts, end_pnts):
    # center coordinates of a cylinder
    center = (start_pnt + end_pnt)*0.5
    # direction vector of a cylinder
    direc = end_pnt - start_pnt
    # height of cylinder
    h = np.linalg.norm(direc)
    # define the local coordinate system of a cylinder
    dir2 = direc / h
    dir1 = np.array([dir2[1], -dir2[0], 0])
    dir1 /= np.linalg.norm(dir1)
    dir3 = np.cross(dir1, dir2)
    # basis transformation matrix of the local coordinate system of a cylinder
    T = np.array([dir1, dir2, dir3]).T
    T = np.concatenate([T, np.zeros((3,1))], axis=1)
    T = np.concatenate([T, np.zeros((1,4))], axis=0)
    T[3, 3] = 1.0
    # create a cylinder
    cyl = vtk.vtkCylinderSource()
    cyl.SetCenter([0,0,0])
    cyl.SetRadius(r)
    cyl.SetHeight(h)
    cyl.SetResolution(100)
    cyl.Update()
    # transforms cylinder along its local coordinate system
    transform = vtk.vtkTransform()
    transform.PostMultiply()
    transform.SetMatrix(T.flatten())
    transform.Translate(center)
    transformFilter = vtk.vtkTransformPolyDataFilter()
    transformFilter.SetInputData(cyl.GetOutput())
    transformFilter.SetTransform(transform)
    transformFilter.Update()

    # add a cylinder to appender
    appender.AddInputData(transformFilter.GetOutput())

appender.Update()

output.DeepCopy(appender.GetOutput())

How can I create the polydata?

When using the Tube filter, the Programmable Source script becomes very simple as follows.

import numpy as np
import vtk

# CSV file to define the cylinders
csv_file = 'C:/Users/icem/Desktop/cyl/foo.csv'
# cylinder radius
r = 0.025

# CSV file reading
data = np.genfromtxt(csv_file, skip_header=2, dtype=None, names=['start_x','start_y','start_z','end_x','end_y','end_z'], delimiter=',', autostrip=True)
# obtain starting and ending coordinates of cylinders
start_pnts = np.vstack([data['start_x'],data['start_y'],data['start_z']]).T
end_pnts = np.vstack([data['end_x'],data['end_y'],data['end_z']]).T

appender = vtk.vtkAppendPolyData()

for start_pnt, end_pnt in zip(start_pnts, end_pnts):
    # creating the central axis of a cylinder    
    line = vtk.vtkLineSource()
    line.SetPoint1(start_pnt)
    line.SetPoint2(end_pnt)
    line.SetResolution(1)
    line.Update()

    # creating a cylinder
    tubefilter = vtk.vtkTubeFilter()
    tubefilter.SetInputData(line.GetOutput())
    tubefilter.SetRadius(r)
    tubefilter.SetNumberOfSides(50)
    tubefilter.CappingOn()
    tubefilter.Update()

    # add a cylinder to appender
    appender.AddInputData(tubefilter.GetOutput())

appender.Update()

output.DeepCopy(appender.GetOutput())
2 Likes

This worked!

I am trying to figure out out to ask information to the user (file name, and name of an image to save). I used trace feature to get a python script

Really appreciate your help.
It can be very helpful to me.

Actually, I have a question about this problem.
In my case, I need to add various tube radii and colors.
But the error showed only scalar-type data is available.
Could you help me to solve this problem?

Hi,

I don’t have a good grasp of what you want to do, but if you want to add new arrays, the following is a good reference.

https://docs.paraview.org/en/latest/ReferenceManual/pythonProgrammableFilter.html#reading-a-csv-file-with-particles

If the radius of each cylinder changes, you may want to add an array of radii to the zip function. Also, if you need to color by RGB, the following is a good guide.