Commit d7c4240ebed155ff41ce8a4a790f8a6c0e37b375
1 parent
15b8a469
Exists in
master
and in
68 other branches
STYLE: Reestructured clut raycasting widget class. Created a class to represents…
… the curves and class to represent the points from preset
Showing
1 changed file
with
159 additions
and
103 deletions
Show diff stats
invesalius/gui/widgets/clut_raycasting.py
@@ -21,6 +21,28 @@ BACKGROUND_TEXT_COLOUR_RGBA = (1, 0, 0, 0.5) | @@ -21,6 +21,28 @@ BACKGROUND_TEXT_COLOUR_RGBA = (1, 0, 0, 0.5) | ||
21 | GRADIENT_RGBA = 0.75 | 21 | GRADIENT_RGBA = 0.75 |
22 | RADIUS = 5 | 22 | RADIUS = 5 |
23 | 23 | ||
24 | +class Node(object): | ||
25 | + """ | ||
26 | + Represents the points in the raycasting preset. Contains its colour, | ||
27 | + graylevel (hounsfield scale), opacity, x and y position in the widget. | ||
28 | + """ | ||
29 | + def __init__(self): | ||
30 | + self.colour = None | ||
31 | + self.x = 0 | ||
32 | + self.y = 0 | ||
33 | + self.graylevel = 0 | ||
34 | + self.opacity = 0 | ||
35 | + | ||
36 | +class Curve(object): | ||
37 | + """ | ||
38 | + Represents the curves in the raycasting preset. It contains the point nodes from | ||
39 | + the curve and its window width & level. | ||
40 | + """ | ||
41 | + def __init__(self): | ||
42 | + self.wl = 0 | ||
43 | + self.ww = 0 | ||
44 | + self.nodes = [] | ||
45 | + | ||
24 | class CLUTRaycastingWidget(wx.Panel): | 46 | class CLUTRaycastingWidget(wx.Panel): |
25 | """ | 47 | """ |
26 | This class represents the frame where images is showed | 48 | This class represents the frame where images is showed |
@@ -35,6 +57,7 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -35,6 +57,7 @@ class CLUTRaycastingWidget(wx.Panel): | ||
35 | super(CLUTRaycastingWidget, self).__init__(parent, id) | 57 | super(CLUTRaycastingWidget, self).__init__(parent, id) |
36 | self.points = [] | 58 | self.points = [] |
37 | self.colours = [] | 59 | self.colours = [] |
60 | + self.curves = [] | ||
38 | self.init = -1024 | 61 | self.init = -1024 |
39 | self.end = 2000 | 62 | self.end = 2000 |
40 | self.padding = 5 | 63 | self.padding = 5 |
@@ -42,7 +65,7 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -42,7 +65,7 @@ class CLUTRaycastingWidget(wx.Panel): | ||
42 | self.to_draw_points = 0 | 65 | self.to_draw_points = 0 |
43 | self.histogram_pixel_points = [[0,0]] | 66 | self.histogram_pixel_points = [[0,0]] |
44 | self.histogram_array = [100,100] | 67 | self.histogram_array = [100,100] |
45 | - self.CreatePixelArray() | 68 | + self.CalculatePixelPoints() |
46 | self.dragged = False | 69 | self.dragged = False |
47 | self.point_dragged = None | 70 | self.point_dragged = None |
48 | self.__bind_events_wx() | 71 | self.__bind_events_wx() |
@@ -55,7 +78,7 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -55,7 +78,7 @@ class CLUTRaycastingWidget(wx.Panel): | ||
55 | """ | 78 | """ |
56 | self.init, self.end = range | 79 | self.init, self.end = range |
57 | print "Range", range | 80 | print "Range", range |
58 | - self.CreatePixelArray() | 81 | + self.CalculatePixelPoints() |
59 | 82 | ||
60 | def SetPadding(self, padding): | 83 | def SetPadding(self, padding): |
61 | self.padding = padding | 84 | self.padding = padding |
@@ -92,11 +115,21 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -92,11 +115,21 @@ class CLUTRaycastingWidget(wx.Panel): | ||
92 | p = self._has_clicked_in_line(evt.GetPositionTuple()) | 115 | p = self._has_clicked_in_line(evt.GetPositionTuple()) |
93 | # The user clicked in the line. Insert a new point. | 116 | # The user clicked in the line. Insert a new point. |
94 | if p: | 117 | if p: |
118 | + x, y = evt.GetPositionTuple() | ||
95 | n, p = p | 119 | n, p = p |
96 | self.points[n].insert(p, {'x': 0, 'y': 0}) | 120 | self.points[n].insert(p, {'x': 0, 'y': 0}) |
97 | - self.pixels_points[n].insert(p, list(evt.GetPositionTuple())) | ||
98 | self.colours[n].insert(p, {'red': 0, 'green': 0, 'blue': 0}) | 121 | self.colours[n].insert(p, {'red': 0, 'green': 0, 'blue': 0}) |
99 | - self.PixelToHounsfield(n, p) | 122 | + self.points[n][p]['x'] = self.PixelToHounsfield(x) |
123 | + self.points[n][p]['y'] = self.PixelToOpacity(y) | ||
124 | + | ||
125 | + node = Node() | ||
126 | + node.colour = (0, 0, 0) | ||
127 | + node.x = x | ||
128 | + node.y = y | ||
129 | + node.graylevel = self.points[n][p]['x'] | ||
130 | + node.opacity = self.points[n][p]['y'] | ||
131 | + self.curves[n].nodes.insert(p, node) | ||
132 | + | ||
100 | self.Refresh() | 133 | self.Refresh() |
101 | nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) | 134 | nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) |
102 | self.GetEventHandler().ProcessEvent(nevt) | 135 | self.GetEventHandler().ProcessEvent(nevt) |
@@ -116,6 +149,8 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -116,6 +149,8 @@ class CLUTRaycastingWidget(wx.Panel): | ||
116 | self.colours[i][j]['red'] = r | 149 | self.colours[i][j]['red'] = r |
117 | self.colours[i][j]['green'] = g | 150 | self.colours[i][j]['green'] = g |
118 | self.colours[i][j]['blue'] = b | 151 | self.colours[i][j]['blue'] = b |
152 | + print self.curves[i].nodes | ||
153 | + self.curves[i].nodes[j].colour = (r, g, b) | ||
119 | self.Refresh() | 154 | self.Refresh() |
120 | nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) | 155 | nevt = CLUTEvent(myEVT_CLUT_POINT_CHANGED, self.GetId()) |
121 | self.GetEventHandler().ProcessEvent(nevt) | 156 | self.GetEventHandler().ProcessEvent(nevt) |
@@ -183,17 +218,22 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -183,17 +218,22 @@ class CLUTRaycastingWidget(wx.Panel): | ||
183 | x = width | 218 | x = width |
184 | 219 | ||
185 | # A point must be greater than the previous one, but the first one | 220 | # A point must be greater than the previous one, but the first one |
186 | - if j > 0 and x <= self.pixels_points[i][j-1][0]: | ||
187 | - x = self.pixels_points[i][j-1][0] + 1 | 221 | + if j > 0 and x <= self.curves[i].nodes[j-1].x: |
222 | + x = self.curves[i].nodes[j-1].x + 1 | ||
188 | 223 | ||
189 | # A point must be lower than the previous one, but the last one | 224 | # A point must be lower than the previous one, but the last one |
190 | - if j < len(self.pixels_points[i]) -1 \ | ||
191 | - and x >= self.pixels_points[i][j+1][0]: | ||
192 | - x = self.pixels_points[i][j+1][0] - 1 | ||
193 | - | ||
194 | - self.pixels_points[i][j][0] = x | ||
195 | - self.pixels_points[i][j][1] = y | ||
196 | - self.PixelToHounsfield(i,j) | 225 | + if j < len(self.curves[i].nodes) -1 \ |
226 | + and x >= self.curves[i].nodes[j+1].x: | ||
227 | + x = self.curves[i].nodes[j+1].x - 1 | ||
228 | + | ||
229 | + graylevel = self.PixelToHounsfield(x) | ||
230 | + opacity = self.PixelToOpacity(y) | ||
231 | + self.points[i][j]['x'] = graylevel | ||
232 | + self.points[i][j]['y'] = opacity | ||
233 | + self.curves[i].nodes[j].x = x | ||
234 | + self.curves[i].nodes[j].y = y | ||
235 | + self.curves[i].nodes[j].graylevel = graylevel | ||
236 | + self.curves[i].nodes[j].opacity = opacity | ||
197 | self.Refresh() | 237 | self.Refresh() |
198 | evt = CLUTEvent(myEVT_CLUT_POINT , self.GetId()) | 238 | evt = CLUTEvent(myEVT_CLUT_POINT , self.GetId()) |
199 | self.GetEventHandler().ProcessEvent(evt) | 239 | self.GetEventHandler().ProcessEvent(evt) |
@@ -208,38 +248,16 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -208,38 +248,16 @@ class CLUTRaycastingWidget(wx.Panel): | ||
208 | self.Render(dc) | 248 | self.Render(dc) |
209 | 249 | ||
210 | def OnSize(self, evt): | 250 | def OnSize(self, evt): |
211 | - self.CreatePixelArray() | 251 | + self.CalculatePixelPoints() |
212 | self.Refresh() | 252 | self.Refresh() |
213 | 253 | ||
214 | - def _draw_gradient(self, ctx, height): | ||
215 | - #The gradient | ||
216 | - height += self.padding | ||
217 | - for i, curve in enumerate(self.pixels_points): | ||
218 | - x, y = curve[0] | ||
219 | - xini, yini = curve[0] | ||
220 | - xend, yend = curve[-1] | ||
221 | - gradient = cairo.LinearGradient(xini, height, xend, height) | ||
222 | - ctx.move_to(x, y) | ||
223 | - for j, point in enumerate(curve): | ||
224 | - x, y = point | ||
225 | - ctx.line_to(x, y) | ||
226 | - r = self.colours[i][j]['red'] | ||
227 | - g = self.colours[i][j]['green'] | ||
228 | - b = self.colours[i][j]['blue'] | ||
229 | - gradient.add_color_stop_rgba((x - xini) * 1.0 / (xend - xini), | ||
230 | - r, g, b, GRADIENT_RGBA) | ||
231 | - ctx.line_to(x, height) | ||
232 | - ctx.line_to(xini, height) | ||
233 | - ctx.close_path() | ||
234 | - ctx.set_source(gradient) | ||
235 | - ctx.fill() | ||
236 | def _has_clicked_in_a_point(self, position): | 254 | def _has_clicked_in_a_point(self, position): |
237 | """ | 255 | """ |
238 | returns the index from the selected point | 256 | returns the index from the selected point |
239 | """ | 257 | """ |
240 | - for i,curve in enumerate(self.pixels_points): | ||
241 | - for j,point in enumerate(curve): | ||
242 | - if self._calculate_distance(point, position) <= RADIUS: | 258 | + for i, curve in enumerate(self.curves): |
259 | + for j, node in enumerate(curve.nodes): | ||
260 | + if self._calculate_distance(node, position) <= RADIUS: | ||
243 | return (i, j) | 261 | return (i, j) |
244 | return None | 262 | return None |
245 | 263 | ||
@@ -248,27 +266,28 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -248,27 +266,28 @@ class CLUTRaycastingWidget(wx.Panel): | ||
248 | Verify if was clicked in a line. If yes, it returns the insertion | 266 | Verify if was clicked in a line. If yes, it returns the insertion |
249 | position in the point list. | 267 | position in the point list. |
250 | """ | 268 | """ |
251 | - for n, point in enumerate(self.pixels_points): | ||
252 | - p = bisect.bisect([i[0] for i in point], position[0]) | 269 | + for n, curve in enumerate(self.curves): |
270 | + p = bisect.bisect([node.x for node in curve.nodes], position[0]) | ||
253 | print p | 271 | print p |
254 | - if p != 0 and p != len(point): | ||
255 | - x1, y1 = point[p-1] | 272 | + if p != 0 and p != len(curve.nodes): |
273 | + x1, y1 = curve.nodes[p-1].x, curve.nodes[p-1].y | ||
256 | x2, y2 = position | 274 | x2, y2 = position |
257 | - x3, y3 = point[p] | 275 | + x3, y3 = curve.nodes[p].x, curve.nodes[p].y |
258 | if int(float(x2 - x1) / (x3 - x2)) - int(float(y2 - y1) / (y3 - y2)) == 0: | 276 | if int(float(x2 - x1) / (x3 - x2)) - int(float(y2 - y1) / (y3 - y2)) == 0: |
259 | return (n, p) | 277 | return (n, p) |
260 | return None | 278 | return None |
261 | 279 | ||
262 | def _calculate_distance(self, p1, p2): | 280 | def _calculate_distance(self, p1, p2): |
263 | - return ((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) ** 0.5 | 281 | + return ((p1.x-p2[0])**2 + (p1.y-p2[1])**2) ** 0.5 |
264 | 282 | ||
265 | def RemovePoint(self, i, j): | 283 | def RemovePoint(self, i, j): |
266 | """ | 284 | """ |
267 | The point the point in the given i,j index | 285 | The point the point in the given i,j index |
268 | """ | 286 | """ |
269 | - self.pixels_points[i].pop(j) | ||
270 | self.points[i].pop(j) | 287 | self.points[i].pop(j) |
271 | self.colours[i].pop(j) | 288 | self.colours[i].pop(j) |
289 | + | ||
290 | + self.curves[i].nodes.pop(j) | ||
272 | # If the point to removed is that was selected before and have a | 291 | # If the point to removed is that was selected before and have a |
273 | # textbox, then remove the point and the textbox | 292 | # textbox, then remove the point and the textbox |
274 | if (i, j) == self.point_dragged: | 293 | if (i, j) == self.point_dragged: |
@@ -283,43 +302,58 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -283,43 +302,58 @@ class CLUTRaycastingWidget(wx.Panel): | ||
283 | # Can't have only one point in the curve | 302 | # Can't have only one point in the curve |
284 | if len(self.points[i]) == 1: | 303 | if len(self.points[i]) == 1: |
285 | self.points.pop(i) | 304 | self.points.pop(i) |
286 | - self.pixels_points.pop(i) | ||
287 | self.colours.pop(i) | 305 | self.colours.pop(i) |
288 | self.point_dragged = None | 306 | self.point_dragged = None |
289 | 307 | ||
290 | - def _draw_curves(self, ctx): | ||
291 | - #Drawing the lines | ||
292 | - for curve in self.pixels_points: | ||
293 | - x,y = curve[0] | ||
294 | - ctx.move_to(x, y) | ||
295 | - for point in curve: | ||
296 | - x,y = point | 308 | + self.curves.pop(i) |
309 | + | ||
310 | + def _draw_gradient(self, ctx, height): | ||
311 | + #The gradient | ||
312 | + height += self.padding | ||
313 | + for curve in self.curves: | ||
314 | + first_node = curve.nodes[0] | ||
315 | + last_node = curve.nodes[-1] | ||
316 | + xini, yini = first_node.x, first_node.y | ||
317 | + xend, yend = last_node.x, last_node.y | ||
318 | + gradient = cairo.LinearGradient(xini, height, xend, height) | ||
319 | + ctx.move_to(xini, yini) | ||
320 | + for node in curve.nodes: | ||
321 | + x, y = node.x, node.y | ||
322 | + r, g, b = node.colour | ||
297 | ctx.line_to(x, y) | 323 | ctx.line_to(x, y) |
324 | + gradient.add_color_stop_rgba((x - xini) * 1.0 / (xend - xini), | ||
325 | + r, g, b, GRADIENT_RGBA) | ||
326 | + ctx.line_to(x, height) | ||
327 | + ctx.line_to(xini, height) | ||
328 | + ctx.close_path() | ||
329 | + ctx.set_source(gradient) | ||
330 | + ctx.fill() | ||
331 | + | ||
332 | + def _draw_curves(self, ctx): | ||
333 | + for curve in self.curves: | ||
334 | + ctx.move_to(curve.nodes[0].x, curve.nodes[0].y) | ||
335 | + for node in curve.nodes: | ||
336 | + ctx.line_to(node.x, node.y) | ||
298 | ctx.set_source_rgb(*LINE_COLOUR) | 337 | ctx.set_source_rgb(*LINE_COLOUR) |
299 | ctx.stroke() | 338 | ctx.stroke() |
300 | 339 | ||
301 | def _draw_points(self, ctx): | 340 | def _draw_points(self, ctx): |
302 | - #Drawing the circles that represents the points | ||
303 | - for i, curve in enumerate(self.pixels_points): | ||
304 | - for j, point in enumerate(curve): | ||
305 | - x,y = point | ||
306 | - r = self.colours[i][j]['red'] | ||
307 | - g = self.colours[i][j]['green'] | ||
308 | - b = self.colours[i][j]['blue'] | ||
309 | - ctx.arc(x, y, RADIUS, 0, math.pi * 2) | ||
310 | - ctx.set_source_rgb(r, g, b) | 341 | + for curve in self.curves: |
342 | + for node in curve.nodes: | ||
343 | + ctx.arc(node.x, node.y, RADIUS, 0, math.pi * 2) | ||
344 | + ctx.set_source_rgb(*node.colour) | ||
311 | ctx.fill_preserve() | 345 | ctx.fill_preserve() |
312 | ctx.set_source_rgb(*LINE_COLOUR) | 346 | ctx.set_source_rgb(*LINE_COLOUR) |
313 | ctx.stroke() | 347 | ctx.stroke() |
314 | - #ctx.move_to(x, y) | ||
315 | 348 | ||
316 | def _draw_selected_point_text(self, ctx): | 349 | def _draw_selected_point_text(self, ctx): |
317 | ctx.select_font_face('Sans') | 350 | ctx.select_font_face('Sans') |
318 | ctx.set_font_size(15) | 351 | ctx.set_font_size(15) |
319 | i,j = self.point_dragged | 352 | i,j = self.point_dragged |
320 | - x,y = self.pixels_points[i][j] | ||
321 | - value = self.points[i][j]['x'] | ||
322 | - alpha = self.points[i][j]['y'] | 353 | + node = self.curves[i].nodes[j] |
354 | + x,y = node.x, node.y | ||
355 | + value = node.graylevel | ||
356 | + alpha = node.opacity | ||
323 | x_bearing, y_bearing, width, height, x_advance, y_advance\ | 357 | x_bearing, y_bearing, width, height, x_advance, y_advance\ |
324 | = ctx.text_extents("Value %6d" % value) | 358 | = ctx.text_extents("Value %6d" % value) |
325 | 359 | ||
@@ -329,15 +363,15 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -329,15 +363,15 @@ class CLUTRaycastingWidget(wx.Panel): | ||
329 | y_superior = y - RADIUS * 2 - 2 + y_bearing * 2 | 363 | y_superior = y - RADIUS * 2 - 2 + y_bearing * 2 |
330 | y_inferior = fheight * 2 | 364 | y_inferior = fheight * 2 |
331 | 365 | ||
332 | - # The bottom position of the text box mustn't be upper thant the top of | 366 | + # The bottom position of the text box mustn't be upper than the top of |
333 | # the width to always appears in the widget | 367 | # the width to always appears in the widget |
334 | if y_superior <= self.padding: | 368 | if y_superior <= self.padding: |
335 | y_superior = y | 369 | y_superior = y |
336 | y_text1 = y + height | 370 | y_text1 = y + height |
337 | y_text2 = y_text1 + 1 + fheight | 371 | y_text2 = y_text1 + 1 + fheight |
338 | else: | 372 | else: |
339 | - y_text1 = y - RADIUS - 1 | ||
340 | - y_text2 = y_text1 - 1 - fheight | 373 | + y_text2 = y - RADIUS - 1 |
374 | + y_text1 = y_text2 - 1 - fheight | ||
341 | 375 | ||
342 | x_left = x + RADIUS + 1 | 376 | x_left = x + RADIUS + 1 |
343 | rectangle_width = width + RADIUS + 1 | 377 | rectangle_width = width + RADIUS + 1 |
@@ -377,13 +411,12 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -377,13 +411,12 @@ class CLUTRaycastingWidget(wx.Panel): | ||
377 | ctx.set_source_rgb(*HISTOGRAM_LINE_COLOUR) | 411 | ctx.set_source_rgb(*HISTOGRAM_LINE_COLOUR) |
378 | ctx.stroke() | 412 | ctx.stroke() |
379 | 413 | ||
380 | - def _draw_selection_curve(self, ctx, width): | ||
381 | - for curve in self.pixels_points: | ||
382 | - x_center = (curve[0][0] + curve[-1][0])/2.0 | ||
383 | - print "x_center", curve[0][0], curve[-1][0], x_center | 414 | + def _draw_selection_curve(self, ctx, height): |
415 | + for curve in self.curves: | ||
416 | + x_center = (curve.nodes[0].x + curve.nodes[-1].x)/2.0 | ||
384 | ctx.set_source_rgb(*LINE_COLOUR) | 417 | ctx.set_source_rgb(*LINE_COLOUR) |
385 | ctx.stroke() | 418 | ctx.stroke() |
386 | - ctx.rectangle(x_center-5, width-5, 10, 10) | 419 | + ctx.rectangle(x_center-5, height, 10, 10) |
387 | ctx.set_source_rgb(0,0,0) | 420 | ctx.set_source_rgb(0,0,0) |
388 | ctx.fill_preserve() | 421 | ctx.fill_preserve() |
389 | 422 | ||
@@ -398,11 +431,10 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -398,11 +431,10 @@ class CLUTRaycastingWidget(wx.Panel): | ||
398 | self._draw_gradient(ctx, height) | 431 | self._draw_gradient(ctx, height) |
399 | self._draw_curves(ctx) | 432 | self._draw_curves(ctx) |
400 | self._draw_points(ctx) | 433 | self._draw_points(ctx) |
401 | - self._draw_selection_curve(ctx, width) | 434 | + self._draw_selection_curve(ctx, height) |
402 | if sys.platform != "darwin": | 435 | if sys.platform != "darwin": |
403 | if self.point_dragged: | 436 | if self.point_dragged: |
404 | self._draw_selected_point_text(ctx) | 437 | self._draw_selected_point_text(ctx) |
405 | - | ||
406 | 438 | ||
407 | def _build_histogram(self): | 439 | def _build_histogram(self): |
408 | width, height= self.GetVirtualSizeTuple() | 440 | width, height= self.GetVirtualSizeTuple() |
@@ -425,17 +457,6 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -425,17 +457,6 @@ class CLUTRaycastingWidget(wx.Panel): | ||
425 | y = height - y * proportion_y | 457 | y = height - y * proportion_y |
426 | self.histogram_pixel_points.append((x, y)) | 458 | self.histogram_pixel_points.append((x, y)) |
427 | 459 | ||
428 | - def CreatePixelArray(self): | ||
429 | - """ | ||
430 | - Create a list with points (in pixel x, y coordinate) to draw based in | ||
431 | - the preset points (Hounsfield scale, opacity). | ||
432 | - """ | ||
433 | - self.pixels_points = [] | ||
434 | - self.__sort_pixel_points() | ||
435 | - for curve in self.points: | ||
436 | - self.pixels_points.append([self.HounsfieldToPixel(i) for i in curve]) | ||
437 | - self._build_histogram() | ||
438 | - | ||
439 | def __sort_pixel_points(self): | 460 | def __sort_pixel_points(self): |
440 | """ | 461 | """ |
441 | Sort the pixel points (colours and points) maintaining the reference | 462 | Sort the pixel points (colours and points) maintaining the reference |
@@ -448,30 +469,65 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -448,30 +469,65 @@ class CLUTRaycastingWidget(wx.Panel): | ||
448 | self.points[n] = [i[0] for i in point_colour] | 469 | self.points[n] = [i[0] for i in point_colour] |
449 | self.colours[n] = [i[1] for i in point_colour] | 470 | self.colours[n] = [i[1] for i in point_colour] |
450 | 471 | ||
451 | - def HounsfieldToPixel(self, h_pt): | 472 | + def CalculatePixelPoints(self): |
452 | """ | 473 | """ |
453 | - Given a Hounsfield point(graylevel, opacity), returns a pixel point in the canvas. | 474 | + Create a list with points (in pixel x, y coordinate) to draw based in |
475 | + the preset points (Hounsfield scale, opacity). | ||
476 | + """ | ||
477 | + self.curves = [] | ||
478 | + self.__sort_pixel_points() | ||
479 | + for points, colours in zip(self.points, self.colours): | ||
480 | + curve = Curve() | ||
481 | + for point, colour in zip(points, colours): | ||
482 | + x = self.HounsfieldToPixel(point['x']) | ||
483 | + y = self.OpacityToPixel(point['y']) | ||
484 | + node = Node() | ||
485 | + node.x = x | ||
486 | + node.y = y | ||
487 | + node.graylevel = point['x'] | ||
488 | + node.opacity = point['y'] | ||
489 | + node.colour = colour['red'], colour['green'], colour['blue'] | ||
490 | + curve.nodes.append(node) | ||
491 | + self.curves.append(curve) | ||
492 | + self._build_histogram() | ||
493 | + | ||
494 | + def HounsfieldToPixel(self, graylevel): | ||
495 | + """ | ||
496 | + Given a Hounsfield point returns a pixel point in the canvas. | ||
454 | """ | 497 | """ |
455 | width,height = self.GetVirtualSizeTuple() | 498 | width,height = self.GetVirtualSizeTuple() |
456 | width -= self.padding | 499 | width -= self.padding |
457 | - height -= (self.padding * 2) | ||
458 | proportion = width * 1.0 / (self.end - self.init) | 500 | proportion = width * 1.0 / (self.end - self.init) |
459 | - x = (h_pt['x'] - self.init) * proportion | ||
460 | - y = height - (h_pt['y'] * height) + self.padding | ||
461 | - return [x,y] | 501 | + x = (graylevel - self.init) * proportion |
502 | + return x | ||
462 | 503 | ||
463 | - def PixelToHounsfield(self, i, j): | 504 | + def OpacityToPixel(self, opacity): |
464 | """ | 505 | """ |
465 | - Given a Hounsfield point(graylevel, opacity), returns a pixel point in the canvas. | 506 | + Given a Opacity point returns a pixel point in the canvas. |
507 | + """ | ||
508 | + width,height = self.GetVirtualSizeTuple() | ||
509 | + height -= (self.padding * 2) | ||
510 | + y = height - (opacity * height) + self.padding | ||
511 | + return y | ||
512 | + | ||
513 | + def PixelToHounsfield(self, x): | ||
514 | + """ | ||
515 | + Translate from pixel point to Hounsfield scale. | ||
466 | """ | 516 | """ |
467 | width, height= self.GetVirtualSizeTuple() | 517 | width, height= self.GetVirtualSizeTuple() |
468 | width -= self.padding | 518 | width -= self.padding |
469 | - height -= (self.padding * 2) | ||
470 | proportion = width * 1.0 / (self.end - self.init) | 519 | proportion = width * 1.0 / (self.end - self.init) |
471 | - x = self.pixels_points[i][j][0] / proportion - abs(self.init) | ||
472 | - y = (height - self.pixels_points[i][j][1] + self.padding) * 1.0 / height | ||
473 | - self.points[i][j]['x'] = x | ||
474 | - self.points[i][j]['y'] = y | 520 | + graylevel = x / proportion - abs(self.init) |
521 | + return graylevel | ||
522 | + | ||
523 | + def PixelToOpacity(self, y): | ||
524 | + """ | ||
525 | + Translate from pixel point to opacity. | ||
526 | + """ | ||
527 | + width, height= self.GetVirtualSizeTuple() | ||
528 | + height -= (self.padding * 2) | ||
529 | + opacity = (height - y + self.padding) * 1.0 / height | ||
530 | + return opacity | ||
475 | 531 | ||
476 | def SetRaycastPreset(self, preset): | 532 | def SetRaycastPreset(self, preset): |
477 | preset = project.Project().raycasting_preset | 533 | preset = project.Project().raycasting_preset |
@@ -482,13 +538,13 @@ class CLUTRaycastingWidget(wx.Panel): | @@ -482,13 +538,13 @@ class CLUTRaycastingWidget(wx.Panel): | ||
482 | self.to_draw_points = 1 | 538 | self.to_draw_points = 1 |
483 | self.points = preset['16bitClutCurves'] | 539 | self.points = preset['16bitClutCurves'] |
484 | self.colours = preset['16bitClutColors'] | 540 | self.colours = preset['16bitClutColors'] |
485 | - self.CreatePixelArray() | 541 | + self.CalculatePixelPoints() |
486 | else: | 542 | else: |
487 | self.to_draw_points = 0 | 543 | self.to_draw_points = 0 |
488 | self.Refresh() | 544 | self.Refresh() |
489 | 545 | ||
490 | def RefreshPoints(self, pubsub_evt): | 546 | def RefreshPoints(self, pubsub_evt): |
491 | - self.CreatePixelArray() | 547 | + self.CalculatePixelPoints() |
492 | self.Refresh() | 548 | self.Refresh() |
493 | 549 | ||
494 | def SetHistrogramArray(self, h_array): | 550 | def SetHistrogramArray(self, h_array): |