Skip to content

Commit d9d1e30

Browse files
DOCS: Add documentation on TL node
1 parent f3df869 commit d9d1e30

3 files changed

Lines changed: 139 additions & 18 deletions

File tree

Docs/diagrams/layer_dm_collaboration.mmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ classDiagram
1616
AbstractDisplayableManager <|-- LayerDisplayableManager: Inherits
1717
PipelineCreatorI --> PipelineI: Creates
1818
PipelineManager "1" --o "*" PipelineI: Uses
19+
PipelineManager "1" --o "1" NodeReferenceObserver: Uses

Docs/extension_architecture.md

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,22 @@ The diagram below summarizes the relationship between the different classes.
2929

3030
The main classes of the library and their responsibilities are summarized below :
3131

32-
| Class | Description |
33-
|---------------------------------------|----------------------------------------------------------------------------------------------|
34-
| vtkMRMLLayerDMPipelineI | Interface for display pipelines. Handles interaction, rendering, camera, and observer logic. |
35-
| vtkMRMLLayerDisplayableManager | Main displayable manager. Initializes pipeline manager and delegates scene updates. |
36-
| vtkMRMLLayerDMCameraSynchronizer | Synchronizes default camera with renderer or slice node state. |
37-
| vtkMRMLLayerDMLayerManager | Manages renderer layers based on pipeline layer/camera pairs. |
38-
| vtkMRMLLayerDMPipelineCreatorI | Interface for pipeline creation. Supports custom instantiation logic. |
39-
| vtkMRMLLayerDMPipelineCallbackCreator | Callback-based implementation of pipeline creator. |
40-
| vtkMRMLLayerDMPipelineScriptedCreator | Python lambda-based pipeline creator. |
41-
| vtkMRMLLayerDMPipelineFactory | Singleton factory for pipeline instantiation and registration. |
42-
| vtkMRMLLayerDMPipelineManager | Manages pipeline lifecycle, layer manager, and camera sync. |
43-
| vtkMRMLLayerDMScriptedPipelineBridge | Python bridge for virtual method delegation. |
44-
| vtkMRMLLayerDMScriptedPipeline | Python abstract class for scripted pipelines. |
32+
| Class | Description |
33+
|------------------------------------------|----------------------------------------------------------------------------------------------|
34+
| vtkMRMLLayerDMPipelineI | Interface for display pipelines. Handles interaction, rendering, camera, and observer logic. |
35+
| vtkMRMLLayerDisplayableManager | Main displayable manager. Initializes pipeline manager and delegates scene updates. |
36+
| vtkMRMLLayerDMCameraSynchronizer | Synchronizes default camera with renderer or slice node state. |
37+
| vtkMRMLLayerDMLayerManager | Manages renderer layers based on pipeline layer/camera pairs. |
38+
| vtkMRMLLayerDMPipelineCreatorI | Interface for pipeline creation. Supports custom instantiation logic. |
39+
| vtkMRMLLayerDMPipelineCallbackCreator | Callback-based implementation of pipeline creator. |
40+
| vtkMRMLLayerDMPipelineScriptedCreator | Python lambda-based pipeline creator. |
41+
| vtkMRMLLayerDMPipelineFactory | Singleton factory for pipeline instantiation and registration. |
42+
| vtkMRMLLayerDMPipelineManager | Manages pipeline lifecycle, layer manager, and camera sync. |
43+
| vtkMRMLLayerDMScriptedPipelineBridge | Python bridge for virtual method delegation. |
44+
| vtkMRMLLayerDMScriptedPipeline | Python abstract class for scripted pipelines. |
45+
| vtkMRMLLayerDMWidgetEventTranslationNode | MRML node providing interactions to widget event map. |
46+
| vtkMRMLLayerDMNodeReferenceObserver | Monitors scene for reference changes to trigger pipeline update. |
47+
| vtkSlicerLayerDMLogic | Module's logic class providing helper functionalities to manage display / TL nodes. |
4548

4649
## Pipeline lifecycle
4750

@@ -61,7 +64,7 @@ The diagram below shows how the LayerDM handles interactions :
6164
:align: center
6265
```
6366

64-
During interactions, the cap process / process and lose focus events are forwarded to the interaction logic class
67+
During interactions, the can process / process and lose focus events are forwarded to the interaction logic class
6568
responsible for handling the events.
6669

6770
The interaction logic will forward the events to the underlying pipelines.
@@ -90,8 +93,8 @@ by the LayerManager class.
9093
From a developer standpoint, only the `GetRenderOrder` value needs to be returned. This value is a static value read
9194
when new pipelines are added / removed from the pipeline manager.
9295

93-
Pipelines with the same GetRenderOrder and the same GetCustomCamera will be grouped in the same renderer layer. If the value
94-
is set to 0, the pipelines will be set to the default renderer layer.
96+
Pipelines with the same GetRenderOrder and the same GetCustomCamera will be grouped in the same renderer layer. If the
97+
value is set to 0, the pipelines will be set to the default renderer layer.
9598

9699
The grouping logic is summarized below:
97100

@@ -115,3 +118,29 @@ between screen and RAS space.
115118

116119
Another added value is that 3D actors can benefit from all VTK features, including antialiasing, which is not the
117120
case when using 2D actors which is currently used in SliceViews.
121+
122+
## Node reference updates
123+
124+
vtkMRML nodes provide a referencing mechanism. This mechanism is, for instance, used to register nodes as display nodes
125+
to other nodes.
126+
127+
Pipelines are associated to a given `Display Node` (note: the node doesn't need to inherit from vtkMRMLDisplayNode) when
128+
nodes in the scene add references to the display node, the pipeline's `OnReferenceToDisplayNodeAdded` method will be
129+
triggered.
130+
131+
Similarly, when a reference is removed, the pipeline's `OnReferenceToDisplayNodeRemoved` method will be triggered.
132+
133+
By default, these calls will be dispatched to the `OnUpdate` method with the display node as the target vtkObject and
134+
`vtkMRMLNode::ReferenceAddedEvent` / `vtkMRMLNode::ReferenceRemovedEvent` event Ids.
135+
136+
## Event translation
137+
138+
Although not used internally, the library provides the `vtkMRMLLayerDMWidgetEventTranslationNode` MRML node to help in
139+
converting processing event data to significant widget events.
140+
141+
Although with a different API, its usage follows the `vtkMRMLAbstractWidget` event translation mechanism.
142+
143+
The implementation was split to provide:
144+
145+
* Direct access from Python
146+
* Possible access from the Scene to customize the interaction events

Docs/getting_started.md

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,9 @@ The state of the widget and its current mouse cursor can be returned using the f
220220
* `virtual int GetMouseCursor() const`: Return custom mouse cursor VTK enum.
221221
* `virtual int GetWidgetState() const`: Return current widget state enum (default idle).
222222

223-
It is advised for complex interactions to
224-
use [vtkMRMLAbstractWidget objects](https://github.com/Slicer/Slicer/blob/main/Libs/MRML/DisplayableManager/vtkMRMLAbstractWidget.h).
223+
It is advised for complex interactions to use
224+
either [vtkMRMLLayerDMWidgetEventTranslationNode objects](https://github.com/KitwareMedical/SlicerLayerDisplayableManager/blob/main/LayerDM/MRML/vtkMRMLLayerDMWidgetEventTranslationNode.h)
225+
or [vtkMRMLAbstractWidget objects](https://github.com/Slicer/Slicer/blob/main/Libs/MRML/DisplayableManager/vtkMRMLAbstractWidget.h).
225226

226227
```python
227228
class MyPipeline(vtkMRMLLayerDMScriptedPipeline):
@@ -391,3 +392,93 @@ vtkMRMLLayerDMPipelineFactory.GetInstance().AddPipelineCreator(pipeline_creator)
391392
});
392393
}
393394
```
395+
396+
## Defining custom event translation across pipelines
397+
398+
For complex interactions, it is recommended to use
399+
either [vtkMRMLLayerDMWidgetEventTranslationNode objects](https://github.com/KitwareMedical/SlicerLayerDisplayableManager/blob/main/LayerDM/MRML/vtkMRMLLayerDMWidgetEventTranslationNode.h)
400+
or [vtkMRMLAbstractWidget objects](https://github.com/Slicer/Slicer/blob/main/Libs/MRML/DisplayableManager/vtkMRMLAbstractWidget.h).
401+
402+
The vtkMRMLLayerDMWidgetEventTranslationNode are compatible with scene exchange and can be used as an easy way to define
403+
and customize interactions for given pipelines.
404+
405+
The easiest way to register a TL node is using the LayerDM logic class.
406+
The logic class can register singleton TL nodes which will not be saved by the scene to provide the default expected TL
407+
behavior.
408+
409+
```python
410+
import slicer
411+
from slicer import vtkSlicerLayerDMLogic
412+
413+
414+
def configureTLNode(node):
415+
"""Configuration logic"""
416+
417+
418+
# The following code creates an configures a default translation node
419+
tl_node = vtkSlicerLayerDMLogic.GetWidgetEventTranslationSingleton(slicer.mrmlScene, "MyTLNodeSingleton")
420+
if tl_node is None:
421+
tl_node = vtkSlicerLayerDMLogic.CreateWidgetEventTranslationSingleton(slicer.mrmlScene, "MyTLNodeSingleton")
422+
configureTLNode(tl_node)
423+
424+
# After creation, the node can be attached to a given display node
425+
vtkSlicerLayerDMLogic.SetWidgetEventTranslationNode(node, tl_node)
426+
427+
# The TL node can then be retrieved from the display node
428+
tl_node = vtkSlicerLayerDMLogic.GetWidgetEventTranslationNode(node)
429+
```
430+
431+
The TL node provides the following event translation methods:
432+
433+
```python
434+
# Click event translation
435+
tl_node.SetTranslation(
436+
vtkMRMLAbstractWidget.WidgetStateAny,
437+
vtkCommand.LeftButtonReleaseEvent,
438+
vtkMRMLAbstractWidget.WidgetEventUser,
439+
)
440+
441+
# Click drag event translation
442+
tl_node.SetTranslationClickAndDrag(
443+
vtkMRMLAbstractWidget.WidgetStateOnWidget,
444+
vtkCommand.LeftButtonPressEvent,
445+
dragging_state,
446+
start_event,
447+
end_event,
448+
)
449+
450+
# Keyboard events
451+
tl_node.SetTranslationKeyboard(
452+
vtkMRMLAbstractWidget.WidgetStateIdle,
453+
"Delete",
454+
vtkMRMLAbstractWidget.WidgetEventReset,
455+
)
456+
```
457+
458+
In the pipeline, during the can process and process interaction methods, the TL node can be used to translate the
459+
incoming event data.
460+
461+
```{note}
462+
The pipelines don't have builtin widget state. The widget should be managed internally by the pipeline if needed.
463+
State values should reuse the vtkMRMLAbstractWidget enum for compability with the other displayable managers.
464+
```
465+
466+
```python
467+
class MyPipeline(vtkMRMLLayerDMScriptedPipeline):
468+
def CanProcessInteractionEvent(self, eventData: vtkMRMLInteractionEventData) -> tuple[bool, float]:
469+
widgetEvent = self.tl_node.Translate(self.widgetState, eventData)
470+
if widgetEvent == vtkMRMLAbstractWidget.WidgetEventNone:
471+
return False, sys.float_info.max
472+
473+
# Compute representative distance to event
474+
return True, my_distance
475+
476+
def ProcessInteractionEvent(self, eventData: vtkMRMLInteractionEventData) -> bool:
477+
widgetEvent = self.tl_node.Translate(self.widgetState, eventData)
478+
if widgetEvent == vtkMRMLAbstractWidget.WidgetEventTranslateStart:
479+
self.widgetState = vtkMRMLAbstractWidget.WidgetStateTranslate
480+
return self.StartTranslate(eventData)
481+
482+
# ...
483+
return True
484+
```

0 commit comments

Comments
 (0)