Commit f7b5fc4d6a0a17b0359510217568644ca344dc5a

Authored by Thiago Franco de Moraes
1 parent 071dff96
Exists in interactor_style

Created a new interactor style to handle the cross tool

invesalius/data/styles.py
@@ -19,8 +19,145 @@ @@ -19,8 +19,145 @@
19 19
20 import vtk 20 import vtk
21 21
  22 +from wx.lib.pubsub import pub as Publisher
  23 +
22 import constants as const 24 import constants as const
23 25
  26 +class ZoomInteractorStyle(vtk.vtkInteractorStyleImage):
  27 + """
  28 + Interactor style responsible for zoom the camera.
  29 + """
  30 + def __init__(self):
  31 + self.right_pressed = False
  32 +
  33 + # Zoom using right button
  34 + self.AddObserver("RightButtonPressEvent",self.OnZoomRightClick)
  35 + self.AddObserver("MouseMoveEvent", self.OnZoomRightMove)
  36 + self.AddObserver("RightButtonReleaseEvent", self.OnZoomRightRelease)
  37 +
  38 + def OnZoomRightMove(self, evt, obj):
  39 + if (self.right_pressed):
  40 + evt.Dolly()
  41 + evt.OnRightButtonDown()
  42 +
  43 + def OnZoomRightClick(self, evt, obj):
  44 + self.right_pressed = 1
  45 + evt.StartDolly()
  46 +
  47 + def OnZoomRightRelease(self, evt, obj):
  48 + self.right_pressed = False
  49 +
  50 +class CrossInteractorStyle(ZoomInteractorStyle):
  51 + """
  52 + Interactor style responsible for the Cross.
  53 + """
  54 + def __init__(self, orientation, slice_data):
  55 + ZoomInteractorStyle.__init__(self)
  56 +
  57 + self.orientation = orientation
  58 + self.slice_actor = slice_data.actor
  59 + self.slice_data = slice_data
  60 +
  61 + self.left_pressed = False
  62 + self.picker = vtk.vtkWorldPointPicker()
  63 +
  64 + self.AddObserver("MouseMoveEvent", self.OnCrossMove)
  65 + self.AddObserver("LeftButtonPressEvent", self.OnCrossMouseClick)
  66 + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton)
  67 +
  68 + def OnCrossMouseClick(self, obj, evt):
  69 + self.left_pressed = True
  70 + iren = obj.GetInteractor()
  71 + self.ChangeCrossPosition(iren)
  72 +
  73 + def OnCrossMove(self, obj, evt):
  74 + # The user moved the mouse with left button pressed
  75 + if self.left_pressed:
  76 + print "OnCrossMove interactor style"
  77 + iren = obj.GetInteractor()
  78 + self.ChangeCrossPosition(iren)
  79 +
  80 + def OnReleaseLeftButton(self, obj, evt):
  81 + self.left_pressed = False
  82 +
  83 + def ChangeCrossPosition(self, iren):
  84 + mouse_x, mouse_y = iren.GetEventPosition()
  85 + ren = iren.GetRenderWindow().GetRenderers().GetFirstRenderer()
  86 + self.picker.Pick(mouse_x, mouse_y, 0, ren)
  87 +
  88 + # Get in what slice data the click occurred
  89 + # pick to get click position in the 3d world
  90 + coord_cross = self.get_coordinate_cursor()
  91 + position = self.slice_actor.GetInput().FindPoint(coord_cross)
  92 + # Forcing focal point to be setted in the center of the pixel.
  93 + coord_cross = self.slice_actor.GetInput().GetPoint(position)
  94 +
  95 + coord = self.calcultate_scroll_position(position)
  96 + self.ScrollSlice(coord)
  97 +
  98 + Publisher.sendMessage('Update cross position', coord_cross)
  99 + Publisher.sendMessage('Set ball reference position based on bound',
  100 + coord_cross)
  101 + Publisher.sendMessage('Set camera in volume', coord_cross)
  102 + Publisher.sendMessage('Render volume viewer')
  103 +
  104 + iren.Render()
  105 +
  106 + def get_coordinate_cursor(self):
  107 + # Find position
  108 + x, y, z = self.picker.GetPickPosition()
  109 + bounds = self.slice_actor.GetBounds()
  110 + if bounds[0] == bounds[1]:
  111 + x = bounds[0]
  112 + elif bounds[2] == bounds[3]:
  113 + y = bounds[2]
  114 + elif bounds[4] == bounds[5]:
  115 + z = bounds[4]
  116 + return x, y, z
  117 +
  118 + def calcultate_scroll_position(self, position):
  119 + # Based in the given coord (x, y, z), returns a list with the scroll positions for each
  120 + # orientation, being the first position the sagital, second the coronal
  121 + # and the last, axial.
  122 +
  123 + if self.orientation == 'AXIAL':
  124 + image_width = self.slice_actor.GetInput().GetDimensions()[0]
  125 + axial = self.slice_data.number
  126 + coronal = position / image_width
  127 + sagital = position % image_width
  128 +
  129 + elif self.orientation == 'CORONAL':
  130 + image_width = self.slice_actor.GetInput().GetDimensions()[0]
  131 + axial = position / image_width
  132 + coronal = self.slice_data.number
  133 + sagital = position % image_width
  134 +
  135 + elif self.orientation == 'SAGITAL':
  136 + image_width = self.slice_actor.GetInput().GetDimensions()[1]
  137 + axial = position / image_width
  138 + coronal = position % image_width
  139 + sagital = self.slice_data.number
  140 +
  141 + return sagital, coronal, axial
  142 +
  143 + def ScrollSlice(self, coord):
  144 + if self.orientation == "AXIAL":
  145 + Publisher.sendMessage(('Set scroll position', 'SAGITAL'),
  146 + coord[0])
  147 + Publisher.sendMessage(('Set scroll position', 'CORONAL'),
  148 + coord[1])
  149 + elif self.orientation == "SAGITAL":
  150 + Publisher.sendMessage(('Set scroll position', 'AXIAL'),
  151 + coord[2])
  152 + Publisher.sendMessage(('Set scroll position', 'CORONAL'),
  153 + coord[1])
  154 + elif self.orientation == "CORONAL":
  155 + Publisher.sendMessage(('Set scroll position', 'AXIAL'),
  156 + coord[2])
  157 + Publisher.sendMessage(('Set scroll position', 'SAGITAL'),
  158 + coord[0])
  159 +
  160 +
24 class ViewerStyle: 161 class ViewerStyle:
25 def __init__(self): 162 def __init__(self):
26 self.interactor = None 163 self.interactor = None
invesalius/data/viewer_slice.py
@@ -27,6 +27,8 @@ import numpy @@ -27,6 +27,8 @@ import numpy
27 import vtk 27 import vtk
28 from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor 28 from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor
29 29
  30 +import styles
  31 +
30 import wx 32 import wx
31 from wx.lib.pubsub import pub as Publisher 33 from wx.lib.pubsub import pub as Publisher
32 34
@@ -173,127 +175,142 @@ class Viewer(wx.Panel): @@ -173,127 +175,142 @@ class Viewer(wx.Panel):
173 interactor.SetInteractorStyle(style) 175 interactor.SetInteractorStyle(style)
174 176
175 def SetInteractorStyle(self, state): 177 def SetInteractorStyle(self, state):
176 - self.state = state  
177 - action = {const.SLICE_STATE_CROSS:  
178 - {  
179 - "MouseMoveEvent": self.OnCrossMove,  
180 - "LeftButtonPressEvent": self.OnCrossMouseClick,  
181 - },  
182 - const.SLICE_STATE_EDITOR:  
183 - {  
184 - "MouseMoveEvent": self.OnBrushMove,  
185 - "LeftButtonPressEvent": self.OnBrushClick,  
186 - "LeftButtonReleaseEvent": self.OnBrushRelease,  
187 - "EnterEvent": self.OnEnterInteractor,  
188 - "LeaveEvent": self.OnLeaveInteractor  
189 - },  
190 - const.STATE_PAN:  
191 - {  
192 - "MouseMoveEvent": self.OnPanMove,  
193 - "LeftButtonPressEvent": self.OnPanClick,  
194 - "LeftButtonReleaseEvent": self.OnVtkRightRelease  
195 - },  
196 - const.STATE_SPIN:  
197 - {  
198 - "MouseMoveEvent": self.OnSpinMove,  
199 - "LeftButtonPressEvent": self.OnSpinClick,  
200 - "LeftButtonReleaseEvent": self.OnVtkRightRelease  
201 - },  
202 - const.STATE_ZOOM:  
203 - {  
204 - "MouseMoveEvent": self.OnZoomMoveLeft,  
205 - "LeftButtonPressEvent": self.OnZoomLeftClick,  
206 - "LeftButtonReleaseEvent": self.OnVtkRightRelease  
207 - },  
208 - const.SLICE_STATE_SCROLL:  
209 - {  
210 - "MouseMoveEvent": self.OnChangeSliceMove,  
211 - "LeftButtonPressEvent": self.OnChangeSliceClick,  
212 - },  
213 - const.STATE_WL:  
214 - {  
215 - "MouseMoveEvent": self.OnWindowLevelMove,  
216 - "LeftButtonPressEvent": self.OnWindowLevelClick,  
217 - },  
218 - const.STATE_DEFAULT:  
219 - {  
220 - },  
221 - const.STATE_MEASURE_DISTANCE:  
222 - {  
223 - "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint  
224 - },  
225 - const.STATE_MEASURE_ANGLE:  
226 - {  
227 - "LeftButtonPressEvent": self.OnInsertAngularMeasurePoint  
228 - },  
229 - } 178 + if state == const.SLICE_STATE_CROSS:
  179 + style = styles.CrossInteractorStyle(self.orientation,
  180 + self.slice_data)
  181 + self.style = style
  182 + self.interactor.SetInteractorStyle(style)
  183 + self.interactor.Render()
230 184
  185 + ## Zoom using right button
  186 + #style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick)
  187 + #style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight)
  188 + #style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease)
  189 +
  190 + #Scroll change slice
  191 + style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward)
  192 + style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward)
231 193
232 - if state == const.SLICE_STATE_CROSS:  
233 self.__set_cross_visibility(1) 194 self.__set_cross_visibility(1)
234 Publisher.sendMessage('Activate ball reference') 195 Publisher.sendMessage('Activate ball reference')
235 else: 196 else:
236 - self.__set_cross_visibility(0)  
237 - Publisher.sendMessage('Deactivate ball reference') 197 + self.state = state
  198 + action = {
  199 + const.SLICE_STATE_EDITOR:
  200 + {
  201 + "MouseMoveEvent": self.OnBrushMove,
  202 + "LeftButtonPressEvent": self.OnBrushClick,
  203 + "LeftButtonReleaseEvent": self.OnBrushRelease,
  204 + "EnterEvent": self.OnEnterInteractor,
  205 + "LeaveEvent": self.OnLeaveInteractor
  206 + },
  207 + const.STATE_PAN:
  208 + {
  209 + "MouseMoveEvent": self.OnPanMove,
  210 + "LeftButtonPressEvent": self.OnPanClick,
  211 + "LeftButtonReleaseEvent": self.OnVtkRightRelease
  212 + },
  213 + const.STATE_SPIN:
  214 + {
  215 + "MouseMoveEvent": self.OnSpinMove,
  216 + "LeftButtonPressEvent": self.OnSpinClick,
  217 + "LeftButtonReleaseEvent": self.OnVtkRightRelease
  218 + },
  219 + const.STATE_ZOOM:
  220 + {
  221 + "MouseMoveEvent": self.OnZoomMoveLeft,
  222 + "LeftButtonPressEvent": self.OnZoomLeftClick,
  223 + "LeftButtonReleaseEvent": self.OnVtkRightRelease
  224 + },
  225 + const.SLICE_STATE_SCROLL:
  226 + {
  227 + "MouseMoveEvent": self.OnChangeSliceMove,
  228 + "LeftButtonPressEvent": self.OnChangeSliceClick,
  229 + },
  230 + const.STATE_WL:
  231 + {
  232 + "MouseMoveEvent": self.OnWindowLevelMove,
  233 + "LeftButtonPressEvent": self.OnWindowLevelClick,
  234 + },
  235 + const.STATE_DEFAULT:
  236 + {
  237 + },
  238 + const.STATE_MEASURE_DISTANCE:
  239 + {
  240 + "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint
  241 + },
  242 + const.STATE_MEASURE_ANGLE:
  243 + {
  244 + "LeftButtonPressEvent": self.OnInsertAngularMeasurePoint
  245 + },
  246 + }
  247 +
  248 +
  249 + if state == const.SLICE_STATE_CROSS:
  250 + self.__set_cross_visibility(1)
  251 + Publisher.sendMessage('Activate ball reference')
  252 + else:
  253 + self.__set_cross_visibility(0)
  254 + Publisher.sendMessage('Deactivate ball reference')
238 255
239 - if state == const.STATE_WL:  
240 - self.on_wl = True  
241 - self.wl_text.Show()  
242 - else:  
243 - self.on_wl = False  
244 - self.wl_text.Hide() 256 + if state == const.STATE_WL:
  257 + self.on_wl = True
  258 + self.wl_text.Show()
  259 + else:
  260 + self.on_wl = False
  261 + self.wl_text.Hide()
245 262
246 - self.__set_editor_cursor_visibility(0)  
247 -  
248 - # Bind method according to current mode  
249 - if(state == const.STATE_ZOOM_SL):  
250 - style = vtk.vtkInteractorStyleRubberBandZoom() 263 + self.__set_editor_cursor_visibility(0)
  264 +
  265 + # Bind method according to current mode
  266 + if(state == const.STATE_ZOOM_SL):
  267 + style = vtk.vtkInteractorStyleRubberBandZoom()
251 268
252 - style.AddObserver("RightButtonPressEvent", self.QuitRubberBandZoom)  
253 - #style.AddObserver("RightButtonPressEvent", self.EnterRubberBandZoom) 269 + style.AddObserver("RightButtonPressEvent", self.QuitRubberBandZoom)
  270 + #style.AddObserver("RightButtonPressEvent", self.EnterRubberBandZoom)
254 271
255 - else:  
256 - style = vtk.vtkInteractorStyleImage()  
257 -  
258 - # Check each event available for each state  
259 - for event in action[state]:  
260 - # Bind event  
261 - style.AddObserver(event,  
262 - action[state][event])  
263 -  
264 - # Common to all styles  
265 - # Mouse Buttons' presses / releases  
266 - style.AddObserver("LeftButtonPressEvent", self.OnLeftClick)  
267 - style.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton)  
268 - style.AddObserver("RightButtonPressEvent", self.OnRightClick)  
269 - style.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton)  
270 -  
271 - # Zoom using right button  
272 - style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick)  
273 - style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight)  
274 - style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease)  
275 -  
276 - #Scroll change slice  
277 - style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward)  
278 - style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward)  
279 -  
280 - if ((state == const.STATE_ZOOM) or (state == const.STATE_ZOOM_SL)):  
281 - self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom)  
282 - else:  
283 - self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnSpinPan) 272 + else:
  273 + style = vtk.vtkInteractorStyleImage()
  274 +
  275 + # Check each event available for each state
  276 + for event in action[state]:
  277 + # Bind event
  278 + style.AddObserver(event,
  279 + action[state][event])
  280 +
  281 + # Common to all styles
  282 + # Mouse Buttons' presses / releases
  283 + style.AddObserver("LeftButtonPressEvent", self.OnLeftClick)
  284 + style.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton)
  285 + style.AddObserver("RightButtonPressEvent", self.OnRightClick)
  286 + style.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton)
  287 +
  288 + # Zoom using right button
  289 + style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick)
  290 + style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight)
  291 + style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease)
  292 +
  293 + #Scroll change slice
  294 + style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward)
  295 + style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward)
  296 +
  297 + if ((state == const.STATE_ZOOM) or (state == const.STATE_ZOOM_SL)):
  298 + self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom)
  299 + else:
  300 + self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnSpinPan)
284 301
285 - # Measures are using vtkPropPicker because they need to get which actor  
286 - # was picked.  
287 - if state in (const.STATE_MEASURE_DISTANCE, const.STATE_MEASURE_ANGLE):  
288 - self.pick = vtk.vtkPropPicker()  
289 - self.interactor.SetPicker(self.pick)  
290 - else:  
291 - self.pick = vtk.vtkWorldPointPicker()  
292 - self.interactor.SetPicker(self.pick) 302 + # Measures are using vtkPropPicker because they need to get which actor
  303 + # was picked.
  304 + if state in (const.STATE_MEASURE_DISTANCE, const.STATE_MEASURE_ANGLE):
  305 + self.pick = vtk.vtkPropPicker()
  306 + self.interactor.SetPicker(self.pick)
  307 + else:
  308 + self.pick = vtk.vtkWorldPointPicker()
  309 + self.interactor.SetPicker(self.pick)
293 310
294 - self.style = style  
295 - self.interactor.SetInteractorStyle(style)  
296 - self.interactor.Render() 311 + self.style = style
  312 + self.interactor.SetInteractorStyle(style)
  313 + self.interactor.Render()
297 314
298 def QuitRubberBandZoom(self, evt, obj): 315 def QuitRubberBandZoom(self, evt, obj):
299 style = vtk.vtkInteractorStyleImage() 316 style = vtk.vtkInteractorStyleImage()
@@ -808,37 +825,6 @@ class Viewer(wx.Panel): @@ -808,37 +825,6 @@ class Viewer(wx.Panel):
808 self.slice_.apply_slice_buffer_to_mask(self.orientation) 825 self.slice_.apply_slice_buffer_to_mask(self.orientation)
809 self._flush_buffer = False 826 self._flush_buffer = False
810 827
811 - def OnCrossMouseClick(self, evt, obj):  
812 - self.ChangeCrossPosition()  
813 -  
814 - def OnCrossMove(self, evt, obj):  
815 - # The user moved the mouse with left button pressed  
816 - if self.left_pressed:  
817 - self.ChangeCrossPosition()  
818 -  
819 - def ChangeCrossPosition(self):  
820 - mouse_x, mouse_y = self.interactor.GetEventPosition()  
821 - renderer = self.slice_data.renderer  
822 - self.pick.Pick(mouse_x, mouse_y, 0, renderer)  
823 -  
824 - # Get in what slice data the click occurred  
825 - # pick to get click position in the 3d world  
826 - coord_cross = self.get_coordinate_cursor()  
827 - position = self.slice_data.actor.GetInput().FindPoint(coord_cross)  
828 - # Forcing focal point to be setted in the center of the pixel.  
829 - coord_cross = self.slice_data.actor.GetInput().GetPoint(position)  
830 -  
831 - coord = self.calcultate_scroll_position(position)  
832 - self.ScrollSlice(coord)  
833 -  
834 - Publisher.sendMessage('Update cross position', coord_cross)  
835 - Publisher.sendMessage('Set ball reference position based on bound',  
836 - coord_cross)  
837 - Publisher.sendMessage('Set camera in volume', coord_cross)  
838 - Publisher.sendMessage('Render volume viewer')  
839 -  
840 - self.interactor.Render()  
841 -  
842 def Navigation(self, pubsub_evt): 828 def Navigation(self, pubsub_evt):
843 # Get point from base change 829 # Get point from base change
844 x, y, z = pubsub_evt.data 830 x, y, z = pubsub_evt.data