Transform interaction
Created by: vovythevov
This PR add a widget that allow user to interact with transform directly in the 3D view.
To support this feature, the following infrastructure was added/modified in Slicer:
-
Improved
GetRASBounds()
: ThevtkMRMLDisplayableTransform
methodGetRASBounds()
was improved to allow the user choose whether they want the bounds of the node transformed (when the node is observing a transform) or not. This is useful when you want to be able to have a cube that emcopasses the bounds of the node. Also, a boolean return value was added to be able to check easily if the bounding box returned is valid. Note: Because it wasn't very straight forward to do for the Segmentation node, it is NOT supported for this node. A warning is emitted if you try to use that option on such node. Testing was added withSlicerRASBoundsTest.py
-
Added Transform logic methods:
-
GetNodesRASBounds()
: This method is fairly straight forward and returns the RAS bounds of a list of nodes. It is tested invtkSlicerTransformLogicTest3.cxx
. -
GetTransformedNodes()
This method returns the nodes that are observing a particular transform. It also offers the option to do so recursively so any nodes under a child transform may be returned as well. It is tested invtkSlicerTransformLogicTest2
.
-
Add TransformReferenceAddedEvent and TransformReferenceRemovedEvent The transform node lacked a good way to be notified when a node would start observing it. These new event remediate to this by being sent on the transform node whenever another node set it as its transform. This is tested in
vtkMRMLTransformNodeOnNodeReferenceAddTest
. -
Minor fix of sample data This is simply to be able to use the
CTA abdomen (Panoramix)
dataset in testing because it is not centered and thus offers better testing for transforms.
The transform interaction itself has the following features:
- The widget is shown/hidden based on the
vtkMRMLTransformDisplayNode::VisualizationMode
and theVisible
option. The visualization mode option was chosen instead of a separate "Interactive" option because the interactive mode is so far incompatible with the other modes of visualization for the transform. In my opinion, it integrates better in the interface as well.
- If no transform is present, the widget will have a default size so the user can still interact with it.
-
Whenever a node that has valid bounds is placed under the transform, the widget will automatically resize itself to the bounds of the node. If multiple nodes (with valid bounds) are set under the transform, the widget will resize itself to encompass all the nodes.
-
GUI/Widget synchronization Of course, whenever the user interacts with the widget, the transform node will be updated. Likewise, the whenever the transform node is modified the widget will be updated.
-
Support for parent transforms Parent transforms are supported and allow one to be able to move node individually or by group like so:
-
User interaction: The interaction is the standard provided by the widget. Options are exposed to the user through the transform module interface (enable/disable Scaling/Translation/Rotation).
Interaction infrastructure:
-
The underlying widget used is vtkBoxWidget2. This widget provides a very nice interface as you can directly set to it the desired transform.
-
No non-linear transforms: As of now, due to the limitations of the widget, the interaction does not support non-linear transforms.
-
Do not allow interactive and transform visualization at the same time The interaction takes precedence over the transform visualization.
-
Default scale size of widget: The default size is hard-coded for now in the displayable manager. It could made into a parameter later easily however.
-
Testing: The widget is tested through the
SlicerTransformInteractionTest1
. To do so, an internal method to access the widget was added to thevtkMRMLTransformsDisplayableManager3D
.
Limitations and future work:
- 2D interaction Unfortunately, VTK does not have an appropriate 2D widget that would work. The only remotely close widget vtkBorderWidget does not handle rotations correctly. To have the 2D interaction working, one would first have to write a VTK 2D widget for it.