Commit 7c2fdb4bbe8bb56d5fbc8e15f04f26f0168f8712

Authored by tfmoraes
1 parent 4191a837

Merge branch 'surface_creation'

invesalius/data/slice_.py
@@ -596,10 +596,11 @@ class Slice(object): @@ -596,10 +596,11 @@ class Slice(object):
596 #--------------------------------------------------------------------------- 596 #---------------------------------------------------------------------------
597 597
598 def CreateSurfaceFromIndex(self, pubsub_evt): 598 def CreateSurfaceFromIndex(self, pubsub_evt):
599 - mask_index, overwrite_surface, algorithm, options = pubsub_evt.data 599 + print pubsub_evt.data
  600 + surface_parameters = pubsub_evt.data
600 601
601 proj = Project() 602 proj = Project()
602 - mask = proj.mask_dict[mask_index] 603 + mask = proj.mask_dict[surface_parameters['options']['index']]
603 604
604 # This is very important. Do not use masks' imagedata. It would mess up 605 # This is very important. Do not use masks' imagedata. It would mess up
605 # surface quality event when using contour 606 # surface quality event when using contour
@@ -611,12 +612,8 @@ class Slice(object): @@ -611,12 +612,8 @@ class Slice(object):
611 612
612 mask.matrix.flush() 613 mask.matrix.flush()
613 614
614 - Publisher.sendMessage('Create surface', (algorithm, options,  
615 - self.matrix,  
616 - self.matrix_filename,  
617 - mask, self.spacing,  
618 - overwrite_surface))  
619 - 615 + Publisher.sendMessage('Create surface', (self, mask,
  616 + surface_parameters))
620 def GetOutput(self): 617 def GetOutput(self):
621 return self.blend_filter.GetOutput() 618 return self.blend_filter.GetOutput()
622 619
invesalius/data/surface.py
@@ -369,31 +369,29 @@ class SurfaceManager(): @@ -369,31 +369,29 @@ class SurfaceManager():
369 """ 369 """
370 Create surface actor, save into project and send it to viewer. 370 Create surface actor, save into project and send it to viewer.
371 """ 371 """
372 - algorithm, options, matrix, filename_img, mask, spacing, overwrite = pubsub_evt.data  
373 - min_value, max_value = mask.threshold_range  
374 - fill_holes = False  
375 -  
376 - mask.matrix.flush()  
377 -  
378 - #if len(surface_data) == 5:  
379 - #imagedata, colour, [min_value, max_value], \  
380 - #edited_points, overwrite = pubsub_evt.data  
381 - #quality=_('Optimal *')  
382 - #surface_name = ""  
383 - #fill_holes = True  
384 - #keep_largest = False  
385 - #else:  
386 - #imagedata, colour, [min_value, max_value],\  
387 - #edited_points, overwrite, surface_name,\  
388 - #quality, fill_holes, keep_largest =\  
389 - #pubsub_evt.data 372 + slice_, mask, surface_parameters = pubsub_evt.data
  373 + matrix = slice_.matrix
  374 + filename_img = slice_.matrix_filename
  375 + spacing = slice_.spacing
  376 +
  377 + algorithm = surface_parameters['method']['algorithm']
  378 + options = surface_parameters['method']['options']
  379 +
  380 + surface_name = surface_parameters['options']['name']
  381 + quality = surface_parameters['options']['quality']
  382 + fill_holes = surface_parameters['options']['fill']
  383 + keep_largest = surface_parameters['options']['keep_largest']
390 384
391 mode = 'CONTOUR' # 'GRAYSCALE' 385 mode = 'CONTOUR' # 'GRAYSCALE'
392 - quality=_('Optimal *')  
393 - keep_largest = False  
394 - surface_name = "" 386 + min_value, max_value = mask.threshold_range
395 colour = mask.colour 387 colour = mask.colour
396 388
  389 + try:
  390 + overwrite = surface_parameters['options']['overwrite']
  391 + except KeyError:
  392 + overwrite = False
  393 + mask.matrix.flush()
  394 +
397 if quality in const.SURFACE_QUALITY.keys(): 395 if quality in const.SURFACE_QUALITY.keys():
398 imagedata_resolution = const.SURFACE_QUALITY[quality][0] 396 imagedata_resolution = const.SURFACE_QUALITY[quality][0]
399 smooth_iterations = const.SURFACE_QUALITY[quality][1] 397 smooth_iterations = const.SURFACE_QUALITY[quality][1]
@@ -448,8 +446,9 @@ class SurfaceManager(): @@ -448,8 +446,9 @@ class SurfaceManager():
448 smooth_relaxation_factor, 446 smooth_relaxation_factor,
449 smooth_iterations, language, 447 smooth_iterations, language,
450 flip_image, q_in, q_out, 448 flip_image, q_in, q_out,
451 - mask.was_edited and algorithm != u'InVesalius 3.b2',  
452 - algorithm) 449 + algorithm != 'Default',
  450 + algorithm,
  451 + imagedata_resolution)
453 p.append(sp) 452 p.append(sp)
454 sp.start() 453 sp.start()
455 454
@@ -501,7 +500,7 @@ class SurfaceManager(): @@ -501,7 +500,7 @@ class SurfaceManager():
501 polydata.SetSource(None) 500 polydata.SetSource(None)
502 del polydata_append 501 del polydata_append
503 502
504 - if algorithm == u'Context aware smoothing': 503 + if algorithm == 'ca_smoothing':
505 normals = vtk.vtkPolyDataNormals() 504 normals = vtk.vtkPolyDataNormals()
506 normals_ref = weakref.ref(normals) 505 normals_ref = weakref.ref(normals)
507 normals_ref().AddObserver("ProgressEvent", lambda obj,evt: 506 normals_ref().AddObserver("ProgressEvent", lambda obj,evt:
@@ -542,21 +541,23 @@ class SurfaceManager(): @@ -542,21 +541,23 @@ class SurfaceManager():
542 polydata.DebugOn() 541 polydata.DebugOn()
543 542
544 else: 543 else:
545 - smoother = vtk.vtkWindowedSincPolyDataFilter() 544 + #smoother = vtk.vtkWindowedSincPolyDataFilter()
  545 + smoother = vtk.vtkSmoothPolyDataFilter()
546 smoother_ref = weakref.ref(smoother) 546 smoother_ref = weakref.ref(smoother)
547 smoother_ref().AddObserver("ProgressEvent", lambda obj,evt: 547 smoother_ref().AddObserver("ProgressEvent", lambda obj,evt:
548 UpdateProgress(smoother_ref(), _("Generating 3D surface..."))) 548 UpdateProgress(smoother_ref(), _("Generating 3D surface...")))
549 smoother.SetInput(polydata) 549 smoother.SetInput(polydata)
550 smoother.SetNumberOfIterations(smooth_iterations) 550 smoother.SetNumberOfIterations(smooth_iterations)
551 - smoother.SetFeatureAngle(120)  
552 - smoother.SetEdgeAngle(90.0) 551 + smoother.SetRelaxationFactor(smooth_relaxation_factor)
  552 + smoother.SetFeatureAngle(80)
  553 + #smoother.SetEdgeAngle(90.0)
  554 + #smoother.SetPassBand(0.1)
553 smoother.BoundarySmoothingOn() 555 smoother.BoundarySmoothingOn()
554 - smoother.SetPassBand(0.1) 556 + smoother.FeatureEdgeSmoothingOn()
  557 + #smoother.NormalizeCoordinatesOn()
  558 + #smoother.NonManifoldSmoothingOn()
555 smoother.ReleaseDataFlagOn() 559 smoother.ReleaseDataFlagOn()
556 smoother.GetOutput().ReleaseDataFlagOn() 560 smoother.GetOutput().ReleaseDataFlagOn()
557 - #smoother.FeatureEdgeSmoothingOn()  
558 - #smoother.NonManifoldSmoothingOn()  
559 - #smoother.NormalizeCoordinatesOn()  
560 smoother.Update() 561 smoother.Update()
561 del polydata 562 del polydata
562 polydata = smoother.GetOutput() 563 polydata = smoother.GetOutput()
invesalius/data/surface_process.py
@@ -7,6 +7,8 @@ import vtk @@ -7,6 +7,8 @@ import vtk
7 7
8 import i18n 8 import i18n
9 import converters 9 import converters
  10 +import imagedata_utils as iu
  11 +
10 from scipy import ndimage 12 from scipy import ndimage
11 13
12 class SurfaceProcess(multiprocessing.Process): 14 class SurfaceProcess(multiprocessing.Process):
@@ -15,7 +17,7 @@ class SurfaceProcess(multiprocessing.Process): @@ -15,7 +17,7 @@ class SurfaceProcess(multiprocessing.Process):
15 mask_shape, mask_dtype, spacing, mode, min_value, max_value, 17 mask_shape, mask_dtype, spacing, mode, min_value, max_value,
16 decimate_reduction, smooth_relaxation_factor, 18 decimate_reduction, smooth_relaxation_factor,
17 smooth_iterations, language, flip_image, q_in, q_out, 19 smooth_iterations, language, flip_image, q_in, q_out,
18 - from_binary, algorithm): 20 + from_binary, algorithm, imagedata_resolution):
19 21
20 multiprocessing.Process.__init__(self) 22 multiprocessing.Process.__init__(self)
21 self.pipe = pipe 23 self.pipe = pipe
@@ -35,6 +37,7 @@ class SurfaceProcess(multiprocessing.Process): @@ -35,6 +37,7 @@ class SurfaceProcess(multiprocessing.Process):
35 self.shape = shape 37 self.shape = shape
36 self.from_binary = from_binary 38 self.from_binary = from_binary
37 self.algorithm = algorithm 39 self.algorithm = algorithm
  40 + self.imagedata_resolution = imagedata_resolution
38 41
39 self.mask_filename = mask_filename 42 self.mask_filename = mask_filename
40 self.mask_shape = mask_shape 43 self.mask_shape = mask_shape
@@ -96,6 +99,9 @@ class SurfaceProcess(multiprocessing.Process): @@ -96,6 +99,9 @@ class SurfaceProcess(multiprocessing.Process):
96 "AXIAL") 99 "AXIAL")
97 del a_image 100 del a_image
98 101
  102 + if self.imagedata_resolution:
  103 + image = iu.ResampleImage3D(image, self.imagedata_resolution)
  104 +
99 flip = vtk.vtkImageFlip() 105 flip = vtk.vtkImageFlip()
100 flip.SetInput(image) 106 flip.SetInput(image)
101 flip.SetFilteredAxis(1) 107 flip.SetFilteredAxis(1)
invesalius/gui/data_notebook.py
@@ -29,6 +29,7 @@ import wx.lib.platebtn as pbtn @@ -29,6 +29,7 @@ import wx.lib.platebtn as pbtn
29 from wx.lib.pubsub import pub as Publisher 29 from wx.lib.pubsub import pub as Publisher
30 30
31 import constants as const 31 import constants as const
  32 +import data.slice_ as slice_
32 import gui.dialogs as dlg 33 import gui.dialogs as dlg
33 import gui.widgets.listctrl as listmix 34 import gui.widgets.listctrl as listmix
34 import utils as ul 35 import utils as ul
@@ -615,9 +616,10 @@ class SurfaceButtonControlPanel(wx.Panel): @@ -615,9 +616,10 @@ class SurfaceButtonControlPanel(wx.Panel):
615 self.OnDuplicate() 616 self.OnDuplicate()
616 617
617 def OnNew(self): 618 def OnNew(self):
618 - import project as prj  
619 -  
620 - dialog = dlg.NewSurfaceDialog() 619 + sl = slice_.Slice()
  620 + dialog = dlg.SurfaceCreationDialog(self, -1,
  621 + _('InVesalius 3 - New surface'),
  622 + mask_edited=sl.current_mask.was_edited)
621 try: 623 try:
622 if dialog.ShowModal() == wx.ID_OK: 624 if dialog.ShowModal() == wx.ID_OK:
623 ok = 1 625 ok = 1
@@ -627,25 +629,10 @@ class SurfaceButtonControlPanel(wx.Panel): @@ -627,25 +629,10 @@ class SurfaceButtonControlPanel(wx.Panel):
627 ok = 1 629 ok = 1
628 630
629 if ok: 631 if ok:
630 - (mask_index, surface_name, surface_quality, fill_holes,\  
631 - keep_largest) = dialog.GetValue()  
632 -  
633 - # Retrieve information from mask  
634 - proj = prj.Project()  
635 - mask = proj.mask_dict[mask_index]  
636 -  
637 - # Send all information so surface can be created  
638 - surface_data = [proj.imagedata,  
639 - mask.colour,  
640 - mask.threshold_range,  
641 - mask.edited_points,  
642 - False, # overwrite  
643 - surface_name,  
644 - surface_quality,  
645 - fill_holes,  
646 - keep_largest]  
647 -  
648 - Publisher.sendMessage('Create surface', surface_data) 632 + surface_options = dialog.GetValue()
  633 +
  634 + Publisher.sendMessage('Create surface from index', surface_options)
  635 + dialog.Destroy()
649 636
650 def OnRemove(self): 637 def OnRemove(self):
651 self.parent.listctrl.RemoveSurfaces() 638 self.parent.listctrl.RemoveSurfaces()
invesalius/gui/default_tasks.py
@@ -260,7 +260,7 @@ class UpperTaskPanel(wx.Panel): @@ -260,7 +260,7 @@ class UpperTaskPanel(wx.Panel):
260 Publisher.subscribe(self.OnFoldExport, 'Fold export task') 260 Publisher.subscribe(self.OnFoldExport, 'Fold export task')
261 261
262 def OnOverwrite(self, pubsub_evt): 262 def OnOverwrite(self, pubsub_evt):
263 - self.overwrite = pubsub_evt.data[1] 263 + self.overwrite = pubsub_evt.data['options']['overwrite']
264 264
265 def OnFoldSurface(self, pubsub_evt): 265 def OnFoldSurface(self, pubsub_evt):
266 if not self.overwrite: 266 if not self.overwrite:
invesalius/gui/dialogs.py
@@ -34,6 +34,14 @@ import project as proj @@ -34,6 +34,14 @@ import project as proj
34 import session as ses 34 import session as ses
35 import utils 35 import utils
36 36
  37 +class MaskEvent(wx.PyCommandEvent):
  38 + def __init__(self , evtType, id, mask_index):
  39 + wx.PyCommandEvent.__init__(self, evtType, id,)
  40 + self.mask_index = mask_index
  41 +
  42 +myEVT_MASK_SET = wx.NewEventType()
  43 +EVT_MASK_SET = wx.PyEventBinder(myEVT_MASK_SET, 1)
  44 +
37 class NumberDialog(wx.Dialog): 45 class NumberDialog(wx.Dialog):
38 def __init__(self, message, value=0): 46 def __init__(self, message, value=0):
39 pre = wx.PreDialog() 47 pre = wx.PreDialog()
@@ -964,7 +972,6 @@ class SurfaceDialog(wx.Dialog): @@ -964,7 +972,6 @@ class SurfaceDialog(wx.Dialog):
964 def __init__(self): 972 def __init__(self):
965 wx.Dialog.__init__(self, None, -1, u'Surface generation options') 973 wx.Dialog.__init__(self, None, -1, u'Surface generation options')
966 self._build_widgets() 974 self._build_widgets()
967 - self._bind_wx()  
968 self.CenterOnScreen() 975 self.CenterOnScreen()
969 976
970 def _build_widgets(self): 977 def _build_widgets(self):
@@ -975,12 +982,266 @@ class SurfaceDialog(wx.Dialog): @@ -975,12 +982,266 @@ class SurfaceDialog(wx.Dialog):
975 btn_sizer.AddButton(btn_cancel) 982 btn_sizer.AddButton(btn_cancel)
976 btn_sizer.Realize() 983 btn_sizer.Realize()
977 984
978 - self.alg_types = {0: u'Context aware smoothing', 1: u'Binary'}#,2: u'InVesalius 3.b2'}  
979 - self.cb_types = wx.ComboBox(self, -1, self.alg_types[0],  
980 - choices=[self.alg_types[i] for i in sorted(self.alg_types)],  
981 - style=wx.CB_READONLY) 985 + self.ca = SurfaceMethodPanel(self, -1, True)
  986 +
  987 + self.main_sizer = wx.BoxSizer(wx.VERTICAL)
  988 + self.main_sizer.Add(self.ca, 0, wx.EXPAND|wx.ALL, 5)
  989 + self.main_sizer.Add(btn_sizer, 0, wx.EXPAND | wx.ALL, 5)
  990 +
  991 + self.SetSizer(self.main_sizer)
  992 + self.Fit()
  993 +
  994 + def GetOptions(self):
  995 + return self.ca.GetOptions()
  996 +
  997 + def GetAlgorithmSelected(self):
  998 + return self.ca.GetAlgorithmSelected()
  999 +
  1000 +
  1001 +####################### New surface creation dialog ###########################
  1002 +class SurfaceCreationDialog(wx.Dialog):
  1003 + def __init__(self, parent=None, ID=-1, title=_(u"Surface creation"),
  1004 + size=wx.DefaultSize, pos=wx.DefaultPosition,
  1005 + style=wx.DEFAULT_DIALOG_STYLE, useMetal=False,
  1006 + mask_edited=False):
  1007 +
  1008 + # Instead of calling wx.Dialog.__init__ we precreate the dialog
  1009 + # so we can set an extra style that must be set before
  1010 + # creation, and then we create the GUI object using the Create
  1011 + # method.
  1012 + pre = wx.PreDialog()
  1013 + pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP)
  1014 + pre.Create(parent, ID, title, pos, (50,30), style)
  1015 +
  1016 + # This extra style can be set after the UI object has been created.
  1017 + if 'wxMac' in wx.PlatformInfo and useMetal:
  1018 + self.SetExtraStyle(wx.DIALOG_EX_METAL)
  1019 +
  1020 + # This next step is the most important, it turns this Python
  1021 + # object into the real wrapper of the dialog (instead of pre)
  1022 + # as far as the wxPython extension is concerned.
  1023 + self.PostCreate(pre)
  1024 +
  1025 + self.CenterOnScreen()
  1026 +
  1027 + self.nsd = SurfaceCreationOptionsPanel(self, -1)
  1028 + self.nsd.Bind(EVT_MASK_SET, self.OnSetMask)
  1029 + surface_options_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1,
  1030 + _('Surface creation options')), wx.VERTICAL)
  1031 + surface_options_sizer.Add(self.nsd, 0, wx.EXPAND|wx.ALL, 5)
  1032 +
  1033 + self.ca = SurfaceMethodPanel(self, -1, mask_edited)
  1034 + surface_method_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1,
  1035 + _('Surface creation method')), wx.VERTICAL)
  1036 + surface_method_sizer.Add(self.ca, 0, wx.EXPAND|wx.ALL, 5)
  1037 +
  1038 + btn_ok = wx.Button(self, wx.ID_OK)
  1039 + btn_ok.SetDefault()
  1040 + btn_cancel = wx.Button(self, wx.ID_CANCEL)
  1041 +
  1042 + btnsizer = wx.StdDialogButtonSizer()
  1043 + btnsizer.AddButton(btn_ok)
  1044 + btnsizer.AddButton(btn_cancel)
  1045 + btnsizer.Realize()
  1046 +
  1047 + sizer_panels = wx.BoxSizer(wx.HORIZONTAL)
  1048 + sizer_panels.Add(surface_method_sizer, 0, wx.EXPAND|wx.ALL, 5)
  1049 + sizer_panels.Add(surface_options_sizer, 0, wx.EXPAND|wx.ALL, 5)
  1050 +
  1051 + sizer = wx.BoxSizer(wx.VERTICAL)
  1052 + sizer.Add(sizer_panels, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
  1053 + sizer.Add(btnsizer, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
  1054 +
  1055 + self.SetSizer(sizer)
  1056 + sizer.Fit(self)
  1057 +
  1058 + self.Show()
  1059 +
  1060 + def OnSetMask(self, evt):
  1061 + mask = proj.Project().mask_dict[evt.mask_index]
  1062 + self.ca.mask_edited = mask.was_edited
  1063 + self.ca.ReloadMethodsOptions()
  1064 +
  1065 +
  1066 +
  1067 + def GetValue(self):
  1068 + return {"method": self.ca.GetValue(),
  1069 + "options": self.nsd.GetValue()}
  1070 +
  1071 +
  1072 +class SurfaceCreationOptionsPanel(wx.Panel):
  1073 + def __init__(self, parent, ID=-1):
  1074 + import constants as const
  1075 + import data.surface as surface
  1076 + import project as prj
  1077 +
  1078 + wx.Panel.__init__(self, parent, ID)
  1079 +
  1080 + # LINE 1: Surface name
  1081 + label_surface = wx.StaticText(self, -1, _("New surface name:"))
  1082 +
  1083 + default_name = const.SURFACE_NAME_PATTERN %(surface.Surface.general_index+2)
  1084 + text = wx.TextCtrl(self, -1, "", size=(80,-1))
  1085 + text.SetHelpText(_("Name the surface to be created"))
  1086 + text.SetValue(default_name)
  1087 + self.text = text
  1088 +
  1089 + # LINE 2: Mask of reference
  1090 +
  1091 + # Informative label
  1092 + label_mask = wx.StaticText(self, -1, _("Mask of reference:"))
  1093 +
  1094 + #Retrieve existing masks
  1095 + project = prj.Project()
  1096 + index_list = project.mask_dict.keys()
  1097 + index_list.sort()
  1098 + self.mask_list = [project.mask_dict[index].name for index in index_list]
  1099 +
  1100 + # Mask selection combo
  1101 + combo_mask = wx.ComboBox(self, -1, "", choices= self.mask_list,
  1102 + style=wx.CB_DROPDOWN|wx.CB_READONLY)
  1103 + combo_mask.SetSelection(len(self.mask_list)-1)
  1104 + combo_mask.Bind(wx.EVT_COMBOBOX, self.OnSetMask)
  1105 + if sys.platform != 'win32':
  1106 + combo_mask.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
  1107 + self.combo_mask = combo_mask
  1108 +
  1109 + # LINE 3: Surface quality
  1110 + label_quality = wx.StaticText(self, -1, _("Surface quality:"))
  1111 +
  1112 + choices = const.SURFACE_QUALITY_LIST
  1113 + style = wx.CB_DROPDOWN|wx.CB_READONLY
  1114 + combo_quality = wx.ComboBox(self, -1, "",
  1115 + choices= choices,
  1116 + style=style)
  1117 + combo_quality.SetSelection(3)
  1118 + if sys.platform != 'win32':
  1119 + combo_quality.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)
  1120 + self.combo_quality = combo_quality
  1121 +
  1122 + # OVERVIEW
  1123 + # Sizer that joins content above
  1124 + flag_link = wx.EXPAND|wx.GROW|wx.ALL
  1125 + flag_button = wx.ALL | wx.EXPAND| wx.GROW
  1126 +
  1127 + fixed_sizer = wx.FlexGridSizer(rows=2, cols=2, hgap=10, vgap=5)
  1128 + fixed_sizer.AddGrowableCol(0, 1)
  1129 + fixed_sizer.AddMany([ (label_surface, 1, flag_link, 0),
  1130 + (text, 1, flag_button, 0),
  1131 + (label_mask, 1, flag_link, 0),
  1132 + (combo_mask, 0, flag_button, 0),
  1133 + (label_quality, 1, flag_link, 0),
  1134 + (combo_quality, 0, flag_button, 0)])
  1135 +
  1136 +
  1137 + # LINES 4 and 5: Checkboxes
  1138 + check_box_holes = wx.CheckBox(self, -1, _("Fill holes"))
  1139 + check_box_holes.SetValue(True)
  1140 + self.check_box_holes = check_box_holes
  1141 + check_box_largest = wx.CheckBox(self, -1, _("Keep largest region"))
  1142 + self.check_box_largest = check_box_largest
  1143 +
  1144 + # OVERVIEW
  1145 + # Merge all sizers and checkboxes
  1146 + sizer = wx.BoxSizer(wx.VERTICAL)
  1147 + sizer.Add(fixed_sizer, 0, wx.TOP|wx.RIGHT|wx.LEFT|wx.GROW|wx.EXPAND, 5)
  1148 + sizer.Add(check_box_holes, 0, wx.RIGHT|wx.LEFT, 5)
  1149 + sizer.Add(check_box_largest, 0, wx.RIGHT|wx.LEFT, 5)
  1150 +
  1151 + self.SetSizer(sizer)
  1152 + sizer.Fit(self)
  1153 +
  1154 + def OnSetMask(self, evt):
  1155 + new_evt = MaskEvent(myEVT_MASK_SET, -1, self.combo_mask.GetSelection())
  1156 + self.GetEventHandler().ProcessEvent(new_evt)
  1157 +
  1158 + def GetValue(self):
  1159 + mask_index = self.combo_mask.GetSelection()
  1160 + surface_name = self.text.GetValue()
  1161 + quality = const.SURFACE_QUALITY_LIST[self.combo_quality.GetSelection()]
  1162 + fill_holes = self.check_box_holes.GetValue()
  1163 + keep_largest = self.check_box_largest.GetValue()
  1164 + return {"index": mask_index,
  1165 + "name": surface_name,
  1166 + "quality": quality,
  1167 + "fill": fill_holes,
  1168 + "keep_largest": keep_largest,
  1169 + "overwrite": False}
  1170 +
  1171 +
  1172 +class CAOptions(wx.Panel):
  1173 + '''
  1174 + Options related to Context aware algorithm:
  1175 + Angle: The min angle to a vertex to be considered a staircase vertex;
  1176 + Max distance: The max distance a normal vertex must be to calculate its
  1177 + weighting;
  1178 + Min Weighting: The min weight a vertex must have;
  1179 + Steps: The number of iterations the smoothing algorithm have to do.
  1180 + '''
  1181 + def __init__(self, parent):
  1182 + wx.Panel.__init__(self, parent, -1)
  1183 + self._build_widgets()
  1184 +
  1185 + def _build_widgets(self):
  1186 + self.angle = floatspin.FloatSpin(self, -1, value=0.7, min_val=0.0,
  1187 + max_val=1.0, increment=0.1,
  1188 + digits=1)
  1189 +
  1190 + self.max_distance = floatspin.FloatSpin(self, -1, value=3.0, min_val=0.0,
  1191 + max_val=100.0, increment=0.1,
  1192 + digits=2)
  1193 +
  1194 + self.min_weight = floatspin.FloatSpin(self, -1, value=0.2, min_val=0.0,
  1195 + max_val=1.0, increment=0.1,
  1196 + digits=1)
  1197 +
  1198 + self.steps = wx.SpinCtrl(self, -1, value='10', min=1, max=100)
  1199 +
  1200 + layout_sizer = wx.FlexGridSizer(rows=4, cols=2, hgap=5, vgap=5)
  1201 + layout_sizer.Add(wx.StaticText(self, -1, _(u'Angle:')), 0, wx.EXPAND)
  1202 + layout_sizer.Add(self.angle, 0, wx.EXPAND)
  1203 + layout_sizer.Add(wx.StaticText(self, -1, _(u'Max. distance:')), 0, wx.EXPAND)
  1204 + layout_sizer.Add(self.max_distance, 0, wx.EXPAND)
  1205 + layout_sizer.Add(wx.StaticText(self, -1, _(u'Min. weight:')), 0, wx.EXPAND)
  1206 + layout_sizer.Add(self.min_weight, 0, wx.EXPAND)
  1207 + layout_sizer.Add(wx.StaticText(self, -1, _(u'N. steps:')), 0, wx.EXPAND)
  1208 + layout_sizer.Add(self.steps, 0, wx.EXPAND)
  1209 +
  1210 + self.main_sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, _('Context aware options')), wx.VERTICAL)
  1211 + self.main_sizer.Add(layout_sizer, 0, wx.EXPAND | wx.ALL, 5)
  1212 + self.SetSizer(self.main_sizer)
  1213 +
  1214 +class SurfaceMethodPanel(wx.Panel):
  1215 + '''
  1216 + This dialog is only shown when the mask whose surface will be generate was
  1217 + edited. So far, the only options available are the choice of method to
  1218 + generate the surface, Binary or `Context aware smoothing', and options from
  1219 + `Context aware smoothing'
  1220 + '''
  1221 + def __init__(self, parent, id, mask_edited=False):
  1222 + wx.Panel.__init__(self, parent, id)
  1223 +
  1224 + self.mask_edited = mask_edited
  1225 + self.alg_types = {_(u'Default'): 'Default',
  1226 + _(u'Context aware smoothing'): 'ca_smoothing',
  1227 + _(u'Binary'): 'Binary'}
  1228 + self.edited_imp = [_(u'Default'), ]
  1229 +
  1230 + self._build_widgets()
  1231 + self._bind_wx()
982 1232
  1233 + def _build_widgets(self):
983 self.ca_options = CAOptions(self) 1234 self.ca_options = CAOptions(self)
  1235 + self.cb_types = wx.ComboBox(self, -1, _(u'Default'),
  1236 + choices=[i for i in sorted(self.alg_types)
  1237 + if not (self.mask_edited and i in self.edited_imp)],
  1238 + style=wx.CB_READONLY)
  1239 + if self.mask_edited:
  1240 + self.cb_types.SetValue(_(u'Context aware smoothing'))
  1241 + self.ca_options.Enable()
  1242 + else:
  1243 + self.ca_options.Disable()
  1244 +
984 1245
985 method_sizer = wx.BoxSizer(wx.HORIZONTAL) 1246 method_sizer = wx.BoxSizer(wx.HORIZONTAL)
986 method_sizer.Add(wx.StaticText(self, -1, u'Method:'), 0, 1247 method_sizer.Add(wx.StaticText(self, -1, u'Method:'), 0,
@@ -990,7 +1251,6 @@ class SurfaceDialog(wx.Dialog): @@ -990,7 +1251,6 @@ class SurfaceDialog(wx.Dialog):
990 self.main_sizer = wx.BoxSizer(wx.VERTICAL) 1251 self.main_sizer = wx.BoxSizer(wx.VERTICAL)
991 self.main_sizer.Add(method_sizer, 0, wx.EXPAND | wx.ALL, 5) 1252 self.main_sizer.Add(method_sizer, 0, wx.EXPAND | wx.ALL, 5)
992 self.main_sizer.Add(self.ca_options, 0, wx.EXPAND | wx.ALL, 5) 1253 self.main_sizer.Add(self.ca_options, 0, wx.EXPAND | wx.ALL, 5)
993 - self.main_sizer.Add(btn_sizer, 0, wx.EXPAND | wx.ALL, 5)  
994 1254
995 self.SetSizer(self.main_sizer) 1255 self.SetSizer(self.main_sizer)
996 self.Fit() 1256 self.Fit()
@@ -1000,7 +1260,7 @@ class SurfaceDialog(wx.Dialog): @@ -1000,7 +1260,7 @@ class SurfaceDialog(wx.Dialog):
1000 1260
1001 def _set_cb_types(self, evt): 1261 def _set_cb_types(self, evt):
1002 print evt.GetString() 1262 print evt.GetString()
1003 - if self.alg_types[evt.GetSelection()] == self.alg_types[0]: 1263 + if self.alg_types[evt.GetString()] == 'ca_smoothing':
1004 self.ca_options.Enable() 1264 self.ca_options.Enable()
1005 else: 1265 else:
1006 self.ca_options.Disable() 1266 self.ca_options.Disable()
@@ -1008,12 +1268,12 @@ class SurfaceDialog(wx.Dialog): @@ -1008,12 +1268,12 @@ class SurfaceDialog(wx.Dialog):
1008 1268
1009 def GetAlgorithmSelected(self): 1269 def GetAlgorithmSelected(self):
1010 try: 1270 try:
1011 - return self.alg_types[self.cb_types.GetSelection()] 1271 + return self.alg_types[self.cb_types.GetValue()]
1012 except KeyError: 1272 except KeyError:
1013 return self.alg_types[0] 1273 return self.alg_types[0]
1014 1274
1015 def GetOptions(self): 1275 def GetOptions(self):
1016 - if self.GetAlgorithmSelected() == self.alg_types[0]: 1276 + if self.GetAlgorithmSelected() == 'ca_smoothing':
1017 options = {'angle': self.ca_options.angle.GetValue(), 1277 options = {'angle': self.ca_options.angle.GetValue(),
1018 'max distance': self.ca_options.max_distance.GetValue(), 1278 'max distance': self.ca_options.max_distance.GetValue(),
1019 'min weight': self.ca_options.min_weight.GetValue(), 1279 'min weight': self.ca_options.min_weight.GetValue(),
@@ -1022,4 +1282,20 @@ class SurfaceDialog(wx.Dialog): @@ -1022,4 +1282,20 @@ class SurfaceDialog(wx.Dialog):
1022 options = {} 1282 options = {}
1023 return options 1283 return options
1024 1284
  1285 + def GetValue(self):
  1286 + algorithm = self.GetAlgorithmSelected()
  1287 + options = self.GetOptions()
1025 1288
  1289 + return {"algorithm": algorithm,
  1290 + "options": options}
  1291 +
  1292 + def ReloadMethodsOptions(self):
  1293 + self.cb_types.Clear()
  1294 + self.cb_types.AppendItems([i for i in sorted(self.alg_types)
  1295 + if not (self.mask_edited and i in self.edited_imp)])
  1296 + if self.mask_edited:
  1297 + self.cb_types.SetValue(_(u'Context aware smoothing'))
  1298 + self.ca_options.Enable()
  1299 + else:
  1300 + self.cb_types.SetValue(_(u'Default'))
  1301 + self.ca_options.Disable()
invesalius/gui/task_slice.py
@@ -155,9 +155,19 @@ class InnerTaskPanel(wx.Panel): @@ -155,9 +155,19 @@ class InnerTaskPanel(wx.Panel):
155 options = dlgs.GetOptions() 155 options = dlgs.GetOptions()
156 else: 156 else:
157 return 157 return
158 - Publisher.sendMessage('Create surface from index',  
159 - (self.GetMaskSelected(),  
160 - overwrite, algorithm, options)) 158 +
  159 + mask_index = sl.current_mask.index
  160 + method = {'algorithm': 'Default',
  161 + 'options': options}
  162 + srf_options = {"index": mask_index,
  163 + "name": '',
  164 + "quality": _('Optimal *'),
  165 + "fill": False,
  166 + "keep_largest": False,
  167 + "overwrite": overwrite}
  168 +
  169 + Publisher.sendMessage('Create surface from index',
  170 + {'method': method, 'options': srf_options})
161 Publisher.sendMessage('Fold surface task') 171 Publisher.sendMessage('Fold surface task')
162 else: 172 else:
163 dlg.InexistentMask() 173 dlg.InexistentMask()
invesalius/gui/task_surface.py
@@ -23,6 +23,7 @@ import wx.lib.hyperlink as hl @@ -23,6 +23,7 @@ import wx.lib.hyperlink as hl
23 from wx.lib.pubsub import pub as Publisher 23 from wx.lib.pubsub import pub as Publisher
24 24
25 import constants as const 25 import constants as const
  26 +import data.slice_ as slice_
26 import gui.dialogs as dlg 27 import gui.dialogs as dlg
27 import gui.widgets.foldpanelbar as fpb 28 import gui.widgets.foldpanelbar as fpb
28 import gui.widgets.colourselect as csel 29 import gui.widgets.colourselect as csel
@@ -30,7 +31,6 @@ import gui.widgets.platebtn as pbtn @@ -30,7 +31,6 @@ import gui.widgets.platebtn as pbtn
30 import project as prj 31 import project as prj
31 import utils as utl 32 import utils as utl
32 33
33 -  
34 #INTERPOLATION_MODE_LIST = ["Cubic", "Linear", "NearestNeighbor"] 34 #INTERPOLATION_MODE_LIST = ["Cubic", "Linear", "NearestNeighbor"]
35 MIN_TRANSPARENCY = 0 35 MIN_TRANSPARENCY = 0
36 MAX_TRANSPARENCY = 100 36 MAX_TRANSPARENCY = 100
@@ -131,7 +131,10 @@ class InnerTaskPanel(wx.Panel): @@ -131,7 +131,10 @@ class InnerTaskPanel(wx.Panel):
131 131
132 def OnLinkNewSurface(self, evt=None): 132 def OnLinkNewSurface(self, evt=None):
133 #import gui.dialogs as dlg 133 #import gui.dialogs as dlg
134 - dialog = dlg.NewSurfaceDialog(self, -1, _('InVesalius 3 - New surface')) 134 + sl = slice_.Slice()
  135 + dialog = dlg.SurfaceCreationDialog(self, -1,
  136 + _('InVesalius 3 - New surface'),
  137 + mask_edited=sl.current_mask.was_edited)
135 138
136 try: 139 try:
137 if dialog.ShowModal() == wx.ID_OK: 140 if dialog.ShowModal() == wx.ID_OK:
@@ -142,26 +145,29 @@ class InnerTaskPanel(wx.Panel): @@ -142,26 +145,29 @@ class InnerTaskPanel(wx.Panel):
142 ok = 1 145 ok = 1
143 146
144 if (ok): 147 if (ok):
145 - # Retrieve information from dialog  
146 - (mask_index, surface_name, surface_quality, fill_holes,\  
147 - keep_largest) = dialog.GetValue()  
148 -  
149 - # Retrieve information from mask  
150 - proj = prj.Project()  
151 - mask = proj.mask_dict[mask_index]  
152 -  
153 - # Send all information so surface can be created  
154 - surface_data = [proj.imagedata,  
155 - mask.colour,  
156 - mask.threshold_range,  
157 - mask.edited_points,  
158 - False, # overwrite  
159 - surface_name,  
160 - surface_quality,  
161 - fill_holes,  
162 - keep_largest]  
163 -  
164 - Publisher.sendMessage('Create surface', surface_data) 148 + ## Retrieve information from dialog
  149 + #(mask_index, surface_name, surface_quality, fill_holes,\
  150 + #keep_largest) = dialog.GetValue()
  151 +
  152 + ## Retrieve information from mask
  153 + #proj = prj.Project()
  154 + #mask = proj.mask_dict[mask_index]
  155 +
  156 + ## Send all information so surface can be created
  157 + #surface_data = [proj.imagedata,
  158 + #mask.colour,
  159 + #mask.threshold_range,
  160 + #mask.edited_points,
  161 + #False, # overwrite
  162 + #surface_name,
  163 + #surface_quality,
  164 + #fill_holes,
  165 + #keep_largest]
  166 +
  167 +
  168 + surface_options = dialog.GetValue()
  169 +
  170 + Publisher.sendMessage('Create surface from index', surface_options)
165 dialog.Destroy() 171 dialog.Destroy()
166 if evt: 172 if evt:
167 evt.Skip() 173 evt.Skip()