Commit 5caf20b9d7ac4ed1b953a8c21f040d4ec8dee527

Authored by tatiana
1 parent 2b5c9c39

FIX: Normals in STL and OBJ

invesalius/data/surface.py
@@ -35,12 +35,12 @@ import wx.lib.pubsub as ps @@ -35,12 +35,12 @@ import wx.lib.pubsub as ps
35 #------------------------------------------------------------------ 35 #------------------------------------------------------------------
36 class SurfaceProcess(multiprocessing.Process): 36 class SurfaceProcess(multiprocessing.Process):
37 37
38 - def __init__(self, conn, filename, mode, min_value, max_value, 38 + def __init__(self, pipe, filename, mode, min_value, max_value,
39 decimate_reduction, smooth_relaxation_factor, 39 decimate_reduction, smooth_relaxation_factor,
40 smooth_iterations): 40 smooth_iterations):
41 41
42 multiprocessing.Process.__init__(self) 42 multiprocessing.Process.__init__(self)
43 - self.conn = conn 43 + self.pipe = pipe
44 self.filename = filename 44 self.filename = filename
45 self.mode = mode 45 self.mode = mode
46 self.min_value = min_value 46 self.min_value = min_value
@@ -54,7 +54,7 @@ class SurfaceProcess(multiprocessing.Process): @@ -54,7 +54,7 @@ class SurfaceProcess(multiprocessing.Process):
54 54
55 def SendProgress(self, obj, msg): 55 def SendProgress(self, obj, msg):
56 prog = obj.GetProgress() 56 prog = obj.GetProgress()
57 - self.conn.send([prog, msg]) 57 + self.pipe.send([prog, msg])
58 58
59 def CreateSurface(self): 59 def CreateSurface(self):
60 60
@@ -97,7 +97,7 @@ class SurfaceProcess(multiprocessing.Process): @@ -97,7 +97,7 @@ class SurfaceProcess(multiprocessing.Process):
97 decimation.SetTargetReduction(self.decimate_reduction) 97 decimation.SetTargetReduction(self.decimate_reduction)
98 decimation.GetOutput().ReleaseDataFlagOn() 98 decimation.GetOutput().ReleaseDataFlagOn()
99 decimation.AddObserver("ProgressEvent", lambda obj,evt: 99 decimation.AddObserver("ProgressEvent", lambda obj,evt:
100 - self.SendProgress(obj, "Reducing number of triangles...")) 100 + self.SendProgress(obj, "Generating 3D surface..."))
101 polydata = decimation.GetOutput() 101 polydata = decimation.GetOutput()
102 102
103 if self.smooth_iterations and self.smooth_relaxation_factor: 103 if self.smooth_iterations and self.smooth_relaxation_factor:
@@ -110,7 +110,7 @@ class SurfaceProcess(multiprocessing.Process): @@ -110,7 +110,7 @@ class SurfaceProcess(multiprocessing.Process):
110 smoother.BoundarySmoothingOn() 110 smoother.BoundarySmoothingOn()
111 smoother.GetOutput().ReleaseDataFlagOn() 111 smoother.GetOutput().ReleaseDataFlagOn()
112 smoother.AddObserver("ProgressEvent", lambda obj,evt: 112 smoother.AddObserver("ProgressEvent", lambda obj,evt:
113 - self.SendProgress(obj, "Smoothing surface...")) 113 + self.SendProgress(obj, "Generating 3D surface..."))
114 polydata = smoother.GetOutput() 114 polydata = smoother.GetOutput()
115 115
116 # Filter used to detect and fill holes. Only fill boundary edges holes. 116 # Filter used to detect and fill holes. Only fill boundary edges holes.
@@ -120,19 +120,9 @@ class SurfaceProcess(multiprocessing.Process): @@ -120,19 +120,9 @@ class SurfaceProcess(multiprocessing.Process):
120 filled_polydata.SetInput(polydata) 120 filled_polydata.SetInput(polydata)
121 filled_polydata.SetHoleSize(500) 121 filled_polydata.SetHoleSize(500)
122 filled_polydata.AddObserver("ProgressEvent", lambda obj,evt: 122 filled_polydata.AddObserver("ProgressEvent", lambda obj,evt:
123 - self.SendProgress(obj, "Filling surface...")) 123 + self.SendProgress(obj, "Generating 3D surface..."))
124 polydata = filled_polydata.GetOutput() 124 polydata = filled_polydata.GetOutput()
125 - # Orient normals from inside to outside  
126 125
127 - normals = vtk.vtkPolyDataNormals()  
128 - normals.SetInput(polydata)  
129 - normals.SetFeatureAngle(80)  
130 - normals.AutoOrientNormalsOn()  
131 - normals.GetOutput().ReleaseDataFlagOn()  
132 - normals.AddObserver("ProgressEvent", lambda obj,evt:  
133 - self.SendProgress(obj, "Orienting normals..."))  
134 - normals.UpdateInformation()  
135 - polydata = normals.GetOutput()  
136 126
137 filename = tempfile.mktemp() 127 filename = tempfile.mktemp()
138 writer = vtk.vtkXMLPolyDataWriter() 128 writer = vtk.vtkXMLPolyDataWriter()
@@ -140,8 +130,8 @@ class SurfaceProcess(multiprocessing.Process): @@ -140,8 +130,8 @@ class SurfaceProcess(multiprocessing.Process):
140 writer.SetFileName(filename) 130 writer.SetFileName(filename)
141 writer.Write() 131 writer.Write()
142 132
143 - self.conn.send(None)  
144 - self.conn.send(filename) 133 + self.pipe.send(None)
  134 + self.pipe.send(filename)
145 135
146 #---------------------------------------------------------------------------------------------- 136 #----------------------------------------------------------------------------------------------
147 class Surface(): 137 class Surface():
@@ -307,19 +297,19 @@ class SurfaceManager(): @@ -307,19 +297,19 @@ class SurfaceManager():
307 pipeline_size = 5 297 pipeline_size = 5
308 UpdateProgress = vu.ShowProgress(pipeline_size) 298 UpdateProgress = vu.ShowProgress(pipeline_size)
309 299
310 - conn_in, conn_out = multiprocessing.Pipe()  
311 - sp = SurfaceProcess(conn_in, filename_img, mode, min_value, max_value, 300 + pipe_in, pipe_out = multiprocessing.Pipe()
  301 + sp = SurfaceProcess(pipe_in, filename_img, mode, min_value, max_value,
312 decimate_reduction, smooth_relaxation_factor, 302 decimate_reduction, smooth_relaxation_factor,
313 smooth_iterations) 303 smooth_iterations)
314 sp.start() 304 sp.start()
315 305
316 while 1: 306 while 1:
317 - msg = conn_out.recv() 307 + msg = pipe_out.recv()
318 if(msg is None): 308 if(msg is None):
319 break 309 break
320 UpdateProgress(msg[0],msg[1]) 310 UpdateProgress(msg[0],msg[1])
321 311
322 - filename_polydata = conn_out.recv() 312 + filename_polydata = pipe_out.recv()
323 313
324 reader = vtk.vtkXMLPolyDataReader() 314 reader = vtk.vtkXMLPolyDataReader()
325 reader.SetFileName(filename_polydata) 315 reader.SetFileName(filename_polydata)
@@ -447,16 +437,19 @@ class SurfaceManager(): @@ -447,16 +437,19 @@ class SurfaceManager():
447 def OnExportSurface(self, pubsub_evt): 437 def OnExportSurface(self, pubsub_evt):
448 filename, filetype = pubsub_evt.data 438 filename, filetype = pubsub_evt.data
449 if (filetype == const.FILETYPE_STL) or\ 439 if (filetype == const.FILETYPE_STL) or\
450 - (filetype == const.FILETYPE_VTP): 440 + (filetype == const.FILETYPE_VTP) or\
  441 + (filetype == const.FILETYPE_PLY) :
451 442
452 # First we identify all surfaces that are selected 443 # First we identify all surfaces that are selected
453 # (if any) 444 # (if any)
454 proj = prj.Project() 445 proj = prj.Project()
455 polydata_list = [] 446 polydata_list = []
  447 +
456 for index in proj.surface_dict: 448 for index in proj.surface_dict:
457 surface = proj.surface_dict[index] 449 surface = proj.surface_dict[index]
458 if surface.is_shown: 450 if surface.is_shown:
459 polydata_list.append(surface.polydata) 451 polydata_list.append(surface.polydata)
  452 +
460 if len(polydata_list) == 0: 453 if len(polydata_list) == 0:
461 print "oops - no polydata" 454 print "oops - no polydata"
462 return 455 return
@@ -473,8 +466,8 @@ class SurfaceManager(): @@ -473,8 +466,8 @@ class SurfaceManager():
473 writer.SetFileTypeToBinary() 466 writer.SetFileTypeToBinary()
474 elif filetype == const.FILETYPE_VTP: 467 elif filetype == const.FILETYPE_VTP:
475 writer = vtk.vtkXMLPolyDataWriter() 468 writer = vtk.vtkXMLPolyDataWriter()
476 - elif filetype == const.FILETYPE_IV:  
477 - writer = vtk.vtkIVWriter() 469 + #elif filetype == const.FILETYPE_IV:
  470 + # writer = vtk.vtkIVWriter()
478 elif filetype == const.FILETYPE_PLY: 471 elif filetype == const.FILETYPE_PLY:
479 writer = vtk.vtkPLYWriter() 472 writer = vtk.vtkPLYWriter()
480 writer.SetFileTypeToBinary() 473 writer.SetFileTypeToBinary()
@@ -482,6 +475,16 @@ class SurfaceManager(): @@ -482,6 +475,16 @@ class SurfaceManager():
482 #writer.SetColorModeToUniformCellColor() 475 #writer.SetColorModeToUniformCellColor()
483 #writer.SetColor(255, 0, 0) 476 #writer.SetColor(255, 0, 0)
484 477
  478 + if filetype == const.FILETYPE_STL:
  479 + # Invert normals
  480 + normals = vtk.vtkPolyDataNormals()
  481 + normals.SetInput(polydata)
  482 + normals.SetFeatureAngle(80)
  483 + normals.AutoOrientNormalsOn()
  484 + normals.GetOutput().ReleaseDataFlagOn()
  485 + normals.UpdateInformation()
  486 + polydata = normals.GetOutput()
  487 +
485 writer.SetFileName(filename) 488 writer.SetFileName(filename)
486 writer.SetInput(polydata) 489 writer.SetInput(polydata)
487 writer.Write() 490 writer.Write()
invesalius/data/viewer_volume.py
@@ -279,25 +279,30 @@ class Viewer(wx.Panel): @@ -279,25 +279,30 @@ class Viewer(wx.Panel):
279 279
280 def OnExportSurface(self, pubsub_evt): 280 def OnExportSurface(self, pubsub_evt):
281 filename, filetype = pubsub_evt.data 281 filename, filetype = pubsub_evt.data
  282 + fileprefix = filename.split(".")[-2]
282 renwin = self.interactor.GetRenderWindow() 283 renwin = self.interactor.GetRenderWindow()
283 284
284 if filetype == const.FILETYPE_RIB: 285 if filetype == const.FILETYPE_RIB:
285 writer = vtk.vtkRIBExporter() 286 writer = vtk.vtkRIBExporter()
286 - writer.SetFileName(filename) 287 + writer.SetFilePrefix(fileprefix)
  288 + writer.SetTexturePrefix(fileprefix)
287 writer.SetInput(renwin) 289 writer.SetInput(renwin)
  290 + writer.Write()
288 elif filetype == const.FILETYPE_VRML: 291 elif filetype == const.FILETYPE_VRML:
289 writer = vtk.vtkVRMLExporter() 292 writer = vtk.vtkVRMLExporter()
290 writer.SetFileName(filename) 293 writer.SetFileName(filename)
291 writer.SetInput(renwin) 294 writer.SetInput(renwin)
  295 + writer.Write()
292 elif filetype == const.FILETYPE_OBJ: 296 elif filetype == const.FILETYPE_OBJ:
293 writer = vtk.vtkOBJExporter() 297 writer = vtk.vtkOBJExporter()
294 - writer.SetFilePrefix(filename.split(".")[-2]) 298 + writer.SetFilePrefix(fileprefix)
295 writer.SetInput(renwin) 299 writer.SetInput(renwin)
296 - else: # const.FILETYPE_IV: 300 + writer.Write()
  301 + elif filetype == const.FILETYPE_IV:
297 writer = vtk.vtkIVExporter() 302 writer = vtk.vtkIVExporter()
298 writer.SetFileName(filename) 303 writer.SetFileName(filename)
299 writer.SetInput(renwin) 304 writer.SetInput(renwin)
300 - writer.Write() 305 + writer.Write()
301 306
302 def OnEnableBrightContrast(self, pubsub_evt): 307 def OnEnableBrightContrast(self, pubsub_evt):
303 style = self.style 308 style = self.style