Commit 333c4fd41e655127e2e6bc9ffd5b984809c2a112

Authored by tfmoraes
1 parent 46b08a73

ENH: Select a curve in clut raycasting widget to indicate in wich curve the wwwl…

… operation will be applied
invesalius/data/volume.py
... ... @@ -75,7 +75,7 @@ class Volume():
75 75 self.opacity_transfer_func = None
76 76 self.ww = None
77 77 self.wl = None
78   - self.n = 0
  78 + self.curve = 0
79 79 self.plane = None
80 80 self.plane_on = False
81 81  
... ... @@ -86,6 +86,8 @@ class Volume():
86 86 'Hide raycasting volume')
87 87 ps.Publisher().subscribe(self.OnUpdatePreset,
88 88 'Update raycasting preset')
  89 + ps.Publisher().subscribe(self.OnSetCurve,
  90 + 'Set raycasting curve')
89 91 ps.Publisher().subscribe(self.OnSetWindowLevel,
90 92 'Set raycasting wwwl')
91 93 ps.Publisher().subscribe(self.Refresh,
... ... @@ -157,6 +159,8 @@ class Volume():
157 159 ps.Publisher.sendMessage('Change volume viewer background colour', colour)
158 160 ps.Publisher.sendMessage('Change volume viewer gui colour', colour)
159 161  
  162 + def OnSetCurve(self, pubsub_evt):
  163 + self.curve = pubsub_evt.data
160 164  
161 165 def OnSetRelativeWindowLevel(self, pubsub_evt):
162 166 diff_ww, diff_wl = pubsub_evt.data
... ... @@ -170,13 +174,13 @@ class Volume():
170 174  
171 175 def OnSetWindowLevel(self, pubsub_evt):
172 176 ww, wl, n = pubsub_evt.data
173   - self.n = n
  177 + self.curve = n
174 178 self.SetWWWL(ww,wl)
175 179  
176 180 def SetWWWL(self, ww, wl):
177 181  
178 182 if self.config['advancedCLUT']:
179   - curve = self.config['16bitClutCurves'][self.n]
  183 + curve = self.config['16bitClutCurves'][self.curve]
180 184  
181 185 p1 = curve[0]
182 186 p2 = curve[-1]
... ...
invesalius/gui/default_viewers.py
... ... @@ -26,7 +26,7 @@ import data.viewer_volume as volume_viewer
26 26 import widgets.slice_menu as slice_menu_
27 27  
28 28 from gui.widgets.clut_raycasting import CLUTRaycastingWidget, \
29   - EVT_CLUT_POINT_CHANGED
  29 + EVT_CLUT_POINT_CHANGED, EVT_CLUT_CURVE_SELECTED
30 30  
31 31 class Panel(wx.Panel):
32 32 def __init__(self, parent):
... ... @@ -235,6 +235,7 @@ class VolumeInteraction(wx.Panel):
235 235  
236 236 def __bind_events_wx(self):
237 237 self.clut_raycasting.Bind(EVT_CLUT_POINT_CHANGED, self.OnPointChanged)
  238 + self.clut_raycasting.Bind(EVT_CLUT_CURVE_SELECTED , self.OnCurveSelected)
238 239 #self.Bind(wx.EVT_SIZE, self.OnSize)
239 240 #self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
240 241  
... ... @@ -242,6 +243,9 @@ class VolumeInteraction(wx.Panel):
242 243 ps.Publisher.sendMessage('Set raycasting refresh', None)
243 244 ps.Publisher().sendMessage('Render volume viewer', None)
244 245  
  246 + def OnCurveSelected(self, evt):
  247 + ps.Publisher.sendMessage('Set raycasting curve', evt.GetCurve())
  248 +
245 249 import wx.lib.platebtn as pbtn
246 250 import wx.lib.buttons as btn
247 251 import wx.lib.pubsub as ps
... ...
invesalius/gui/widgets/clut_raycasting.py
... ... @@ -20,6 +20,7 @@ HISTOGRAM_FILL_COLOUR = (0.25, 0.25, 0.25)
20 20 BACKGROUND_TEXT_COLOUR_RGBA = (1, 0, 0, 0.5)
21 21 GRADIENT_RGBA = 0.75
22 22 RADIUS = 5
  23 +SELECTION_SIZE = 10
23 24  
24 25 class Node(object):
25 26 """
... ... @@ -41,8 +42,13 @@ class Curve(object):
41 42 def __init__(self):
42 43 self.wl = 0
43 44 self.ww = 0
  45 + self.wl_px = 0
44 46 self.nodes = []
45 47  
  48 + def CalculateWWWl(self):
  49 + self.ww = self.nodes[-1].graylevel - self.nodes[0].graylevel
  50 + self.wl = self.nodes[0].graylevel + self.ww / 2.0
  51 +
46 52 class CLUTRaycastingWidget(wx.Panel):
47 53 """
48 54 This class represents the frame where images is showed
... ... @@ -65,6 +71,7 @@ class CLUTRaycastingWidget(wx.Panel):
65 71 self.to_draw_points = 0
66 72 self.histogram_pixel_points = [[0,0]]
67 73 self.histogram_array = [100,100]
  74 + self.previous_wl = 0
68 75 self.CalculatePixelPoints()
69 76 self.dragged = False
70 77 self.point_dragged = None
... ... @@ -104,18 +111,27 @@ class CLUTRaycastingWidget(wx.Panel):
104 111 pass
105 112  
106 113 def OnClick(self, evt):
107   - point = self._has_clicked_in_a_point(evt.GetPositionTuple())
  114 + x, y = evt.GetPositionTuple()
  115 + point = self._has_clicked_in_a_point((x, y))
108 116 # A point has been selected. It can be dragged.
109 117 if point:
110 118 self.dragged = True
111 119 self.point_dragged = point
112 120 self.Refresh()
113 121 return
  122 + curve = self._has_clicked_in_selection_curve((x, y))
  123 + if curve is not None:
  124 + print "Selecionou a curva", curve
  125 + self.dragged = True
  126 + self.previous_wl = x
  127 + self.curve_dragged = curve
  128 + evt = CLUTEvent(myEVT_CLUT_CURVE_SELECTED, self.GetId(), curve)
  129 + self.GetEventHandler().ProcessEvent(evt)
  130 + return
114 131 else:
115   - p = self._has_clicked_in_line(evt.GetPositionTuple())
  132 + p = self._has_clicked_in_line((x, y))
116 133 # The user clicked in the line. Insert a new point.
117 134 if p:
118   - x, y = evt.GetPositionTuple()
119 135 n, p = p
120 136 self.points[n].insert(p, {'x': 0, 'y': 0})
121 137 self.colours[n].insert(p, {'red': 0, 'green': 0, 'blue': 0})
... ... @@ -131,7 +147,7 @@ class CLUTRaycastingWidget(wx.Panel):
131 147 self.curves[n].nodes.insert(p, node)
132 148  
133 149 self.Refresh()
134   - nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId())
  150 + nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), n)
135 151 self.GetEventHandler().ProcessEvent(nevt)
136 152 return
137 153 evt.Skip()
... ... @@ -152,7 +168,7 @@ class CLUTRaycastingWidget(wx.Panel):
152 168 print self.curves[i].nodes
153 169 self.curves[i].nodes[j].colour = (r, g, b)
154 170 self.Refresh()
155   - nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId())
  171 + nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), i)
156 172 self.GetEventHandler().ProcessEvent(nevt)
157 173 return
158 174 evt.Skip()
... ... @@ -167,7 +183,7 @@ class CLUTRaycastingWidget(wx.Panel):
167 183 print "RightClick", i, j
168 184 self.RemovePoint(i, j)
169 185 self.Refresh()
170   - nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId())
  186 + nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), i)
171 187 self.GetEventHandler().ProcessEvent(nevt)
172 188 return
173 189 evt.Skip()
... ... @@ -178,10 +194,13 @@ class CLUTRaycastingWidget(wx.Panel):
178 194 been occurred in the preset points.
179 195 """
180 196 if self.to_render:
181   - evt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId())
  197 + evt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId(), 0)
182 198 self.GetEventHandler().ProcessEvent(evt)
183 199 self.dragged = False
  200 + self.curve_dragged = None
  201 + self.point_dragged = None
184 202 self.to_render = False
  203 + self.previous_wl = 0
185 204  
186 205 def OnWheel(self, evt):
187 206 """
... ... @@ -197,11 +216,11 @@ class CLUTRaycastingWidget(wx.Panel):
197 216  
198 217 def OnMotion(self, evt):
199 218 # User dragging a point
200   - if self.dragged:
  219 + x = evt.GetX()
  220 + y = evt.GetY()
  221 + if self.dragged and self.point_dragged:
201 222 self.to_render = True
202 223 i,j = self.point_dragged
203   - x = evt.GetX()
204   - y = evt.GetY()
205 224  
206 225 width, height= self.GetVirtualSizeTuple()
207 226  
... ... @@ -234,9 +253,25 @@ class CLUTRaycastingWidget(wx.Panel):
234 253 self.curves[i].nodes[j].y = y
235 254 self.curves[i].nodes[j].graylevel = graylevel
236 255 self.curves[i].nodes[j].opacity = opacity
  256 + for curve in self.curves:
  257 + curve.CalculateWWWl()
  258 + curve.wl_px = (self.HounsfieldToPixel(curve.wl),
  259 + self.OpacityToPixel(0))
237 260 self.Refresh()
238   - evt = CLUTEvent(myEVT_CLUT_POINT , self.GetId())
  261 + evt = CLUTEvent(myEVT_CLUT_POINT , self.GetId(), i)
239 262 self.GetEventHandler().ProcessEvent(evt)
  263 +
  264 + elif self.dragged and self.curve_dragged is not None:
  265 + curve = self.curves[self.curve_dragged]
  266 + curve.wl = self.PixelToHounsfield(x)
  267 + curve.wl_px = x, self.OpacityToPixel(0)
  268 + for node in curve.nodes:
  269 + node.x += (x - self.previous_wl)
  270 + node.graylevel = self.PixelToHounsfield(node.x)
  271 + ps.Publisher().sendMessage('Set raycasting wwwl',
  272 + (curve.ww, curve.wl, self.curve_dragged))
  273 + self.previous_wl = x
  274 + self.Refresh()
240 275 else:
241 276 evt.Skip()
242 277  
... ... @@ -257,10 +292,18 @@ class CLUTRaycastingWidget(wx.Panel):
257 292 """
258 293 for i, curve in enumerate(self.curves):
259 294 for j, node in enumerate(curve.nodes):
260   - if self._calculate_distance(node, position) <= RADIUS:
  295 + if self._calculate_distance((node.x, node.y), position) <= RADIUS:
261 296 return (i, j)
262 297 return None
263 298  
  299 + def _has_clicked_in_selection_curve(self, position):
  300 + x, y = position
  301 + for i, curve in enumerate(self.curves):
  302 + if self._calculate_distance(curve.wl_px, position) <= RADIUS:
  303 + return i
  304 + return None
  305 +
  306 +
264 307 def _has_clicked_in_line(self, position):
265 308 """
266 309 Verify if was clicked in a line. If yes, it returns the insertion
... ... @@ -278,7 +321,7 @@ class CLUTRaycastingWidget(wx.Panel):
278 321 return None
279 322  
280 323 def _calculate_distance(self, p1, p2):
281   - return ((p1.x-p2[0])**2 + (p1.y-p2[1])**2) ** 0.5
  324 + return ((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) ** 0.5
282 325  
283 326 def RemovePoint(self, i, j):
284 327 """
... ... @@ -413,12 +456,13 @@ class CLUTRaycastingWidget(wx.Panel):
413 456  
414 457 def _draw_selection_curve(self, ctx, height):
415 458 for curve in self.curves:
416   - x_center = (curve.nodes[0].x + curve.nodes[-1].x)/2.0
417   - ctx.set_source_rgb(*LINE_COLOUR)
418   - ctx.stroke()
419   - ctx.rectangle(x_center-5, height, 10, 10)
  459 + x_center, y_center = curve.wl_px
  460 + ctx.rectangle(x_center-SELECTION_SIZE/2.0, y_center, SELECTION_SIZE,
  461 + SELECTION_SIZE)
420 462 ctx.set_source_rgb(0,0,0)
421 463 ctx.fill_preserve()
  464 + ctx.set_source_rgb(*LINE_COLOUR)
  465 + ctx.stroke()
422 466  
423 467 def Render(self, dc):
424 468 ctx = wx.lib.wxcairo.ContextFromDC(dc)
... ... @@ -488,6 +532,9 @@ class CLUTRaycastingWidget(wx.Panel):
488 532 node.opacity = point['y']
489 533 node.colour = colour['red'], colour['green'], colour['blue']
490 534 curve.nodes.append(node)
  535 + curve.CalculateWWWl()
  536 + curve.wl_px = (self.HounsfieldToPixel(curve.wl),
  537 + self.OpacityToPixel(0))
491 538 self.curves.append(curve)
492 539 self._build_histogram()
493 540  
... ... @@ -551,8 +598,11 @@ class CLUTRaycastingWidget(wx.Panel):
551 598 self.histogram_array = h_array
552 599  
553 600 class CLUTEvent(wx.PyCommandEvent):
554   - def __init__(self , evtType, id):
  601 + def __init__(self , evtType, id, curve):
555 602 wx.PyCommandEvent.__init__(self, evtType, id)
  603 + self.curve = curve
  604 + def GetCurve(self):
  605 + return self.curve
556 606  
557 607  
558 608 # Occurs when CLUT is sliding
... ... @@ -570,3 +620,7 @@ EVT_CLUT_POINT = wx.PyEventBinder(myEVT_CLUT_POINT, 1)
570 620 # Occurs when a CLUT point was changed
571 621 myEVT_CLUT_POINT_CHANGED = wx.NewEventType()
572 622 EVT_CLUT_POINT_CHANGED = wx.PyEventBinder(myEVT_CLUT_POINT_CHANGED, 1)
  623 +
  624 +# Selected a curve
  625 +myEVT_CLUT_CURVE_SELECTED = wx.NewEventType()
  626 +EVT_CLUT_CURVE_SELECTED = wx.PyEventBinder(myEVT_CLUT_CURVE_SELECTED, 1)
... ...