Cell Locator API

Markups, Models, and Annotations

Each annotation in the scene has a markup associated with it, and also a translucent 3d model to represent the thickness of that annotation. So for each element in the scene, we need to manage a vtkMRMLMarkupsNode and a vtkMRMLModelNode. The Annotation class in Python does this and handles serialization to JSON.

Annotation is an abstract class, currently with two concrete implementations: FiducialAnnotation and ClosedCurveAnnotation.

FiducialAnnotation consists only of points with no thickness. Since this behavior is provided by vtkMRMLMarkupsFiducialNode; so the annotation does not need to update a model.

ClosedCurveAnnotation does have thickness. Its markup is a vtkMRMLMarkupsClosedCurveNode, and a model is generated from this markup using vtkSlicerSplinesLogic::CreateModelFromContour (see here)

Creating Annotation Types

Below is a template for creating a new type of Annotation. Keep in mind that Annotation does not create a model, so if you need one (or any other nodes) for the new type, you must create those in NewAnnotation.__init__.

class NewAnnotation(Annotation):
    DisplayName = 'Sample'   # default name for this annotation type
    MarkupType = 'vtkMRMLMarkupsFiducialNode'  # markup type managed by this annotation type

    def __init__(self, markup=None):
        # set any type-specific attributes, models, etc

        super().__init__(markup=markup)

        # do any DisplayNode customizations

    def clear(self):
        # do any cleanup of type-specific attributes

    def update(self):
        # update any type-specific attributes; models, etc

    def metadata(self):
        # convert type-specific attributes to dict for serialization

    def setMetadata(self, data):
        # set any type-specific attributes from dict for deserialization

(More info here)

Annotation.update is invoked each time the markup changes, so we can implement it to re-generate a model. Any other nodes or data that need to be synchronized with the markup can be updated here.

Serialization

Annotations are serialized to JSON using Annotation.toDict. The markup is converted to JSON by Slicer (see here). Any annotation-specific metadata (thickness, etc) is included using Annotation.getMetdata.

Deserialization

Annotations are deserialized using Annotation.fromDict. The specific annotation type is determined using the markup type, and the annotation type’s MarkupType class attribute.

class PlaneAnnotation(Annotation):
    MarkupType = 'vtkMRMLMarkupsPlaneNode'

For example, if the above class is defined and a vtkMRMLMarkupsPlaneNode is stored in the JSON, then the resulting annotation will be an instance of PlaneAnnotation.

The JSON contents will then be sent to PlaneAnnotation.setMetadata so that any attributes specific to PlaneAnnotation may be deserialized.