From f7b5fc4d6a0a17b0359510217568644ca344dc5a Mon Sep 17 00:00:00 2001 From: Thiago Franco de Moraes Date: Thu, 21 Mar 2013 15:40:43 -0300 Subject: [PATCH] Created a new interactor style to handle the cross tool --- invesalius/data/styles.py | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ invesalius/data/viewer_slice.py | 268 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------------------------------------------------------------------------- 2 files changed, 264 insertions(+), 141 deletions(-) diff --git a/invesalius/data/styles.py b/invesalius/data/styles.py index f8c33d3..a9cdf7b 100644 --- a/invesalius/data/styles.py +++ b/invesalius/data/styles.py @@ -19,8 +19,145 @@ import vtk +from wx.lib.pubsub import pub as Publisher + import constants as const +class ZoomInteractorStyle(vtk.vtkInteractorStyleImage): + """ + Interactor style responsible for zoom the camera. + """ + def __init__(self): + self.right_pressed = False + + # Zoom using right button + self.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) + self.AddObserver("MouseMoveEvent", self.OnZoomRightMove) + self.AddObserver("RightButtonReleaseEvent", self.OnZoomRightRelease) + + def OnZoomRightMove(self, evt, obj): + if (self.right_pressed): + evt.Dolly() + evt.OnRightButtonDown() + + def OnZoomRightClick(self, evt, obj): + self.right_pressed = 1 + evt.StartDolly() + + def OnZoomRightRelease(self, evt, obj): + self.right_pressed = False + +class CrossInteractorStyle(ZoomInteractorStyle): + """ + Interactor style responsible for the Cross. + """ + def __init__(self, orientation, slice_data): + ZoomInteractorStyle.__init__(self) + + self.orientation = orientation + self.slice_actor = slice_data.actor + self.slice_data = slice_data + + self.left_pressed = False + self.picker = vtk.vtkWorldPointPicker() + + self.AddObserver("MouseMoveEvent", self.OnCrossMove) + self.AddObserver("LeftButtonPressEvent", self.OnCrossMouseClick) + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) + + def OnCrossMouseClick(self, obj, evt): + self.left_pressed = True + iren = obj.GetInteractor() + self.ChangeCrossPosition(iren) + + def OnCrossMove(self, obj, evt): + # The user moved the mouse with left button pressed + if self.left_pressed: + print "OnCrossMove interactor style" + iren = obj.GetInteractor() + self.ChangeCrossPosition(iren) + + def OnReleaseLeftButton(self, obj, evt): + self.left_pressed = False + + def ChangeCrossPosition(self, iren): + mouse_x, mouse_y = iren.GetEventPosition() + ren = iren.GetRenderWindow().GetRenderers().GetFirstRenderer() + self.picker.Pick(mouse_x, mouse_y, 0, ren) + + # Get in what slice data the click occurred + # pick to get click position in the 3d world + coord_cross = self.get_coordinate_cursor() + position = self.slice_actor.GetInput().FindPoint(coord_cross) + # Forcing focal point to be setted in the center of the pixel. + coord_cross = self.slice_actor.GetInput().GetPoint(position) + + coord = self.calcultate_scroll_position(position) + self.ScrollSlice(coord) + + Publisher.sendMessage('Update cross position', coord_cross) + Publisher.sendMessage('Set ball reference position based on bound', + coord_cross) + Publisher.sendMessage('Set camera in volume', coord_cross) + Publisher.sendMessage('Render volume viewer') + + iren.Render() + + def get_coordinate_cursor(self): + # Find position + x, y, z = self.picker.GetPickPosition() + bounds = self.slice_actor.GetBounds() + if bounds[0] == bounds[1]: + x = bounds[0] + elif bounds[2] == bounds[3]: + y = bounds[2] + elif bounds[4] == bounds[5]: + z = bounds[4] + return x, y, z + + def calcultate_scroll_position(self, position): + # Based in the given coord (x, y, z), returns a list with the scroll positions for each + # orientation, being the first position the sagital, second the coronal + # and the last, axial. + + if self.orientation == 'AXIAL': + image_width = self.slice_actor.GetInput().GetDimensions()[0] + axial = self.slice_data.number + coronal = position / image_width + sagital = position % image_width + + elif self.orientation == 'CORONAL': + image_width = self.slice_actor.GetInput().GetDimensions()[0] + axial = position / image_width + coronal = self.slice_data.number + sagital = position % image_width + + elif self.orientation == 'SAGITAL': + image_width = self.slice_actor.GetInput().GetDimensions()[1] + axial = position / image_width + coronal = position % image_width + sagital = self.slice_data.number + + return sagital, coronal, axial + + def ScrollSlice(self, coord): + if self.orientation == "AXIAL": + Publisher.sendMessage(('Set scroll position', 'SAGITAL'), + coord[0]) + Publisher.sendMessage(('Set scroll position', 'CORONAL'), + coord[1]) + elif self.orientation == "SAGITAL": + Publisher.sendMessage(('Set scroll position', 'AXIAL'), + coord[2]) + Publisher.sendMessage(('Set scroll position', 'CORONAL'), + coord[1]) + elif self.orientation == "CORONAL": + Publisher.sendMessage(('Set scroll position', 'AXIAL'), + coord[2]) + Publisher.sendMessage(('Set scroll position', 'SAGITAL'), + coord[0]) + + class ViewerStyle: def __init__(self): self.interactor = None diff --git a/invesalius/data/viewer_slice.py b/invesalius/data/viewer_slice.py index b7ba8ef..06fc10a 100755 --- a/invesalius/data/viewer_slice.py +++ b/invesalius/data/viewer_slice.py @@ -27,6 +27,8 @@ import numpy import vtk from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor +import styles + import wx from wx.lib.pubsub import pub as Publisher @@ -173,127 +175,142 @@ class Viewer(wx.Panel): interactor.SetInteractorStyle(style) def SetInteractorStyle(self, state): - self.state = state - action = {const.SLICE_STATE_CROSS: - { - "MouseMoveEvent": self.OnCrossMove, - "LeftButtonPressEvent": self.OnCrossMouseClick, - }, - const.SLICE_STATE_EDITOR: - { - "MouseMoveEvent": self.OnBrushMove, - "LeftButtonPressEvent": self.OnBrushClick, - "LeftButtonReleaseEvent": self.OnBrushRelease, - "EnterEvent": self.OnEnterInteractor, - "LeaveEvent": self.OnLeaveInteractor - }, - const.STATE_PAN: - { - "MouseMoveEvent": self.OnPanMove, - "LeftButtonPressEvent": self.OnPanClick, - "LeftButtonReleaseEvent": self.OnVtkRightRelease - }, - const.STATE_SPIN: - { - "MouseMoveEvent": self.OnSpinMove, - "LeftButtonPressEvent": self.OnSpinClick, - "LeftButtonReleaseEvent": self.OnVtkRightRelease - }, - const.STATE_ZOOM: - { - "MouseMoveEvent": self.OnZoomMoveLeft, - "LeftButtonPressEvent": self.OnZoomLeftClick, - "LeftButtonReleaseEvent": self.OnVtkRightRelease - }, - const.SLICE_STATE_SCROLL: - { - "MouseMoveEvent": self.OnChangeSliceMove, - "LeftButtonPressEvent": self.OnChangeSliceClick, - }, - const.STATE_WL: - { - "MouseMoveEvent": self.OnWindowLevelMove, - "LeftButtonPressEvent": self.OnWindowLevelClick, - }, - const.STATE_DEFAULT: - { - }, - const.STATE_MEASURE_DISTANCE: - { - "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint - }, - const.STATE_MEASURE_ANGLE: - { - "LeftButtonPressEvent": self.OnInsertAngularMeasurePoint - }, - } + if state == const.SLICE_STATE_CROSS: + style = styles.CrossInteractorStyle(self.orientation, + self.slice_data) + self.style = style + self.interactor.SetInteractorStyle(style) + self.interactor.Render() + ## Zoom using right button + #style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) + #style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight) + #style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease) + + #Scroll change slice + style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward) + style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward) - if state == const.SLICE_STATE_CROSS: self.__set_cross_visibility(1) Publisher.sendMessage('Activate ball reference') else: - self.__set_cross_visibility(0) - Publisher.sendMessage('Deactivate ball reference') + self.state = state + action = { + const.SLICE_STATE_EDITOR: + { + "MouseMoveEvent": self.OnBrushMove, + "LeftButtonPressEvent": self.OnBrushClick, + "LeftButtonReleaseEvent": self.OnBrushRelease, + "EnterEvent": self.OnEnterInteractor, + "LeaveEvent": self.OnLeaveInteractor + }, + const.STATE_PAN: + { + "MouseMoveEvent": self.OnPanMove, + "LeftButtonPressEvent": self.OnPanClick, + "LeftButtonReleaseEvent": self.OnVtkRightRelease + }, + const.STATE_SPIN: + { + "MouseMoveEvent": self.OnSpinMove, + "LeftButtonPressEvent": self.OnSpinClick, + "LeftButtonReleaseEvent": self.OnVtkRightRelease + }, + const.STATE_ZOOM: + { + "MouseMoveEvent": self.OnZoomMoveLeft, + "LeftButtonPressEvent": self.OnZoomLeftClick, + "LeftButtonReleaseEvent": self.OnVtkRightRelease + }, + const.SLICE_STATE_SCROLL: + { + "MouseMoveEvent": self.OnChangeSliceMove, + "LeftButtonPressEvent": self.OnChangeSliceClick, + }, + const.STATE_WL: + { + "MouseMoveEvent": self.OnWindowLevelMove, + "LeftButtonPressEvent": self.OnWindowLevelClick, + }, + const.STATE_DEFAULT: + { + }, + const.STATE_MEASURE_DISTANCE: + { + "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint + }, + const.STATE_MEASURE_ANGLE: + { + "LeftButtonPressEvent": self.OnInsertAngularMeasurePoint + }, + } + + + if state == const.SLICE_STATE_CROSS: + self.__set_cross_visibility(1) + Publisher.sendMessage('Activate ball reference') + else: + self.__set_cross_visibility(0) + Publisher.sendMessage('Deactivate ball reference') - if state == const.STATE_WL: - self.on_wl = True - self.wl_text.Show() - else: - self.on_wl = False - self.wl_text.Hide() + if state == const.STATE_WL: + self.on_wl = True + self.wl_text.Show() + else: + self.on_wl = False + self.wl_text.Hide() - self.__set_editor_cursor_visibility(0) - - # Bind method according to current mode - if(state == const.STATE_ZOOM_SL): - style = vtk.vtkInteractorStyleRubberBandZoom() + self.__set_editor_cursor_visibility(0) + + # Bind method according to current mode + if(state == const.STATE_ZOOM_SL): + style = vtk.vtkInteractorStyleRubberBandZoom() - style.AddObserver("RightButtonPressEvent", self.QuitRubberBandZoom) - #style.AddObserver("RightButtonPressEvent", self.EnterRubberBandZoom) + style.AddObserver("RightButtonPressEvent", self.QuitRubberBandZoom) + #style.AddObserver("RightButtonPressEvent", self.EnterRubberBandZoom) - else: - style = vtk.vtkInteractorStyleImage() - - # Check each event available for each state - for event in action[state]: - # Bind event - style.AddObserver(event, - action[state][event]) - - # Common to all styles - # Mouse Buttons' presses / releases - style.AddObserver("LeftButtonPressEvent", self.OnLeftClick) - style.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) - style.AddObserver("RightButtonPressEvent", self.OnRightClick) - style.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton) - - # Zoom using right button - style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) - style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight) - style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease) - - #Scroll change slice - style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward) - style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward) - - if ((state == const.STATE_ZOOM) or (state == const.STATE_ZOOM_SL)): - self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom) - else: - self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnSpinPan) + else: + style = vtk.vtkInteractorStyleImage() + + # Check each event available for each state + for event in action[state]: + # Bind event + style.AddObserver(event, + action[state][event]) + + # Common to all styles + # Mouse Buttons' presses / releases + style.AddObserver("LeftButtonPressEvent", self.OnLeftClick) + style.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) + style.AddObserver("RightButtonPressEvent", self.OnRightClick) + style.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton) + + # Zoom using right button + style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) + style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight) + style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease) + + #Scroll change slice + style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward) + style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward) + + if ((state == const.STATE_ZOOM) or (state == const.STATE_ZOOM_SL)): + self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom) + else: + self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnSpinPan) - # Measures are using vtkPropPicker because they need to get which actor - # was picked. - if state in (const.STATE_MEASURE_DISTANCE, const.STATE_MEASURE_ANGLE): - self.pick = vtk.vtkPropPicker() - self.interactor.SetPicker(self.pick) - else: - self.pick = vtk.vtkWorldPointPicker() - self.interactor.SetPicker(self.pick) + # Measures are using vtkPropPicker because they need to get which actor + # was picked. + if state in (const.STATE_MEASURE_DISTANCE, const.STATE_MEASURE_ANGLE): + self.pick = vtk.vtkPropPicker() + self.interactor.SetPicker(self.pick) + else: + self.pick = vtk.vtkWorldPointPicker() + self.interactor.SetPicker(self.pick) - self.style = style - self.interactor.SetInteractorStyle(style) - self.interactor.Render() + self.style = style + self.interactor.SetInteractorStyle(style) + self.interactor.Render() def QuitRubberBandZoom(self, evt, obj): style = vtk.vtkInteractorStyleImage() @@ -808,37 +825,6 @@ class Viewer(wx.Panel): self.slice_.apply_slice_buffer_to_mask(self.orientation) self._flush_buffer = False - def OnCrossMouseClick(self, evt, obj): - self.ChangeCrossPosition() - - def OnCrossMove(self, evt, obj): - # The user moved the mouse with left button pressed - if self.left_pressed: - self.ChangeCrossPosition() - - def ChangeCrossPosition(self): - mouse_x, mouse_y = self.interactor.GetEventPosition() - renderer = self.slice_data.renderer - self.pick.Pick(mouse_x, mouse_y, 0, renderer) - - # Get in what slice data the click occurred - # pick to get click position in the 3d world - coord_cross = self.get_coordinate_cursor() - position = self.slice_data.actor.GetInput().FindPoint(coord_cross) - # Forcing focal point to be setted in the center of the pixel. - coord_cross = self.slice_data.actor.GetInput().GetPoint(position) - - coord = self.calcultate_scroll_position(position) - self.ScrollSlice(coord) - - Publisher.sendMessage('Update cross position', coord_cross) - Publisher.sendMessage('Set ball reference position based on bound', - coord_cross) - Publisher.sendMessage('Set camera in volume', coord_cross) - Publisher.sendMessage('Render volume viewer') - - self.interactor.Render() - def Navigation(self, pubsub_evt): # Get point from base change x, y, z = pubsub_evt.data -- libgit2 0.21.2