I’m also trying to clip models as a tracked tool is being moved around, by combining the models module and the volume reslice driver. However it seems that models cannot be clipped in real-time - I manage to get only 1 frame/second. I tried decimating the model, but I have to decimate by 99% to get anywhere near real-time (20/30 FPS) model clipping. Is there a solution to this?
Another issue I’ve come across with clipping modules from the models module is that if you have two different 3D views (‘View1’ and ‘View2’) each showing a different model, and you set both models to be clipped, the clipping parameters will be shared between the two models and views. I think this is a bug, as clipping parameters should be separated by models and views.
We have implemented many navigation systems over the years and I don’t remember model clipping ever came up as a requirement. Could you write a bit more about your use case? How do you envision to utilize model clipping?
This is the intended behavior.
You can always add an observer to a slice node and use it to clip your polydata with a filter. You can then choose what to cut and what to show in each view. If you don’t need straight cut then maybe you can find a filter that can clip faster.
How many points and cells are in your data set before and after decimation?
We want to use model clipping to visualize in 3D how the tool lines up with the preoperatively planned path for orthopaedic surgery. Here are two images showing the preop model with pre-op screw plans, and the same model clipped by two orthogonal planes defined by the tool tip.
This is to aid the volume reslice views which only show 2D visualizations of the tool relative to the patient.
Could you explain why this is the intended behaviour? I would expect different models and views to have independent properties, especially as other model and view properties are not shared (lighting, surface/point rendering, etc.)
I’m not sure what you mean by cutting/clipping with a filter? Do you have any examples? We would like cuts defined by the tracked tool’s planes.
Before decimation: ~1 million points and 2 million cells.
After decimation: 55,000 points and 20,000 cells but the anatomic detail is not sufficient
It seems that your use case is not a good match for what model clipping offers. However, Slicer is so flexible that there are always many alternative options. You can only clip a 1M point model efficiently at the end of the rendering pipeline, in the mapper. So, instead of using model clipping, you can go one level lower: get the polydata mapper from the displayable manager and then add a clipping plane manually:
modelNode = getNode('SpinePhantom2Model')
threeDWidgetIndex = 0
# Get model displayable manager
threeDViewWidget = slicer.app.layoutManager().threeDWidget(threeDWidgetIndex)
managers = vtk.vtkCollection()
threeDViewWidget.getDisplayableManagers(managers)
for i in range(managers.GetNumberOfItems()):
obj = managers.GetItemAsObject(i)
if obj.IsA('vtkMRMLModelDisplayableManager'):
modelDisplayableManager = obj
break
# Get polydata mapper for the model node
modelMapper = modelDisplayableManager.GetActorByID(modelNode.GetDisplayNode().GetID()).GetMapper()
# Add a clipping plane
clippingPlane = vtk.vtkPlane()
modelMapper.AddClippingPlane(clippingPlane)
# Set clipping plane position
clippingPlane.SetOrigin(30,30,30)
clippingPlane.SetNormal(0,0,1)
threeDViewWidget.threeDView().scheduleRender()
# Move clipping plane to a different position
clippingPlane.SetOrigin(30,30,80)
threeDViewWidget.threeDView().scheduleRender()
I tried the above snippet and it can clip the example model in a static way, but how would I change the quoted lines above so that clipping is done dynamically based on my red and yellow slice planes (which are being computed using the volume reslice driver)?
You can add an observer to the tool’s transform node and update the clipping plane position and orientation whenever the transform is modified.
Thanks!
I’m new to using observers. How do I pass more arguments to the AddObserver function - for example adapting your above snippet I want :
def onTransformNodeModified(transformNode, unusedArg2=None, unusedArg3=None):
transformMatrix = vtk.vtkMatrix4x4()
transformNode.GetMatrixTransformToWorld(transformMatrix)
clippingPlane.SetTransform([transformMatrix.GetElement(0,0), transformMatrix.GetElement(0,1), transformMatrix.GetElement(0,2),transformMatrix.GetElement(0,3),
transformMatrix.GetElement(1,0), transformMatrix.GetElement(1,1),transformMatrix.GetElement(1,2),transformMatrix.GetElement(1,3),
transformMatrix.GetElement(2,0), transformMatrix.GetElement(2,1),transformMatrix.GetElement(2,2),transformMatrix.GetElement(2,3),
transformMatrix.GetElement(3,0), transformMatrix.GetElement(3,1),transformMatrix.GetElement(3,2),transformMatrix.GetElement(3,3)])
threeDViewWidget.threeDView().scheduleRender()
tNode = slicer.mrmlScene.GetNodeByID('vtkMRMLLinearTransformNode4')
tNode.AddObserver(slicer.vtkMRMLTransformNode.TransformModifiedEvent, onTransformNodeModified)
When I try this and change the transform, the model remains unclipped.
I don’t think SetTransform
is taken into account when using the plane to define a clipping plane in the mapper. Instead, set the origin (from the 4th column of the transform) and normal (3rd column) as I showed in my example above.
Thanks, that worked great!
So now I’m trying to extend this by having two 3D viewers side-by-side, where the same model is in both viewers but is being clipped by different planes in each viewer. Is this possible? I tried changing the 'threeDWidgetIndex from 0 to 1 or 2 but this either crashes Slicer or does not appear to do anything.
You can ask the number of threeDWidgets from the layout manager. If you try to get an index that goes beyond that number-1 then you’ll make Slicer crash. If you ask for a valid index then it should work well.
Thanks, it’s working perfectly now! See: https://drive.google.com/open?id=1dpjrrUuoFvtAltOaN_RfPkhyqnYpleU6
Just wondering if it’s possible to clip volumes with a clipping plane too (as opposed to a ROI annotation)? We have a powerful GPU machine and Slicer renders medical volumes very nicely so it would be nice to be able to use that in the same way.
Volume rendering displayable manager already uses the mapper’s clipping plane for clipping. You can simply apply the tool transform to the clipping ROI node.
If I understand, for volume rendering it’s a ROI not a plane? Can I apply a plane instead?
I tried transforming the ROI with the tool transform, and manually adjusting the extents of the ROI to make it like a plane, but that isn’t ideal.
If you just want to clip with a single plane then move the other clipping planes very far (this is what we usually do).
You can manipulate the mapper at lower level at replace the clipping plane set with a single plane but then you will fight with the volume rendering displayable manager (time to time the displayable manager will reset the clipping plane set that is stored in MRML).