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 | from math import * | 1 | from math import * |
| 2 | + | ||
| 2 | import vtk | 3 | import vtk |
| 4 | +import wx.lib.pubsub as ps | ||
| 5 | + | ||
| 3 | import utils | 6 | import utils |
| 4 | 7 | ||
| 5 | class CursorCircle: | 8 | class CursorCircle: |
| @@ -10,7 +13,7 @@ class CursorCircle: | @@ -10,7 +13,7 @@ class CursorCircle: | ||
| 10 | 13 | ||
| 11 | self.colour = (0.0, 0.0, 1.0) | 14 | self.colour = (0.0, 0.0, 1.0) |
| 12 | self.opacity = 1 | 15 | self.opacity = 1 |
| 13 | - self.radius = 20 | 16 | + self.radius = 15.0 |
| 14 | self.position = (0 ,0, 1) | 17 | self.position = (0 ,0, 1) |
| 15 | self.points = [] | 18 | self.points = [] |
| 16 | self.orientation = "AXIAL" | 19 | self.orientation = "AXIAL" |
| @@ -22,7 +25,8 @@ class CursorCircle: | @@ -22,7 +25,8 @@ class CursorCircle: | ||
| 22 | 25 | ||
| 23 | self.__build_actor() | 26 | self.__build_actor() |
| 24 | self.__calculate_area_pixels() | 27 | self.__calculate_area_pixels() |
| 25 | - | 28 | + |
| 29 | + | ||
| 26 | def __build_actor(self): | 30 | def __build_actor(self): |
| 27 | """ | 31 | """ |
| 28 | Function to plot the circle | 32 | Function to plot the circle |
| @@ -75,14 +79,15 @@ class CursorCircle: | @@ -75,14 +79,15 @@ class CursorCircle: | ||
| 75 | for k in utils.frange(xi,xf,xs): | 79 | for k in utils.frange(xi,xf,xs): |
| 76 | self.pixel_list.append((k, yi)) | 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 | self.disk.SetInnerRadius(radius-1) # filled = self.radius | 84 | self.disk.SetInnerRadius(radius-1) # filled = self.radius |
| 81 | - self.disk.SetOuterRadius(radius) # filled = 0x | 85 | + self.disk.SetOuterRadius(radius) # filled = 0 |
| 82 | self.__calculate_area_pixels() | 86 | self.__calculate_area_pixels() |
| 83 | 87 | ||
| 84 | def SetColour(self, colour): | 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 | def SetOrientation(self, orientation): | 92 | def SetOrientation(self, orientation): |
| 88 | self.orientation = orientation | 93 | self.orientation = orientation |
| @@ -94,13 +99,120 @@ class CursorCircle: | @@ -94,13 +99,120 @@ class CursorCircle: | ||
| 94 | self.actor.RotateY(90) | 99 | self.actor.RotateY(90) |
| 95 | 100 | ||
| 96 | def SetPosition(self, position): | 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 | self.position = position | 216 | self.position = position |
| 105 | self.actor.SetPosition(position) | 217 | self.actor.SetPosition(position) |
| 106 | 218 | ||
| @@ -110,7 +222,64 @@ class CursorCircle: | @@ -110,7 +222,64 @@ class CursorCircle: | ||
| 110 | def SetSpacing(self, spacing): | 222 | def SetSpacing(self, spacing): |
| 111 | self.spacing = spacing | 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 | def GetPixels(self): | 279 | def GetPixels(self): |
| 280 | + """ | ||
| 281 | + Return the points of the rectangle | ||
| 282 | + """ | ||
| 114 | px, py, pz = self.edition_position | 283 | px, py, pz = self.edition_position |
| 115 | orient = self.orientation | 284 | orient = self.orientation |
| 116 | xs, ys, zs = self.spacing | 285 | xs, ys, zs = self.spacing |
| @@ -119,7 +288,8 @@ class CursorCircle: | @@ -119,7 +288,8 @@ class CursorCircle: | ||
| 119 | # the area, and not the cursor position). | 288 | # the area, and not the cursor position). |
| 120 | # Let's calculate the absolute position | 289 | # Let's calculate the absolute position |
| 121 | # TODO: Optimize this!!!! | 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 | \ No newline at end of file | 296 | \ No newline at end of file |
invesalius/data/viewer_slice.py
| @@ -131,11 +131,43 @@ class Viewer(wx.Panel): | @@ -131,11 +131,43 @@ class Viewer(wx.Panel): | ||
| 131 | 131 | ||
| 132 | def ChangeBrushSize(self, pubsub_evt): | 132 | def ChangeBrushSize(self, pubsub_evt): |
| 133 | size = pubsub_evt.data | 133 | size = pubsub_evt.data |
| 134 | + self.brush_cursor_size = size | ||
| 134 | self.cursor.SetSize(size) | 135 | self.cursor.SetSize(size) |
| 135 | self.ren.Render() | 136 | self.ren.Render() |
| 136 | self.interactor.Render() | 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 | def OnMouseClick(self, obj, evt_vtk): | 171 | def OnMouseClick(self, obj, evt_vtk): |
| 140 | self.mouse_pressed = 1 | 172 | self.mouse_pressed = 1 |
| 141 | 173 | ||
| @@ -263,7 +295,11 @@ class Viewer(wx.Panel): | @@ -263,7 +295,11 @@ class Viewer(wx.Panel): | ||
| 263 | ps.Publisher().subscribe(self.UpdateRender, 'Update slice viewer') | 295 | ps.Publisher().subscribe(self.UpdateRender, 'Update slice viewer') |
| 264 | ps.Publisher().subscribe(self.ChangeSliceNumber, ('Set scroll position', | 296 | ps.Publisher().subscribe(self.ChangeSliceNumber, ('Set scroll position', |
| 265 | self.orientation)) | 297 | self.orientation)) |
| 298 | + | ||
| 299 | + ### | ||
| 266 | ps.Publisher().subscribe(self.ChangeBrushSize,'Set edition brush size') | 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 | def __bind_events_wx(self): | 304 | def __bind_events_wx(self): |
| 269 | self.scroll.Bind(wx.EVT_SCROLL, self.OnScrollBar) | 305 | self.scroll.Bind(wx.EVT_SCROLL, self.OnScrollBar) |
| @@ -373,6 +409,7 @@ class Viewer(wx.Panel): | @@ -373,6 +409,7 @@ class Viewer(wx.Panel): | ||
| 373 | def SetColour(self, pubsub_evt): | 409 | def SetColour(self, pubsub_evt): |
| 374 | colour_wx = pubsub_evt.data | 410 | colour_wx = pubsub_evt.data |
| 375 | colour_vtk = [colour/float(255) for colour in colour_wx] | 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 | self.interactor.Render() | 414 | self.interactor.Render() |
| 378 | 415 |