Creating a custom Annotation node in a Python scripted module

I’m trying to create a “slider ring” in the MRML scene via a Python scripted module. The idea is to be able to grab the ring (modelled as a vtkPolyData torus) and move it along a path defined by another vtkPolyData.
The vtkMRMLAnnotationControlPointsNode in the Annotation module looks like a place to start. It has derived classes that appear to do some of what I am trying.
I have two questions:
The first is whether I’m on the right track, or if there is a better way to accomplish what I’m trying to do.
The second is, can I make a derived class of vtkMRMLAnnotationControlPointsNode in Python, or otherwise customize this class in my module? I see that Annotations is a loadable module, and my attempts so far have not worked.
Thank you for any assistance.

What we usually do is to define curve using markup fiducials, interpolate using Markups to models module. You can get model points as a numpy array and do least squares fit in a small neighborhood to get position and direction vector along the curve. To fully constrain the rotation, we use normal vector of the plane that is least-square-fit to the entire curve. All easily doable from Python.

Moving along the curve with a GUI slider is easy, but interacting in viewers is more tricky. Maybe you can snap the position to the closest position to the cursor that you can move around by Shift+MouesMove.

What is the clinical application?

1 Like

Thanks, Andras.
The application is a viewer for MR image sets of peripheral arteries. The plan is to reformat the 2D slices so that their normals are always oriented along the centreline of the artery (hence my previous question about VMTK).
Your description of getting the point positions and creating the path with a least-squares fit makes sense. Thank you. I’m confused by the part about constraining the rotation with a normal vector from a least-squares fit to the full curve. I understand the need to constrain the rotation, but what direction does it end up pointing in?

Regarding the object in the 3D view, I’ll experiment with ways to snap it to cursor positions as you suggest, but my first question is how to create a handle from a given polydata in Slicer. How do I create a handle that I can move with the mouse?

Thanks very much for your help.

This navigation style isn’t our idea. We’re trying to emulate some of the features in TeraRecon’s EVAR module:

Yes it is standard curved planar reformatting. The functionality is already available in Slicer, it just not easy to figure out how. It is described in detail here:

https://discourse.slicer.org/t/cross-sectional-area-of-a-ring/1015

So, you can find an example of positioning an object along a curve in Endoscopy module. I think this module uses the same method as I described above to constrain rotation around the curve direction vector. For vessels (and for any other significantly non-planar curve), I would use a Frenet-Serret frame (https://github.com/jeromevelut/SplineDrivenImageSlicer/blob/master/Filters/vtkFrenetSerretFrame.h) instead of a fixed constraining direction.

1 Like

Thank you! The Frenet-Serret frame looks like a great approach. That gives me a solution to a couple of my next steps.

Back to the other question of how to customize an object that can move through the 3D view, how can I create something like the Control Points of the ROI box in the Annotations module, or the Fiducial points in the Markups module, but with my own polydata (torus) as the movable object?
torus3D

Creating custom widgets like the ROI widget is extremely difficult. You can emulate custom widgets buy using markup fiducial points as “handles” that you can move and observer the markup node changes to update model position based on that. See this example in the script repository where you reposition and resize a sphere model by using markup fiducials.

1 Like

Thank you, I’ll try that approach.

Regarding the Frenet-Serret frame code, is there a Python-wrapped version accessible? Alternatively, how would I do so? I believe I can create a Python class inherited from vtk.vtkPolyDataAlgorithm and translate the C++ version, though it would be great if there was a way to avoid doing so.
Thanks again.

To add new C++ classes, you need to add a loadable module. It can be hidden, no widget is required. You can add this class to the logic folder. You may use DataProbe module as an example.

1 Like

The Spline-Driven Image Slicer has been added to VTK as a Remote module. The CMake file in the VTKv9/Remote directory fetches from https://github.com/lorensen/midas-journal-838.git, which is more recently maintained than the djelouze repository.