Hi all, I’m looking to create a model of a cylinder and orient it along a known vector using python. More specifically, I have two control points, but I want to create a line that goes between them but extends past the control points along the same direction.
Here is some rough but working code which I wrote a long time ago when I was trying to learn to do something similar that does almost exactly what you want. The only thing missing is calculating the extensions you want, which could be calculated in a pre-step, or modified into the function below.
def makeCylinderModelNode(
endpoint1, endpoint2, radiusMm=0.25, numberOfSides=20
):
"""Make a cylinder surface, transform it so that the center of one end is endpoint1, and the other
is centered on endpoint2"""
import numpy as np
center = np.add(endpoint1, endpoint2) / 2.0
height = np.linalg.norm(np.subtract(endpoint2, endpoint1))
direction = np.subtract(endpoint2, endpoint1)
print(
"Center: (%0.2f, %0.2f, %0.2f)\nHeight: %0.2f\nDirection: (%0.2f, %0.2f, %0.2f)"
% (*center, height, *direction)
)
#
cyl = vtk.vtkCylinderSource()
cyl.SetRadius(radiusMm)
cyl.SetResolution(numberOfSides)
cyl.SetHeight(height)
cyl.Update() # don't know if this is necessary
#
initial_axis = [0, 1, 0]
#
# Create transform for it
# The initial axis direction is anterior/posterior (0,1,0) in RAS, we want to rotate that so that it
# points along the vector represented in "direction"
# Can determine this by finding the cross product, and angle from dot product
axis = np.cross(initial_axis, direction)
angle_deg = 180 / np.pi * np.arccos(np.dot(initial_axis, direction) / (np.linalg.norm(initial_axis) * np.linalg.norm(direction)))
#
print("Axis: (%0.2f, %0.2f, %0.2f)\nAngle: %0.2f deg" % (*axis, angle_deg))
#
vtkTform = vtk.vtkTransform()
vtkTform.Translate(center)
vtkTform.RotateWXYZ(angle_deg, axis)
vtkTform.Modified() # don't know if this is necessary
#
# Create filter
vtkTformFilter = vtk.vtkTransformFilter()
vtkTformFilter.SetInputData(cyl.GetOutput())
vtkTformFilter.SetTransform(vtkTform)
vtkTformFilter.Update() # don't know if this is necessary
#
# Create model node and connect it to the transformed cylinder
model_node = slicer.modules.models.logic().AddModel(vtkTformFilter.GetOutput())
return model_node
You may also draw a cylinder using the ExtraMarkups extension.
Then you may set the control points defining the axis of the cylinder using python since you know the coordinates of your 2 control points. Next ajust the radius.
You may finally calculate the coordinates of a point beyond the line defined by your pair of control points using vtkMath::GetPointAlongLine(). Then draw a line as suggested here.