Hello everyone,
I am new to Slicer, and I have gone through some tutorial PDFs provided in the developer section of the official site. I have created a scripted module using the Extension Wizard. For my module, I am implementing functionality that allows visualization of the slice visibility of a 3D model, specifically the slice intersection.
I found that the class vtkMRMLModelSliceDisplayableManager
is responsible for slice visibility when it is enabled in the Models module. Based on this, I created a rendering pipeline. However, the actor is not visible. Previously, it was visible but not correctly positioned in the slice views—it appeared in the bottom-left corner of the Axial view.
Here is my code:
class SliceVisibilityPipeline(VTKObservationMixin):
def __init__(self, modelNode, sliceViewName='Red'):
VTKObservationMixin.__init__(self)
self.modelNode = modelNode
self.modelDisplayNode = self.modelNode.GetDisplayNode()
self.sliceViewName = sliceViewName
# Get slice node and renderer
layoutManager = slicer.app.layoutManager()
self.sliceWidget = layoutManager.sliceWidget(self.sliceViewName)
self.sliceView = self.sliceWidget.sliceView()
self.renderWindow = self.sliceView.renderWindow()
self.renderer = self.renderWindow.GetRenderers().GetFirstRenderer()
self.sliceLogic = self.sliceWidget.sliceLogic()
self.sliceNode = self.sliceLogic.GetSliceNode()
# Create pipeline
self.plane = vtk.vtkPlane()
self.nodeToWorld = vtk.vtkGeneralTransform()
# Model warper
self.modelWarper = vtk.vtkTransformFilter()
self.modelWarper.SetTransform(self.nodeToWorld)
# Plane cutter
self.cutter = vtk.vtkPlaneCutter()
self.cutter.SetPlane(self.plane)
self.cutter.SetInputConnection(self.modelWarper.GetOutputPort())
# Transform to slice space
self.transformToSlice = vtk.vtkTransform()
self.transformer = vtk.vtkTransformPolyDataFilter()
self.transformer.SetTransform(self.transformToSlice)
self.transformer.SetInputConnection(self.cutter.GetOutputPort())
# Set up mapper and actor for display
self.mapper = vtk.vtkPolyDataMapper2D()
self.mapper.SetInputConnection(self.transformer.GetOutputPort())
self.mapper.ScalarVisibilityOff()
self.actor = vtk.vtkActor2D()
self.actor.SetMapper(self.mapper)
self.actor.SetVisibility(0)
# Add actor to renderer
self.renderer.AddActor2D(self.actor)
# Set up initial pipeline
self.updatePipeline()
# Observe slice node changes
self.addObserver(self.sliceNode, vtk.vtkCommand.ModifiedEvent, self.onSliceModified)
def updatePipeline(self):
self.cleanup()
if not self.modelNode or not self.modelDisplayNode:
print("No model or display node found.")
self.actor.SetVisibility(False)
return
# Get the mesh data
polyData = self.modelNode.GetPolyData()
if not polyData or polyData.GetNumberOfPoints() == 0:
print("No points are found in model")
self.actor.SetVisibility(False)
return
# Update the model warper input
self.modelWarper.SetInputData(polyData)
# Get transform from model to world
if self.modelNode.GetParentTransformNode():
self.modelNode.GetParentTransformNode().GetTransformToWorld(self.nodeToWorld)
else:
self.nodeToWorld.Identity()
# Update the slice plane based on current slice position
sliceXYToRAS = vtk.vtkMatrix4x4()
sliceXYToRAS.DeepCopy(self.sliceNode.GetXYToRAS())
self.updateSlicePlane(sliceXYToRAS, self.plane)
# Update the transform from RAS to slice XY
rasToSliceXY = vtk.vtkMatrix4x4()
vtk.vtkMatrix4x4.Invert(sliceXYToRAS, rasToSliceXY)
self.transformToSlice.SetMatrix(rasToSliceXY)
# Force update of the pipeline to check if there are any points
self.cutter.Update()
if self.cutter.GetOutput().GetNumberOfPoints() < 1:
print("No intersection points found")
self.actor.SetVisibility(False)
return
# Update actor properties
actorProperty = self.actor.GetProperty()
actorProperty.SetColor(self.modelDisplayNode.GetColor())
actorProperty.SetLineWidth(self.modelDisplayNode.GetSliceIntersectionThickness())
actorProperty.SetPointSize(self.modelDisplayNode.GetSliceIntersectionThickness())
actorProperty.SetOpacity(self.modelDisplayNode.GetSliceIntersectionOpacity())
# Make actor visible
self.actor.SetVisibility(True)
# Force render update
self.renderWindow.Render()
# Observe slice movements to update plane and cutter
self.sliceNodeObserverTag = self.sliceNode.AddObserver(vtk.vtkCommand.ModifiedEvent, self.onSliceModified)
def updateSlicePlane(self, sliceMatrix, plane):
normal = [0, 0, 0]
origin = [0, 0, 0]
for i in range(3):
normal[i] = sliceMatrix.GetElement(i, 2)
origin[i] = sliceMatrix.GetElement(i, 3)
vtk.vtkMath.Normalize(normal)
plane.SetNormal(normal)
plane.SetOrigin(origin)
def onSliceModified(self, caller, event):
# Update the slice plane when the slice changes
self.updatePipeline()
def cleanup(self):
if hasattr(self, 'sliceNode') and hasattr(self, 'sliceNodeObserverTag'):
self.sliceNode.RemoveObserver(self.sliceNodeObserverTag)
if hasattr(self, 'renderer') and hasattr(self, 'actor'):
self.renderer.RemoveActor2D(self.actor)
self.renderWindow.Render()
I suspect the issue might lie in the transformation, though I am not sure. Could anyone please help me identify where I might be making a mistake?
Thanks.