Example #1
0
 def addStopWatch(self):
   from libs.StopWatch import StopWatch 
   self.stopWatch = StopWatch()
Example #2
0
class ConoProbeConnectorLogic(ScriptedLoadableModuleLogic):

  def __init__(self):  
    # Member variables
    self.outputLabels = None    
    self.recordedDataBuffer = []   
    self.record = False
    self.reset = False
    self.outputObserverTag = -1
    self.rigidBodyToTrackerTransformNode = None
    self.measurementToMeasurerTransformNode = None
    self.parametersToMeasurerTransformNode = None
    self.plus = None
    self.m = vtk.vtkMatrix4x4()
    self.direction = -1
    self.ras = [0, 0, 0, 1]
    self.d = 0.0
    self.snr = 0
    self.total = 0
    self.LABEL_UPDATE_RATE = 10  
    self.labelUpdateCount = 0
    self.snrThreshold = 40       
    self.distanceMaximumValue = 1000.0       
    self.distanceMinimumValue = 0.0 
    self.lensMaxDistance = 0.0
    self.lensMinDistance = 0.0
    self.normalizingConstant = 0.0
    self.min = -1000.0
    self.addColours = True
    import Viewpoint # Viewpoint
    self.viewpointLogic = Viewpoint.ViewpointLogic()
    self.stopWatch = None # StopWatch        
    # Create style sheets        
    self.errorStyleSheet = "QLabel { color : #FF0000; \
                                font: bold 14px}"
    self.defaultStyleSheet = "QLabel { color : #000000; \
                                  font: bold 14px}"        
    # Create rainbow colour table                                      
    self.colorTable=slicer.vtkMRMLColorTableNode()
    self.colorTable.SetTypeToRainbow ()                                    
    # Add MeasurementPoint
    self.measurementPointMarkupsFiducialNode = slicer.util.getNode('MeasurementPoint')
    if not self.measurementPointMarkupsFiducialNode:
      self.measurementPointMarkupsFiducialNode = slicer.vtkMRMLMarkupsFiducialNode()  
      self.measurementPointMarkupsFiducialNode.SetName('MeasurementPoint')
      self.measurementPointMarkupsFiducialNode.AddFiducial(0, 0, 0)
      self.measurementPointMarkupsFiducialNode.SetNthFiducialLabel(0, '')
      slicer.mrmlScene.AddNode(self.measurementPointMarkupsFiducialNode)
      self.measurementPointMarkupsFiducialNode.GetDisplayNode().SetGlyphScale(2.0)
      self.measurementPointMarkupsFiducialNode.GetDisplayNode().SetGlyphType(13) # Sphere3D
      self.measurementPointMarkupsFiducialNode.GetDisplayNode().SetSelectedColor(1, 0, 0)     
    # Add RecordedModel
    self.recordedModelNode = slicer.util.getNode('RecordedModel')
    if not self.recordedModelNode:
      recordedPoints = vtk.vtkPoints()
      recordedVertices = vtk.vtkCellArray()               
      recordedPolyData = vtk.vtkPolyData()
      recordedPolyData.SetPoints(recordedPoints)
      recordedPolyData.SetVerts(recordedVertices)
      self.recordedModelNode = self.addModelToScene(recordedPolyData, "RecordedModel")    
      self.recordedModelNode.GetModelDisplayNode().SetPointSize(3)
      # Set up coloured scalars  
      colorArray = vtk.vtkDoubleArray()
      colorArray.SetNumberOfComponents(4)
      colorArray.SetName('Colors')
      self.recordedModelNode.GetPolyData().GetPointData().SetScalars(colorArray)          
    # Create share directory
    self.pathToCreatedSaveDir = self.createShareDirectory()    
    # Post-Processing default (for undo)
    self.recordedDataBufferDefault = []
    
  def __del__(self):
    self.removeUpdateObserver()
    self.viewpointLogic.stopViewpoint()
  
  def initColouring(self, direction, min, max):
    if max > min:
      newMin = 0
      newMax = 255
      self.min = min
      self.direction = direction
      self.normalizingConstant = (newMax - newMin) / (max - min)
      return True
    else:
      logging.warning('Not possible to enable Intuitive Colouring: max <= min')
      return False
  
  def addStopWatch(self):
    from libs.StopWatch import StopWatch 
    self.stopWatch = StopWatch()
  
  def startViewpoint(self):
    self.viewpointLogic.startViewpoint()
  
  def stopViewpoint(self):
    self.viewpointLogic.stopViewpoint()
    
  def initViewpoint(self):
    if self.rigidBodyToTrackerTransformNode:
      # ConoProbeModelToMeasurement
      conoProbeModelToMeasurement = slicer.util.getNode('ConoProbeModelToMeasurement')      
      if not conoProbeModelToMeasurement:
        conoProbeModelToMeasurement=slicer.vtkMRMLLinearTransformNode()
        conoProbeModelToMeasurement.SetName("ConoProbeModelToMeasurement")
        m = vtk.vtkMatrix4x4()
        # Large lens
        # m.SetElement( 0, 0, -0.92 ) # Row 1
        # m.SetElement( 0, 1, -0.16 )
        # m.SetElement( 0, 2, 0.37 )
        # m.SetElement( 0, 3, 63.53 )      
        # m.SetElement( 1, 0, -0.38 )  # Row 2
        # m.SetElement( 1, 1, 0.06 )
        # m.SetElement( 1, 2, -0.92 )
        # m.SetElement( 1, 3, 81.99 )       
        # m.SetElement( 2, 0, 0.13 )  # Row 3
        # m.SetElement( 2, 1, -0.98 )
        # m.SetElement( 2, 2, -0.12 )
        # m.SetElement( 2, 3, -40.15 )
        # Small lens
        m.SetElement( 0, 0, -0.92 ) # Row 1
        m.SetElement( 0, 1, -0.16 )
        m.SetElement( 0, 2, 0.37 )
        m.SetElement( 0, 3, 63.53 )      
        m.SetElement( 1, 0, -0.38 )  # Row 2
        m.SetElement( 1, 1, 0.06 )
        m.SetElement( 1, 2, -0.92 )
        m.SetElement( 1, 3, 81.99 )       
        m.SetElement( 2, 0, 0.13 )  # Row 3
        m.SetElement( 2, 1, -0.98 )
        m.SetElement( 2, 2, -0.12 )
        m.SetElement( 2, 3, 129.85 )
        conoProbeModelToMeasurement.SetMatrixTransformToParent(m)
        slicer.mrmlScene.AddNode(conoProbeModelToMeasurement)
      conoProbeModelToMeasurement.SetAndObserveTransformNodeID(self.rigidBodyToTrackerTransformNode.GetID())    
      # ViewPointToMeasurement
      viewPointToMeasurement = slicer.util.getNode('ViewPointToMeasurement')
      if not viewPointToMeasurement:
        viewPointToMeasurement=slicer.vtkMRMLLinearTransformNode()
        viewPointToMeasurement.SetName("ViewPointToMeasurement")
        m = vtk.vtkMatrix4x4()
        # Large lens
        # m.SetElement( 0, 0, -1 ) # Row 1
        # m.SetElement( 0, 1, 0 )
        # m.SetElement( 0, 2, 0 )
        # m.SetElement( 0, 3, 53.00 )      
        # m.SetElement( 1, 0, 0 )  # Row 2
        # m.SetElement( 1, 1, -1 )
        # m.SetElement( 1, 2, 0 )
        # m.SetElement( 1, 3, 88.00 )       
        # m.SetElement( 2, 0, 0 )  # Row 3
        # m.SetElement( 2, 1, 0 )
        # m.SetElement( 2, 2, 1 )
        # m.SetElement( 2, 3, -106 )
        # Small lens
        m.SetElement( 0, 0, -1 ) # Row 1
        m.SetElement( 0, 1, 0 )
        m.SetElement( 0, 2, 0 )
        m.SetElement( 0, 3, 53.00 )      
        m.SetElement( 1, 0, 0 )  # Row 2
        m.SetElement( 1, 1, -1 )
        m.SetElement( 1, 2, 0 )
        m.SetElement( 1, 3, 88.00 )       
        m.SetElement( 2, 0, 0 )  # Row 3
        m.SetElement( 2, 1, 0 )
        m.SetElement( 2, 2, 1 )
        m.SetElement( 2, 3, 54 )
        viewPointToMeasurement.SetMatrixTransformToParent(m)
        slicer.mrmlScene.AddNode(viewPointToMeasurement)
      viewPointToMeasurement.SetAndObserveTransformNodeID(self.rigidBodyToTrackerTransformNode.GetID())  
      # ConoProbeModel
      conoProbeModel = slicer.util.getNode('ConoProbeModel')
      if not conoProbeModel:
        moduleDirectoryPath = slicer.modules.conoprobeconnector.path.replace('ConoProbeConnector.py', '')
        slicer.util.loadModel(qt.QDir.toNativeSeparators(moduleDirectoryPath + '../../Data/Models/ConoProbeModel.stl'))
        conoProbeModel=slicer.util.getNode(pattern="ConoProbeModel")
        conoProbeModel.SetName("ConoProbeModel")   
        conoProbeModel.GetDisplayNode().SetOpacity(0.7)
      conoProbeModel.SetAndObserveTransformNodeID(conoProbeModelToMeasurement.GetID())      
      # Camera
      camera = slicer.util.getNode('Camera')
      if not camera:
        camera=slicer.vtkMRMLCameraNode()
        camera.SetName("Camera")
        slicer.mrmlScene.AddNode(camera)
      threeDView = slicer.util.getNode("view1")
      camera.SetActiveTag(threeDView.GetID())        
      # Viewpoint
      self.viewpointLogic.setCameraNode(camera)
      self.viewpointLogic.setTransformNode(viewPointToMeasurement)
      self.viewpointLogic.setModelPOVOnNode(conoProbeModel)
      self.viewpointLogic.SetCameraXPosMm(53)
      self.viewpointLogic.SetCameraYPosMm(72)
      self.viewpointLogic.SetCameraZPosMm(119)
      self.viewpointLogic.startViewpoint()

  def getLensMinMax(self):
    minMax = [0, 0]
    self.parametersToMeasurerTransformNode.GetMatrixTransformToParent(self.m)    
    minMax[0] = self.m.GetElement(2, 0)     
    minMax[1] = self.m.GetElement(2, 1)   
    return minMax

  def saveRecordedDataToDefault(self):    
    self.recordedDataBufferDefault = self.recordedDataBuffer 
    
  def performPostProcessing(self, snrThreshold, distanceMinimumValue, distanceMaximumValue): 
    # Create new vtkPolyData
    newPoints = vtk.vtkPoints()
    newVertices = vtk.vtkCellArray()               
    newPolyData = vtk.vtkPolyData()
    newPolyData.SetPoints(newPoints)
    newPolyData.SetVerts(newVertices)
    colorArray = vtk.vtkDoubleArray()
    colorArray.SetNumberOfComponents(4)
    colorArray.SetName('Colors')
    newPolyData.GetPointData().SetScalars(colorArray)   
    # Filter accordingly to the input parameters
    recordedDataBufferFiltered = []    
    for idx in range(len(self.recordedDataBuffer)):
      d = self.recordedDataBuffer[idx][3]
      snr = self.recordedDataBuffer[idx][4]
      if (snr > snrThreshold and
          d < distanceMaximumValue and       
          d > distanceMinimumValue):
          recordedDataBufferFiltered.append(self.recordedDataBuffer[idx])   
          self.addPointToPolyData(newPolyData, self.recordedDataBuffer[idx][0:3])
    # Update recorded model and buffer
    self.recordedModelNode.GetPolyData().DeepCopy(newPolyData)     
    self.recordedModelNode.GetPolyData().Modified()  
    self.recordedDataBuffer = recordedDataBufferFiltered
    
  def showColouring(self):
    self.recordedModelNode.GetModelDisplayNode().SetActiveScalarName('Colors')
    self.recordedModelNode.GetModelDisplayNode().SetScalarVisibility(True)  
    
  def undoPostProcessing(self):
    # Create new vtkPolyData
    newPoints = vtk.vtkPoints()
    newVertices = vtk.vtkCellArray()               
    newPolyData = vtk.vtkPolyData()
    newPolyData.SetPoints(newPoints)
    newPolyData.SetVerts(newVertices)
    colorArray = vtk.vtkDoubleArray()
    colorArray.SetNumberOfComponents(4)
    colorArray.SetName('Colors')
    newPolyData.GetPointData().SetScalars(colorArray)   
    # Filter accordingly to the input parameters
    recordedDataBufferFiltered = []    
    for idx in range(len(self.recordedDataBufferDefault)):
      self.addPointToPolyData(newPolyData, self.recordedDataBufferDefault[idx][0:3])
    # Update recorded model and buffer
    self.recordedModelNode.GetPolyData().DeepCopy(newPolyData)     
    self.recordedModelNode.GetPolyData().Modified()  
    self.recordedDataBuffer = self.recordedDataBufferDefault
    
  def setOutPutLabels(self, labels):
    self.outputLabels = labels
    
  def saveData(self):
    dateAndTime = time.strftime("_%Y-%m-%d_%H-%M-%S")  
    path = self.pathToCreatedSaveDir + '/Points' + dateAndTime  
    # Save model to .vtk
    slicer.modules.models.logic().SaveModel(path + '.vtk', self.recordedModelNode)
    # Save data to .csv 
    self.writeRecordedDataBufferToFile(path + '.csv')  
    saveDir = self.pathToCreatedSaveDir.split('SlicerModules')[1] # Just to remove unnecessary information
    self.outputLabels[3].setText('Data saved to: ' + saveDir)
    
  def writeRecordedDataBufferToFile(self, path): 
    with open(path, 'wb') as csvfile:
      writer = csv.writer(csvfile, delimiter=",")
      writer.writerow(['x_ConoProbe', 'y_ConoProbe', 'z_ConoProbe', 'd', 'snr', 'total', 'x_RigidBody', 'y_RigidBody', 'z_RigidBody', 'Rxx', 'Rxy', 'Rxz', 'Ryx', 'Ryy', 'Ryz', 'Rzx', 'Rzy', 'Rzz'])
      for idx in range(len(self.recordedDataBuffer)):       
        writer.writerow(self.recordedDataBuffer[idx])
  
  def getModelXYZ(self):
    xyz = []
    for idx in range(self.recordedModelNode.GetPolyData().GetPoints().GetNumberOfPoints()):
      point = [0, 0, 0]
      self.recordedModelNode.GetPolyData().GetPoint(idx, point)
      xyz.append(point)      
    return xyz
  
  def createShareDirectory(self): 
    date = time.strftime("%Y-%m-%d")        
    shareDirPath = slicer.modules.conoprobeconnector.path.replace("ConoProbeConnector.py","") + 'Output/' + date
    if not os.path.exists(shareDirPath):
      os.makedirs(shareDirPath)   
    return shareDirPath
    
  def addModelToScene(self, polyData, name):
    scene = slicer.mrmlScene
    node = slicer.vtkMRMLModelNode()
    node.SetScene(scene)
    node.SetName(name)
    node.SetAndObservePolyData(polyData)
    modelDisplay = slicer.vtkMRMLModelDisplayNode()
    modelDisplay.SetColor(0, 1, 0)
    modelDisplay.SetScene(scene)
    scene.AddNode(modelDisplay)
    node.SetAndObserveDisplayNodeID(modelDisplay.GetID())
    scene.AddNode(node)
    return node
  
  def clearPointsInRecordedModel(self): 
    self.recordedDataBuffer = [] 
    newPoints = vtk.vtkPoints()
    newVertices = vtk.vtkCellArray()               
    newPolyData = vtk.vtkPolyData()
    newPolyData.SetPoints(newPoints)
    newPolyData.SetVerts(newVertices)
    colorArray = vtk.vtkDoubleArray()
    colorArray.SetNumberOfComponents(4)
    colorArray.SetName('Colors')
    newPolyData.GetPointData().SetScalars(colorArray)   
    self.recordedModelNode.GetPolyData().DeepCopy(newPolyData)     
    self.recordedModelNode.GetPolyData().Modified()        
      
  def setupPlus(self):
    connectorNode = self.createPlusConnector()
    connectorNode.Start()      
    self.plus = Plus(connectorNode.GetID(), self.pathToCreatedSaveDir)
  
  def plusStartRecording(self):
    dateAndTime = time.strftime("_%Y-%m-%d_%H-%M-%S")
    self.plus.startStopRecording(True, 'PlusRecording' + dateAndTime + '.mha')

  def plusStopRecording(self):    
    self.plus.startStopRecording(False)
    
  def createTransformTree(self):
    # Get transform nodes from scene
    if not self.rigidBodyToTrackerTransformNode:
      self.rigidBodyToTrackerTransformNode = slicer.util.getNode('RigidBodyToTracker')
    if not self.measurementToMeasurerTransformNode:
      self.measurementToMeasurerTransformNode = slicer.util.getNode('MeasurementToMeasure')
    if not self.parametersToMeasurerTransformNode:
      self.parametersToMeasurerTransformNode = slicer.util.getNode('ParametersToMeasurer')
    if not self.measurementToMeasurerTransformNode or not self.parametersToMeasurerTransformNode:
      logging.error('Missing transform! Is Plus properly configured?')
      self.outputLabels[3].setText('Missing transform! Is PlusServer running and properly configured?')
      self.outputLabels[3].setStyleSheet(self.errorStyleSheet)
      return False
    if not self.rigidBodyToTrackerTransformNode:
      logging.warning('No tracking transform present, only raw measurement.')      
    self.outputLabels[3].setText(' ')
    self.outputLabels[3].setStyleSheet(self.defaultStyleSheet)       
    if self.measurementToMeasurerTransformNode:
      self.measurementPointMarkupsFiducialNode.SetAndObserveTransformNodeID(self.measurementToMeasurerTransformNode.GetID())
    if self.rigidBodyToTrackerTransformNode:
      self.measurementToMeasurerTransformNode.SetAndObserveTransformNodeID(self.rigidBodyToTrackerTransformNode.GetID())
    else:
      self.outputLabels[3].setText('No tracking transform present, only raw measurement.')
    return True
    
  # From: SlicerIGT/GuideletLib
  # Takes for granted IP:port is localhost:18944
  def createPlusConnector(self):
    connectorNode = slicer.util.getNode('PlusConnector')
    if not connectorNode:
      connectorNode = slicer.vtkMRMLIGTLConnectorNode()
      slicer.mrmlScene.AddNode(connectorNode)
      connectorNode.SetName('PlusConnector')      
      hostNamePort = "localhost:18944"
      [hostName, port] = hostNamePort.split(':')
      connectorNode.SetTypeClient(hostName, int(port))
      logging.debug("PlusConnector created")
    return connectorNode
    
  def addUpdateObserver(self):
    if self.outputObserverTag == -1:
      self.outputObserverTag = self.rigidBodyToTrackerTransformNode.AddObserver('ModifiedEvent', self.updateSceneCallback)
      logging.info('addOutputObserver')

  def removeUpdateObserver(self):
    if self.outputObserverTag != -1:
      self.parametersToMeasurerTransformNode.RemoveObserver(self.outputObserverTag)
      self.outputObserverTag = -1
      logging.info('removeOutputObserver')
  
  def updateSceneCallback(self, modifiedNode, event=None):                    
    # This lowers the update rate of the labels to only every self.LABEL_UPDATE_RATE measurement
    self.labelUpdateCount = self.labelUpdateCount + 1
    updateLabels = not self.labelUpdateCount % self.LABEL_UPDATE_RATE 
    if self.reset:
      self.clearPointsInRecordedModel()
      self.reset = False
    if updateLabels or self.record:
      # Get ConoProbe parameters
      self.parametersToMeasurerTransformNode.GetMatrixTransformToParent(self.m)
      self.d = self.m.GetElement(0, 0)
      self.snr = self.m.GetElement(0, 1)
      self.total = self.m.GetElement(0, 2)       
      self.power = self.m.GetElement(1, 1)
      self.frequency = self.m.GetElement(1, 0)   
      self.lensMaxDistance = self.m.GetElement(2, 0)   
      self.lensMinDistance = self.m.GetElement(2, 1)   
      # Get measurement coordinate
      self.measurementPointMarkupsFiducialNode.GetNthFiducialWorldCoordinates(0, self.ras)      
    if updateLabels:
      # Update output labels
      self.updateOutputLabels()      
      self.labelUpdateCount = 0
    if (self.record and self.snr > self.snrThreshold and self.d < self.distanceMaximumValue and self.d > self.distanceMinimumValue):
       self.acquireSingleMeasurement()
      
  def acquireSingleMeasurement(self):
    # Only for printing tracker position to file
    xRigidBodyToTracker = 0
    yRigidBodyToTracker = 0
    zRigidBodyToTracker = 0
    Rxx = 0
    Rxy = 0
    Rxz = 0
    Ryx = 0
    Ryy = 0
    Ryz = 0
    Rzx = 0
    Rzy = 0
    Rzz = 0
    if self.rigidBodyToTrackerTransformNode:
      self.rigidBodyToTrackerTransformNode.GetMatrixTransformToParent(self.m)
      xRigidBodyToTracker = self.m.GetElement(0, 3)
      yRigidBodyToTracker = self.m.GetElement(1, 3)
      zRigidBodyToTracker = self.m.GetElement(2, 3) 
      Rxx = self.m.GetElement(0, 0)
      Rxy = self.m.GetElement(0, 1)
      Rxz = self.m.GetElement(0, 2) 
      Ryx = self.m.GetElement(1, 0)
      Ryy = self.m.GetElement(1, 1)
      Ryz = self.m.GetElement(1, 2) 
      Rzx = self.m.GetElement(2, 0)
      Rzy = self.m.GetElement(2, 1)
      Rzz = self.m.GetElement(2, 2) 
    self.recordedDataBuffer.append([self.ras[0], self.ras[1], self.ras[2], 
                                    self.d, self.snr, self.total, 
                                    xRigidBodyToTracker, yRigidBodyToTracker, zRigidBodyToTracker,
                                    Rxx, Rxy, Rxz, Ryx, Ryy, Ryz, Rzx, Rzy, Rzz])      
    self.addPointToPolyData(self.recordedModelNode.GetPolyData(), self.ras)
  
  def updateOutputLabels(self):
    self.outputLabels[0].setText('%.1f' % self.d)
    self.outputLabels[1].setText('%.0f' % self.snr)
    self.outputLabels[2].setText('%.0f' % self.total)
    self.outputLabels[4].setText('[' + '%.1f' % self.ras[0] + ', ' + '%.1f' %  self.ras[1] + ', '+ '%.1f' %  self.ras[2] + ']')
    self.outputLabels[5].setText(str(self.recordedModelNode.GetPolyData().GetPoints().GetNumberOfPoints()))    
    self.outputLabels[6].setText('%.2f' % self.stopWatch.getElapsedTime())
    self.outputLabels[7].setText('%.0f' % self.power)
    self.outputLabels[8].setText('%.0f' % self.frequency)
    self.outputLabels[10].setText('%.1f' % self.lensMaxDistance)
    self.outputLabels[9].setText('%.1f' % self.lensMinDistance)
    
  def addPointToPolyData(self, polyData, ras):      
    pid = vtk.vtkIdList()
    pid.SetNumberOfIds(1);
    temp = polyData.GetPoints().InsertNextPoint(ras[0], ras[1], ras[2])    
    pid.SetId(0, temp)    
    polyData.GetVerts().InsertNextCell(pid)        
    if self.direction > -1:          
      color = [0, 0, 0, 0]  
      tableValue = 1 + (ras[self.direction] - self.min) * self.normalizingConstant
      if tableValue > 255:
        tableValue = 255
      elif tableValue < 0:
        tableValue = 0
      self.colorTable.GetColor(int(tableValue), color) # 0 -> 255
      self.recordedModelNode.GetPolyData().GetPointData().GetScalars('Colors').InsertTuple(self.recordedModelNode.GetPolyData().GetPoints().GetNumberOfPoints() - 1, color)
      if self.addColours:
        self.showColouring()
        self.addColours = False
    polyData.Modified() 
    
  def openProbeDialog(self):
    self.plus.showProbeDialog()
    
  def setLayout(self):
    lm=slicer.app.layoutManager()
    lm.setLayout(4) # One 3D-view
    self.resetLayoutFocalPoint(0)  
    self.zoomInThreeDView(0, 8)    
    self.setAxisAndBoxVisibility('View1', False)  
  
  def setAxisAndBoxVisibility(self, viewName, visible):
    view = slicer.mrmlScene.GetFirstNodeByName(viewName)               
    view.SetAxisLabelsVisible(visible)
    view.SetBoxVisible(visible)  
  
  def resetLayoutFocalPoint(self, viewIdx):
    threeDWidget = slicer.app.layoutManager().threeDWidget(viewIdx)
    threeDView = threeDWidget.threeDView()
    threeDView.resetFocalPoint()   
  
  def zoomInThreeDView(self, viewIdx, zoomFactor):
    threeDWidget = slicer.app.layoutManager().threeDWidget(viewIdx)
    threeDView = threeDWidget.threeDView()
    for zoom in range(zoomFactor):
      threeDView.zoomIn()
      
  # Available ctkAxes: 
  # - ctk.ctkAxesWidget.Right 
  # - ctk.ctkAxesWidget.Left
  # - ctk.ctkAxesWidget.Inferior
  # - ctk.ctkAxesWidget.Superior
  # - ctk.ctkAxesWidget.Posterior
  # - ctk.ctkAxesWidget.Anterior
  def setFocalPointByAxis(self, viewIdx, ctkAxis):
    view=slicer.app.layoutManager().threeDWidget(viewIdx).threeDView() 
    view.lookFromViewAxis(ctkAxis)