Commit b4ead449f1844e991b016736ee4a0764162ccde9
1 parent
4014a308
Exists in
master
and in
6 other branches
ADD: Editor brush - change type, change size, change colour
Showing
2 changed files
with
224 additions
and
17 deletions
Show diff stats
invesalius/data/cursor_actors.py
1 | 1 | from math import * |
2 | + | |
2 | 3 | import vtk |
4 | +import wx.lib.pubsub as ps | |
5 | + | |
3 | 6 | import utils |
4 | 7 | |
5 | 8 | class CursorCircle: |
... | ... | @@ -10,7 +13,7 @@ class CursorCircle: |
10 | 13 | |
11 | 14 | self.colour = (0.0, 0.0, 1.0) |
12 | 15 | self.opacity = 1 |
13 | - self.radius = 20 | |
16 | + self.radius = 15.0 | |
14 | 17 | self.position = (0 ,0, 1) |
15 | 18 | self.points = [] |
16 | 19 | self.orientation = "AXIAL" |
... | ... | @@ -22,7 +25,8 @@ class CursorCircle: |
22 | 25 | |
23 | 26 | self.__build_actor() |
24 | 27 | self.__calculate_area_pixels() |
25 | - | |
28 | + | |
29 | + | |
26 | 30 | def __build_actor(self): |
27 | 31 | """ |
28 | 32 | Function to plot the circle |
... | ... | @@ -75,14 +79,15 @@ class CursorCircle: |
75 | 79 | for k in utils.frange(xi,xf,xs): |
76 | 80 | self.pixel_list.append((k, yi)) |
77 | 81 | |
78 | - def SetSize(self, radius): | |
79 | - self.radius = radius | |
82 | + def SetSize(self, diameter): | |
83 | + radius = self.radius = diameter/2.0 | |
80 | 84 | self.disk.SetInnerRadius(radius-1) # filled = self.radius |
81 | - self.disk.SetOuterRadius(radius) # filled = 0x | |
85 | + self.disk.SetOuterRadius(radius) # filled = 0 | |
82 | 86 | self.__calculate_area_pixels() |
83 | 87 | |
84 | 88 | def SetColour(self, colour): |
85 | - self.actor.GetProperty().SetColor(self.colour) | |
89 | + self.colour = colour | |
90 | + self.actor.GetProperty().SetColor(colour) | |
86 | 91 | |
87 | 92 | def SetOrientation(self, orientation): |
88 | 93 | self.orientation = orientation |
... | ... | @@ -94,13 +99,120 @@ class CursorCircle: |
94 | 99 | self.actor.RotateY(90) |
95 | 100 | |
96 | 101 | def SetPosition(self, position): |
102 | + self.position = position | |
103 | + self.actor.SetPosition(position) | |
104 | + | |
105 | + def SetEditionPosition(self, position): | |
106 | + self.edition_position = position | |
107 | + | |
108 | + def SetSpacing(self, spacing): | |
109 | + self.spacing = spacing | |
110 | + | |
111 | + def GetPixels(self): | |
112 | + px, py, pz = self.edition_position | |
113 | + orient = self.orientation | |
114 | + xs, ys, zs = self.spacing | |
115 | + for pixel_0,pixel_1 in self.pixel_list: | |
116 | + # The position of the pixels in this list is relative (based only on | |
117 | + # the area, and not the cursor position). | |
118 | + # Let's calculate the absolute position | |
119 | + # TODO: Optimize this!!!! | |
120 | + abs_pixel = {"AXIAL": [px+pixel_0/xs, py+(pixel_1/ys), pz], | |
121 | + "CORONAL": [px+(pixel_0/xs), py, pz+(pixel_1/zs)], | |
122 | + "SAGITAL": [px, py+(pixel_0/ys), pz+(pixel_1/zs)]} | |
123 | + yield abs_pixel[orient] | |
124 | + | |
125 | + | |
126 | +#------------------------------------------------------------------------------- | |
127 | +#------------------------------------------------------------------------------- | |
128 | +#------------------------------------------------------------------------------- | |
129 | +#------------------------------------------------------------------------------- | |
130 | +#------------------------------------------------------------------------------- | |
131 | +#-------------------------------------------------------------------------------#------------------------------------------------------------------------------- | |
132 | +#------------------------------------------------------------------------------- | |
133 | +#------------------------------------------------------------------------------- | |
134 | +#------------------------------------------------------------------------------- | |
135 | +#------------------------------------------------------------------------------- | |
136 | +#------------------------------------------------------------------------------- | |
137 | +#------------------------------------------------------------------------------- | |
138 | +#------------------------------------------------------------------------------- | |
139 | + | |
140 | +class CursorRectangle: | |
97 | 141 | |
98 | - #if self.orientation == "AXIAL": | |
99 | - # z = 1 | |
100 | - #elif self.orientation == "CORONAL": | |
101 | - # y = 1 | |
102 | - #elif self.orientation == "SAGITAL": | |
103 | - # x = 1 | |
142 | + def __init__(self): | |
143 | + | |
144 | + self.colour = (0.0, 0.0, 1.0) | |
145 | + self.opacity = 1 | |
146 | + | |
147 | + self.x_length = 30 | |
148 | + self.y_length = 30 | |
149 | + | |
150 | + self.dimension = (self.x_length, self.y_length) | |
151 | + self.position = (0 ,0) | |
152 | + | |
153 | + self.mapper = vtk.vtkPolyDataMapper() | |
154 | + | |
155 | + self.retangle = vtk.vtkCubeSource() | |
156 | + self.actor = vtk.vtkActor() | |
157 | + | |
158 | + self.seeds_yi = vtk.vtkLineSource() | |
159 | + self.seeds_yf = vtk.vtkLineSource() | |
160 | + self.seeds_xi = vtk.vtkLineSource() | |
161 | + self.seeds_xf = vtk.vtkLineSource() | |
162 | + self.join_lines = vtk.vtkAppendPolyData() | |
163 | + | |
164 | + self.__build_actor() | |
165 | + self.__calculate_area_pixels() | |
166 | + | |
167 | + def SetSize(self, size): | |
168 | + print "SetSize", size | |
169 | + self.x_length = size | |
170 | + self.y_length = size | |
171 | + retangle = self.retangle | |
172 | + retangle.SetXLength(size) | |
173 | + retangle.SetYLength(size) | |
174 | + | |
175 | + seeds_yi = self.seeds_yi | |
176 | + seeds_yi.SetPoint1(0, 0, 0) | |
177 | + seeds_yi.SetPoint2(0, self.y_length, 0) | |
178 | + | |
179 | + seeds_yf = self.seeds_yf | |
180 | + seeds_yf.SetPoint1(self.x_length, self.y_length, 0) | |
181 | + seeds_yf.SetPoint2(self.x_length, 0, 0) | |
182 | + | |
183 | + seeds_xi = self.seeds_xi | |
184 | + seeds_xi.SetPoint1(0, self.y_length, 0) | |
185 | + seeds_xi.SetPoint2(self.x_length, self.y_length, 0) | |
186 | + | |
187 | + seeds_xf = self.seeds_xf | |
188 | + seeds_xf.SetPoint1(0, 0, 0) | |
189 | + seeds_xf.SetPoint2(self.x_length, 0, 0) | |
190 | + | |
191 | + join_lines = self.join_lines | |
192 | + join_lines.AddInput(seeds_yi.GetOutput()) | |
193 | + join_lines.AddInput(seeds_yf.GetOutput()) | |
194 | + join_lines.AddInput(seeds_xi.GetOutput()) | |
195 | + join_lines.AddInput(seeds_xf.GetOutput()) | |
196 | + | |
197 | + self.__calculate_area_pixels() | |
198 | + | |
199 | + def SetOrientation(self, orientation): | |
200 | + self.orientation = orientation | |
201 | + | |
202 | + def SetColour(self, colour): | |
203 | + self.colour = colour | |
204 | + self.actor.GetProperty().SetColor(colour) | |
205 | + | |
206 | + def SetOrientation(self, orientation): | |
207 | + self.orientation = orientation | |
208 | + | |
209 | + if orientation == "CORONAL": | |
210 | + self.actor.RotateX(90) | |
211 | + | |
212 | + if orientation == "SAGITAL": | |
213 | + self.actor.RotateY(90) | |
214 | + | |
215 | + def SetPosition(self, position): | |
104 | 216 | self.position = position |
105 | 217 | self.actor.SetPosition(position) |
106 | 218 | |
... | ... | @@ -110,7 +222,64 @@ class CursorCircle: |
110 | 222 | def SetSpacing(self, spacing): |
111 | 223 | self.spacing = spacing |
112 | 224 | |
225 | + def __build_actor(self): | |
226 | + """ | |
227 | + Function to plot the Retangle | |
228 | + """ | |
229 | + retangle = self.retangle | |
230 | + retangle.SetXLength(self.x_length) | |
231 | + retangle.SetYLength(self.y_length) | |
232 | + | |
233 | + seeds_yi = self.seeds_yi | |
234 | + seeds_yi.SetPoint1(0, 0, 0) | |
235 | + seeds_yi.SetPoint2(0, self.y_length, 0) | |
236 | + | |
237 | + seeds_yf = self.seeds_yf | |
238 | + seeds_yf.SetPoint1(self.x_length, self.y_length, 0) | |
239 | + seeds_yf.SetPoint2(self.x_length, 0, 0) | |
240 | + | |
241 | + seeds_xi = self.seeds_xi | |
242 | + seeds_xi.SetPoint1(0, self.y_length, 0) | |
243 | + seeds_xi.SetPoint2(self.x_length, self.y_length, 0) | |
244 | + | |
245 | + seeds_xf = self.seeds_xf | |
246 | + seeds_xf.SetPoint1(0, 0, 0) | |
247 | + seeds_xf.SetPoint2(self.x_length, 0, 0) | |
248 | + | |
249 | + join_lines = self.join_lines | |
250 | + join_lines.AddInput(seeds_yi.GetOutput()) | |
251 | + join_lines.AddInput(seeds_yf.GetOutput()) | |
252 | + join_lines.AddInput(seeds_xi.GetOutput()) | |
253 | + join_lines.AddInput(seeds_xf.GetOutput()) | |
254 | + | |
255 | + mapper = self.mapper | |
256 | + mapper.SetScalarRange(0, 360) | |
257 | + | |
258 | + actor = self.actor | |
259 | + | |
260 | + mapper.SetInput(join_lines.GetOutput()) | |
261 | + actor.SetPosition(self.position[0]-self.x_length/2, # if filled remov - | |
262 | + self.position[1]-self.y_length/2, 1) # idem | |
263 | + | |
264 | + actor.SetMapper(mapper) | |
265 | + actor.GetProperty().SetOpacity(self.opacity) | |
266 | + actor.GetProperty().SetColor(self.colour) | |
267 | + actor.SetVisibility(1) | |
268 | + | |
269 | + def __calculate_area_pixels(self): | |
270 | + xc = 0 | |
271 | + yc = 0 | |
272 | + z = 0 | |
273 | + self.pixel_list = [] | |
274 | + for i in xrange(int(yc - self.y_length/2), int(yc + self.y_length/2)): | |
275 | + for k in xrange(xc - self.x_length/2, xc + self.x_length/2): | |
276 | + self.pixel_list.append((k, i)) | |
277 | + | |
278 | + | |
113 | 279 | def GetPixels(self): |
280 | + """ | |
281 | + Return the points of the rectangle | |
282 | + """ | |
114 | 283 | px, py, pz = self.edition_position |
115 | 284 | orient = self.orientation |
116 | 285 | xs, ys, zs = self.spacing |
... | ... | @@ -119,7 +288,8 @@ class CursorCircle: |
119 | 288 | # the area, and not the cursor position). |
120 | 289 | # Let's calculate the absolute position |
121 | 290 | # TODO: Optimize this!!!! |
122 | - absolute_pixel = {"AXIAL": (px + pixel_0 / xs , py + pixel_1 / ys, pz), | |
123 | - "CORONAL": (px + pixel_0 / xs, py, pz + pixel_1 / zs), | |
124 | - "SAGITAL": (px, py + pixel_0 / ys, pz + pixel_1 / zs) } | |
125 | - yield absolute_pixel[orient] | |
291 | + abs_pixel = {"AXIAL": [px+pixel_0/xs, py+(pixel_1/ys), pz], | |
292 | + "CORONAL": [px+(pixel_0/xs), py, pz+(pixel_1/zs)], | |
293 | + "SAGITAL": [px, py+(pixel_0/ys), pz+(pixel_1/zs)]} | |
294 | + yield abs_pixel[orient] | |
295 | + | |
126 | 296 | \ No newline at end of file | ... | ... |
invesalius/data/viewer_slice.py
... | ... | @@ -131,11 +131,43 @@ class Viewer(wx.Panel): |
131 | 131 | |
132 | 132 | def ChangeBrushSize(self, pubsub_evt): |
133 | 133 | size = pubsub_evt.data |
134 | + self.brush_cursor_size = size | |
134 | 135 | self.cursor.SetSize(size) |
135 | 136 | self.ren.Render() |
136 | 137 | self.interactor.Render() |
138 | + | |
139 | + def ChangeBrushColour(self, pubsub_evt): | |
140 | + vtk_colour = pubsub_evt.data[3] | |
141 | + self.cursor.SetColour(vtk_colour) | |
142 | + self._brush_cursor_colour = vtk_colour | |
143 | + self.ren.Render() | |
144 | + self.interactor.Render() | |
137 | 145 | |
138 | 146 | |
147 | + def ChangeBrushActor(self, pubsub_evt): | |
148 | + brush_type = pubsub_evt.data | |
149 | + self.ren.RemoveActor(self.cursor.actor) | |
150 | + | |
151 | + if brush_type == 'square': | |
152 | + cursor = ca.CursorRectangle() | |
153 | + elif brush_type == 'circle': | |
154 | + cursor = ca.CursorCircle() | |
155 | + self.cursor = cursor | |
156 | + | |
157 | + cursor.SetOrientation(self.orientation) | |
158 | + coordinates = {"SAGITAL": [self.slice_number, 0, 0], | |
159 | + "CORONAL": [0, self.slice_number, 0], | |
160 | + "AXIAL": [0, 0, self.slice_number]} | |
161 | + cursor.SetPosition(coordinates[self.orientation]) | |
162 | + cursor.SetSpacing(self.imagedata.GetSpacing()) | |
163 | + cursor.SetColour(self._brush_cursor_colour) | |
164 | + cursor.SetSize(self.brush_cursor_size) | |
165 | + self.ren.AddActor(cursor.actor) | |
166 | + self.ren.Render() | |
167 | + self.interactor.Render() | |
168 | + self.cursor = cursor | |
169 | + | |
170 | + | |
139 | 171 | def OnMouseClick(self, obj, evt_vtk): |
140 | 172 | self.mouse_pressed = 1 |
141 | 173 | |
... | ... | @@ -263,7 +295,11 @@ class Viewer(wx.Panel): |
263 | 295 | ps.Publisher().subscribe(self.UpdateRender, 'Update slice viewer') |
264 | 296 | ps.Publisher().subscribe(self.ChangeSliceNumber, ('Set scroll position', |
265 | 297 | self.orientation)) |
298 | + | |
299 | + ### | |
266 | 300 | ps.Publisher().subscribe(self.ChangeBrushSize,'Set edition brush size') |
301 | + ps.Publisher().subscribe(self.ChangeBrushColour, 'Add mask') | |
302 | + ps.Publisher().subscribe(self.ChangeBrushActor, 'Set brush format') | |
267 | 303 | |
268 | 304 | def __bind_events_wx(self): |
269 | 305 | self.scroll.Bind(wx.EVT_SCROLL, self.OnScrollBar) |
... | ... | @@ -373,6 +409,7 @@ class Viewer(wx.Panel): |
373 | 409 | def SetColour(self, pubsub_evt): |
374 | 410 | colour_wx = pubsub_evt.data |
375 | 411 | colour_vtk = [colour/float(255) for colour in colour_wx] |
376 | - #self.editor.SetColour(colour_vtk) | |
412 | + self._brush_cursor_colour = vtk_colour | |
413 | + self.cursor.SetColour(colour_vtk) | |
377 | 414 | self.interactor.Render() |
378 | 415 | ... | ... |