Commit f9d49cf73f43e2e7a801f09dab738305899a1351
1 parent
fc5abcbd
Exists in
master
and in
68 other branches
ADD: 3D surface export to file
Showing
4 changed files
with
93 additions
and
16 deletions
Show diff stats
invesalius/constants.py
| 1 | 1 | import os.path |
| 2 | + | |
| 2 | 3 | import wx |
| 4 | + | |
| 3 | 5 | from project import Project |
| 4 | 6 | |
| 5 | 7 | # VTK text |
| ... | ... | @@ -187,3 +189,19 @@ MODE_WW_WL = 4#:"Bright and contrast adjustment"} |
| 187 | 189 | MODE_SLICE_SCROLL = -1 |
| 188 | 190 | MODE_SLICE_EDITOR = -2 |
| 189 | 191 | |
| 192 | +############ | |
| 193 | + | |
| 194 | + | |
| 195 | +FILETYPE_IV = wx.NewId() | |
| 196 | +FILETYPE_RIB = wx.NewId() | |
| 197 | +FILETYPE_STL = wx.NewId() | |
| 198 | +FILETYPE_VRML = wx.NewId() | |
| 199 | +FILETYPE_OBJ = wx.NewId() | |
| 200 | + | |
| 201 | +FILETYPE_BMP = wx.NewId() | |
| 202 | +FILETYPE_JPG = wx.NewId() | |
| 203 | +FILETYPE_PNG = wx.NewId() | |
| 204 | +FILETYPE_PS = wx.NewId() | |
| 205 | +FILETYPE_POV = wx.NewId() | |
| 206 | +FILETYPE_OBJ = wx.NewId() | |
| 207 | + | ... | ... |
invesalius/data/polydata_utils.py
| ... | ... | @@ -84,3 +84,17 @@ def CalculateSurfaceArea(polydata): |
| 84 | 84 | measured_polydata = vtk.vtkMassProperties() |
| 85 | 85 | measured_polydata.SetInput(polydata) |
| 86 | 86 | return measured_polydata.GetSurfaceArea() |
| 87 | + | |
| 88 | +def Merge(polydata_list): | |
| 89 | + append = vtk.vtkAppendPolyData() | |
| 90 | + | |
| 91 | + for polydata in polydata_list: | |
| 92 | + triangle = vtk.TriangleFilter() | |
| 93 | + triangle.SetInput(polydata) | |
| 94 | + append.AddInput(triangle.GetOutput()) | |
| 95 | + | |
| 96 | + clean = vtk.vtkCleanPolyData() | |
| 97 | + clean.SetInput(append.GetOutput()) | |
| 98 | + | |
| 99 | + return append.GetOutput() | |
| 100 | + | ... | ... |
invesalius/data/surface.py
| ... | ... | @@ -3,7 +3,7 @@ import wx.lib.pubsub as ps |
| 3 | 3 | |
| 4 | 4 | import constants as const |
| 5 | 5 | import imagedata_utils as iu |
| 6 | -from project import Project | |
| 6 | +import project as prj | |
| 7 | 7 | import vtk_utils as vu |
| 8 | 8 | import polydata_utils as pu |
| 9 | 9 | from imagedata_utils import BuildEditedImage |
| ... | ... | @@ -49,6 +49,7 @@ class SurfaceManager(): |
| 49 | 49 | |
| 50 | 50 | ps.Publisher().subscribe(self.OnChangeSurfaceName, 'Change surface name') |
| 51 | 51 | ps.Publisher().subscribe(self.OnShowSurface, 'Show surface') |
| 52 | + ps.Publisher().subscribe(self.OnExportSurface,'Export surface to file') | |
| 52 | 53 | |
| 53 | 54 | def AddNewActor(self, pubsub_evt): |
| 54 | 55 | """ |
| ... | ... | @@ -170,13 +171,6 @@ class SurfaceManager(): |
| 170 | 171 | UpdateProgress(normals, "Orienting normals...")) |
| 171 | 172 | polydata = normals.GetOutput() |
| 172 | 173 | |
| 173 | - #======= Temporary Code ======= | |
| 174 | - stl = vtk.vtkSTLWriter() | |
| 175 | - stl.SetFileTypeToBinary() | |
| 176 | - stl.SetInputConnection(normals.GetOutputPort()) | |
| 177 | - stl.SetFileName("surface.stl") | |
| 178 | - stl.Write() | |
| 179 | - #============================== | |
| 180 | 174 | |
| 181 | 175 | # TODO (Paulo): Why do we need this filter? |
| 182 | 176 | # without this the volume does not appear |
| ... | ... | @@ -207,7 +201,7 @@ class SurfaceManager(): |
| 207 | 201 | actor.GetProperty().SetOpacity(1-surface.transparency) |
| 208 | 202 | |
| 209 | 203 | # Append surface into Project.surface_dict |
| 210 | - proj = Project() | |
| 204 | + proj = prj.Project() | |
| 211 | 205 | proj.surface_dict[surface.index] = surface |
| 212 | 206 | |
| 213 | 207 | # Save actor for future management tasks |
| ... | ... | @@ -241,13 +235,13 @@ class SurfaceManager(): |
| 241 | 235 | ps.Publisher().sendMessage('Remove surface actor from viewer', (index)) |
| 242 | 236 | self.actors_dict.pop(index) |
| 243 | 237 | # Remove surface from project's surface_dict |
| 244 | - proj = Project() | |
| 238 | + proj = prj.Project() | |
| 245 | 239 | proj.surface_dict.pop(index) |
| 246 | 240 | |
| 247 | 241 | |
| 248 | 242 | def OnChangeSurfaceName(self, pubsub_evt): |
| 249 | 243 | index, name = pubsub_evt.data |
| 250 | - proj = Project() | |
| 244 | + proj = prj.Project() | |
| 251 | 245 | proj.surface_dict[index].name = name |
| 252 | 246 | |
| 253 | 247 | def OnShowSurface(self, pubsub_evt): |
| ... | ... | @@ -261,7 +255,7 @@ class SurfaceManager(): |
| 261 | 255 | """ |
| 262 | 256 | self.actors_dict[index].SetVisibility(value) |
| 263 | 257 | # Update value in project's surface_dict |
| 264 | - proj = Project() | |
| 258 | + proj = prj.Project() | |
| 265 | 259 | proj.surface_dict[index].is_shown = value |
| 266 | 260 | ps.Publisher().sendMessage('Render volume viewer') |
| 267 | 261 | |
| ... | ... | @@ -273,7 +267,7 @@ class SurfaceManager(): |
| 273 | 267 | index, value = pubsub_evt.data |
| 274 | 268 | self.actors_dict[index].GetProperty().SetOpacity(1-value) |
| 275 | 269 | # Update value in project's surface_dict |
| 276 | - proj = Project() | |
| 270 | + proj = prj.Project() | |
| 277 | 271 | proj.surface_dict[index].transparency = value |
| 278 | 272 | ps.Publisher().sendMessage('Render volume viewer') |
| 279 | 273 | |
| ... | ... | @@ -283,6 +277,32 @@ class SurfaceManager(): |
| 283 | 277 | index, colour = pubsub_evt.data |
| 284 | 278 | self.actors_dict[index].GetProperty().SetColor(colour) |
| 285 | 279 | # Update value in project's surface_dict |
| 286 | - proj = Project() | |
| 280 | + proj = prj.Project() | |
| 287 | 281 | proj.surface_dict[index].colour = colour |
| 288 | 282 | ps.Publisher().sendMessage('Render volume viewer') |
| 283 | + | |
| 284 | + | |
| 285 | + def OnExportSurface(self, pubsub_evt): | |
| 286 | + filename, filetype = pubsub_evt.data | |
| 287 | + | |
| 288 | + if filetype == const.FILETYPE_STL: | |
| 289 | + proj = prj.Project() | |
| 290 | + polydata_list = [] | |
| 291 | + for index in proj.surface_dict: | |
| 292 | + surface = proj.surface_dict[index] | |
| 293 | + if surface.is_shown: | |
| 294 | + polydata_list.append(surface.polydata) | |
| 295 | + if len(polydata_list) < 0: | |
| 296 | + print "oops - no polydata" | |
| 297 | + return | |
| 298 | + elif len(polydata_list) == 1: | |
| 299 | + polydata = polydata_list[0] | |
| 300 | + else: | |
| 301 | + polydata = pu.Merge(polydata_list) | |
| 302 | + writer = vtk.vtkSTLWriter() | |
| 303 | + writer.SetFileTypeToBinary() | |
| 304 | + writer.SetFileName(filename) | |
| 305 | + writer.SetInput(polydata) | |
| 306 | + writer.Write() | |
| 307 | + | |
| 308 | + | ... | ... |
invesalius/data/viewer_volume.py
| ... | ... | @@ -158,6 +158,32 @@ class Viewer(wx.Panel): |
| 158 | 158 | ('Set interaction mode', |
| 159 | 159 | const.MODE_SLICE_EDITOR)) |
| 160 | 160 | |
| 161 | + ps.Publisher().subscribe(self.OnExportSurface, 'Export surface to file') | |
| 162 | + | |
| 163 | + def OnExportSurface(self, pubsub_evt): | |
| 164 | + filename, filetype = pubsub_evt.data | |
| 165 | + renwin = self.interactor.GetRenderWindow() | |
| 166 | + | |
| 167 | + if filetype == const.FILETYPE_RIB: | |
| 168 | + writer = vtk.vtkIVExporter() | |
| 169 | + writer.SetFileName(filename) | |
| 170 | + writer.SetInput(renwin) | |
| 171 | + elif filetype == const.FILETYPE_VRML: | |
| 172 | + writer = vtk.vtkVRMLExporter() | |
| 173 | + writer.SetFileName(filename) | |
| 174 | + writer.SetInput(renwin) | |
| 175 | + elif filetype == const.FILETYPE_OBJ: | |
| 176 | + writer = vtk.vtkOBJExporter() | |
| 177 | + writer.SetFilePrefix(filename.split(".")[-2]) | |
| 178 | + writer.SetInput(renwin) | |
| 179 | + else: # const.FILETYPE_IV: | |
| 180 | + writer = vtk.vtkIVExporter() | |
| 181 | + writer.SetFileName(filename) | |
| 182 | + writer.SetInput(renwin) | |
| 183 | + writer.Write() | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 161 | 187 | def __bind_events_wx(self): |
| 162 | 188 | #self.Bind(wx.EVT_SIZE, self.OnSize) |
| 163 | 189 | pass |
| ... | ... | @@ -216,7 +242,6 @@ class Viewer(wx.Panel): |
| 216 | 242 | self.ren.ResetCamera() |
| 217 | 243 | self.ren.ResetCameraClippingRange() |
| 218 | 244 | |
| 219 | - #self.ShowOrientationCube() | |
| 220 | 245 | |
| 221 | 246 | self.UpdateRender() |
| 222 | 247 | |
| ... | ... | @@ -239,7 +264,7 @@ class Viewer(wx.Panel): |
| 239 | 264 | ren.ResetCameraClippingRange() |
| 240 | 265 | |
| 241 | 266 | |
| 242 | - self.ShowOrientationCube() | |
| 267 | + #self.ShowOrientationCube() | |
| 243 | 268 | |
| 244 | 269 | self.interactor.Render() |
| 245 | 270 | ... | ... |