Commit b4ead449f1844e991b016736ee4a0764162ccde9
1 parent
4014a308
Exists in
master
and in
68 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 | ... | ... |