Extension architecture
Layer DM classes
The LayerDisplayableManager is the main entry point for the classes in this extension. It’s automatically registered in the default 3D Slicer SliceViews and ThreeDViews as a normal displayable manager when the module is first setup using conventional 3D Slicer mechanisms.
Internally, the Layer displayable manager delegates to the following objects.
pipeline manager: used for the lifetime of the created pipelines.
layer manager: used for the creation / deletion of the renderers added to the render window and managed by the layer
camera synchronizer: used for camera synchronization and callback mechanism linked to the default view camera.
Tip
From a user standpoint, only the following classes need to be manipulated directly:
The pipeline: either implemented in Python or in C++
The pipeline creator: Responsible for creating the pipeline through a user set callback
The pipeline factory: Accessible through a singleton and used automatically by the displayable manager
The diagram below summarizes the relationship between the different classes.
---
title: LayerDM class collaboration
config:
class:
hideEmptyMembersBox: true
---
classDiagram
direction TB
PipelineFactory "1" --o "*" PipelineCreatorI: Delegates creation
PipelineManager --o "1" PipelineFactory: Delegates creation
LayerDisplayableManager --> PipelineFactory: Singleton usage
LayerDisplayableManager --o "1" PipelineManager: Uses
LayerDisplayableManager --o "1" CameraSynchronizer: Uses
LayerDisplayableManager --o "1" LayerManager: Uses
AbstractDisplayableManager <|-- LayerDisplayableManager: Inherits
PipelineCreatorI --> PipelineI: Creates
PipelineManager "1" --o "*" PipelineI: Uses
PipelineManager "1" --o "1" NodeReferenceObserver: Uses
The main classes of the library and their responsibilities are summarized below :
Class |
Description |
|---|---|
vtkMRMLLayerDMPipelineI |
Interface for display pipelines. Handles interaction, rendering, camera, and observer logic. |
vtkMRMLLayerDisplayableManager |
Main displayable manager. Initializes pipeline manager and delegates scene updates. |
vtkMRMLLayerDMCameraSynchronizer |
Synchronizes default camera with renderer or slice node state. |
vtkMRMLLayerDMLayerManager |
Manages renderer layers based on pipeline layer/camera pairs. |
vtkMRMLLayerDMPipelineCreatorI |
Interface for pipeline creation. Supports custom instantiation logic. |
vtkMRMLLayerDMPipelineCallbackCreator |
Callback-based implementation of pipeline creator. |
vtkMRMLLayerDMPipelineScriptedCreator |
Python lambda-based pipeline creator. |
vtkMRMLLayerDMPipelineFactory |
Singleton factory for pipeline instantiation and registration. |
vtkMRMLLayerDMPipelineManager |
Manages pipeline lifecycle, layer manager, and camera sync. |
vtkMRMLLayerDMScriptedPipelineBridge |
Python bridge for virtual method delegation. |
vtkMRMLLayerDMScriptedPipeline |
Python abstract class for scripted pipelines. |
vtkMRMLLayerDMWidgetEventTranslationNode |
MRML node providing interactions to widget event map. |
vtkMRMLLayerDMNodeReferenceObserver |
Monitors scene for reference changes to trigger pipeline update. |
vtkSlicerLayerDMLogic |
Module’s logic class providing helper functionalities to manage display / TL nodes. |
Pipeline lifecycle
The sequence diagram below shows a typical pipeline lifecycle as handled by the library:
---
title: Pipeline lifecycle sequence
---
sequenceDiagram
participant User
participant Scene
participant LayerDM as Layer Displayable Manager
participant PipelineMan as Pipeline Manager
participant PipelineFactory as Pipeline Factory
participant PipelineCreator as Pipeline Creator
participant Pipeline
Note over User, Pipeline: --- Add sequence ---
User ->> Scene: Add display node
Scene ->> LayerDM: Node Added
LayerDM ->> PipelineMan: Node Added
PipelineMan ->> PipelineFactory: Try Create
PipelineFactory ->> PipelineCreator: Try Create
PipelineCreator ->> Pipeline: Create
Pipeline ->> PipelineMan: Pipeline instance
PipelineMan ->> Pipeline: Set View node / display node / renderer
PipelineMan ->> Pipeline: Reset Display
PipelineMan ->> LayerDM: Request Render
Note over User, Pipeline: --- Remove sequence ---
User ->> Scene: Remove display node
Scene ->> LayerDM: Node Removed
LayerDM ->> PipelineMan: Node Removed
PipelineMan ->> Pipeline: Remove / Remove renderer
PipelineMan ->> LayerDM: Request Render
Interaction handling
The diagram below shows how the LayerDM handles interactions :
---
title: Layer DM interaction sequence
---
sequenceDiagram
participant InteractorStyle as Interactor Style
participant LayerDM as Layer Displayable Manager
participant PipelineMan as Pipeline Manager
participant InteractionLogic as Interaction Logic
participant Pipeline
InteractorStyle ->> LayerDM: Can Process Interaction
LayerDM ->> PipelineMan : Can Process Interaction
PipelineMan ->> InteractionLogic : Can Process Interaction
loop For each pipeline
InteractionLogic ->> Pipeline : Can Process Interaction
end
InteractionLogic ->> InteractionLogic : Handle focus loss
InteractionLogic ->> InteractorStyle : Can process + min distance
InteractorStyle ->> LayerDM: Process Interaction
LayerDM ->> PipelineMan : Process Interaction
PipelineMan ->> InteractionLogic : Process Interaction
loop For each (sorted) pipeline that can process
InteractionLogic ->> Pipeline : Process Interaction
alt Did process
Note over InteractionLogic: Early stop on first success
else Did not process
Note over InteractionLogic: Continue
end
end
InteractionLogic ->> InteractionLogic : Handle focus loss
InteractionLogic ->> InteractorStyle : Did process
During interactions, the can process / process and lose focus events are forwarded to the interaction logic class responsible for handling the events.
The interaction logic will forward the events to the underlying pipelines.
Pipelines that can process the interaction will be sorted using a tuple of three values:
Pipeline widget state: if its value is greater than hovered indicating and active user interaction with the pipeline
Pipeline render order: greater values indicate that pipelines are rendered over other pipelines
Pipeline distance to interaction
This strategy allows for intuitive user interactions depending on the type of widgets they are manipulating.
Render layer handling
In VTK, a render window can contain an arbitrary number of renderers. For each renderer, developers define their render layer, which will control the rendering order of the different renderers. Top most renderer layers drawn elements will be drawn at the top of other elements.
In 3D Slicer’s displayable managers, some displayable managers such as the vtkMRMLRulerDisplayableManager use this strategy to render on top of other elements.
In the extension, the renderers are not manipulated directly by developers in their pipelines but automatically managed by the LayerManager class.
From a developer standpoint, only the GetRenderOrder value needs to be returned. This value is a static value read
when new pipelines are added / removed from the pipeline manager.
Pipelines with the same GetRenderOrder and the same GetCustomCamera will be grouped in the same renderer layer. If the value is set to 0, the pipelines will be set to the default renderer layer.
The grouping logic is summarized below:
---
title: Layer Manager update Sequence
---
stateDiagram-v2
state if_start <<choice>>
[*] --> if_start
RemoveAll: Remove all layers
RemoveOutdatedPipelines: Remove outdated pipeline ref
RemoveOutdatedLayers: Remove outdated renderers
AddMissingLayers: Add missing renderers
UpdateLayerOrdering: Update render window layer order
UpdateCamera: Update layer camera
SynchronizeRenderers: Update pipeline renderers
if_start --> RemoveAll : m_renderWindow == nullptr
if_start --> RemoveOutdatedPipelines : else
RemoveOutdatedPipelines --> RemoveOutdatedLayers
RemoveOutdatedLayers --> AddMissingLayers
AddMissingLayers --> UpdateLayerOrdering
UpdateLayerOrdering --> UpdateCamera
UpdateCamera --> SynchronizeRenderers
RemoveAll --> [*]
SynchronizeRenderers --> [*]
Camera Synchronizer
Most displayable managers follow the main camera present in the render window. This allows actors / widgets to be updated at the right position.
If a pipeline doesn’t use a specific camera, its renderer layer will be set the default camera (i.e., the camera set on
renderer 0). The default synchronization behavior is set to copy the default camera and notify the pipelines of camera
update using the OnDefaultCameraModified method calls.
For SliceViews, the camera synchronizer monitors modified events to set the default camera aligned with the Slice view properties. This allows pipelines to define and update actors directly in 3D and avoid having to do manual conversions between screen and RAS space.
Another added value is that 3D actors can benefit from all VTK features, including antialiasing, which is not the case when using 2D actors which is currently used in SliceViews.
Node reference updates
vtkMRML nodes provide a referencing mechanism. This mechanism is, for instance, used to register nodes as display nodes to other nodes.
Pipelines are associated to a given Display Node (note: the node doesn’t need to inherit from vtkMRMLDisplayNode) when
nodes in the scene add references to the display node, the pipeline’s OnReferenceToDisplayNodeAdded method will be
triggered.
Similarly, when a reference is removed, the pipeline’s OnReferenceToDisplayNodeRemoved method will be triggered.
By default, these calls will be dispatched to the OnUpdate method with the display node as the target vtkObject and
vtkMRMLNode::ReferenceAddedEvent / vtkMRMLNode::ReferenceRemovedEvent event Ids.
Event translation
Although not used internally, the library provides the vtkMRMLLayerDMWidgetEventTranslationNode MRML node to help in
converting processing event data to significant widget events.
Although with a different API, its usage follows the vtkMRMLAbstractWidget event translation mechanism.
The implementation was split to provide:
Direct access from Python
Possible access from the Scene to customize the interaction events