#############################################################################
#
# Author: Anna Omelchenko
#
# Copyright: M. Sanner TSRI
#
#############################################################################

#
#$Header: /opt/cvs/Volume/Pvv/cutplaneCommands.py,v 1.1.1.1 2003/05/23 18:03:19 annao Exp $
#
#$Id: cutplaneCommands.py,v 1.1.1.1 2003/05/23 18:03:19 annao Exp $
#

from ViewerFramework.VFCommand import Command, CommandGUI
from mglutil.gui.InputForm.Tk.gui import InputFormDescr
from mglutil.gui.BasicWidgets.Tk.customizedWidgets import ExtendedSliderWidget
import Tkinter
import Pmw
try:
     from Volume.Renderers.VLI import vli
except:
     pass
volGeom = None

class VLICutPlane(Command):
     """Command to add/remove a cut plane to/from the context.
     The command input form allows to set the cut plane orientation
     (plane normal vector(A,B,C)) and distance(D) from
     the origin to the plane in world space. The cut plane is
     defined by equation:
            Ax + By + Cz + d >= 0.
     The user can set the cut plane to render voxels inside or
     outside of the slice, a fall-off distance (within which the opacity
     falls to zero) and  thickness of the plane.
     The thickness determins two parrallel planes and grows
     in the direction of --D, or toward the origin in world space."""

     def __init__(self):
          Command.__init__(self)
          self.orient = [1.0, 1.0, 1.0]
          self.isDisplayed=0
          self.cutplane = None
          
     def checkDependencies(self):
          from Volume.Renderers.VLI import vli
          
     def onAddCmdToViewer(self):
          self.cpFlags = [vli.VLICutPlane.kInside,
                          vli.VLICutPlane.kOutside] # same as [0,1]
          self.flag = Tkinter.IntVar()
          self.flag.set(self.cpFlags[1])
          self.cpAddRemove = Tkinter.IntVar()
          self.cpAddRemove.set(0)
          self.cpwidth = 0
          self.distance = 0
          self.falloff = 0
          if not volGeom.context: return
          self.createCutPlane()
          
     def doit(self, **kw):
          """Adds a cutplane to the context """
          
          if not volGeom.volume:
               print "VLI volume has not been loaded"
               return
          
          switch = kw.get('switch')
          if switch :
               if switch == 'add':
                    if not self.cutplane:
                         self.createCutPlane()
                    else : 
                         self.setCutPlane()
                         self.cutplane.SetThickness(self.cpwidth)
                         self.cutplane.SetFallOff(self.flag.get())
                    volGeom.context.AddCutPlane(self.cutplane)
##                      print "Cut Plane created with parameters:"
##                      print "orientation: ", self.orient
##                      print "distance: ", self.distance
##                      print "fall off: ", self.falloff
##                      print "flag: ", self.flag.get(), "(0-inside, 1-outside)"
               elif switch == 'remove':
                    if not self.cutplane:
                         print "Cut plane has not been created "
                         return
                    else :
                         volGeom.context.RemoveCutPlane(self.cutplane)
               else:
                    print "Wrong value for switch keyword: ", switch
                    print "'add' or 'remove' expected"
          flag = kw.get('flag')
          if flag:
               if not self.cutplane: self.createCutPlane()
               if flag == "inside":
                    status = self.cutplane.SetFlags(self.cpFlags[0])
               elif flag == "outside":
                    status = self.cutplane.SetFlags(self.cpFlags[1])
               else:
                    print "Wrong value for flag keyword: ", flag
                    print "'inside' or 'outside' expected"
               if status != vli.kVLIOK:
                    print "status SetFlags: ", status
                    
          vec = kw.get('vector')
          if vec:
               assert len(vec) == 3
               if vec[0] == vec[1] == vec[2] == 0:
                    print "cannot accept vector=(0,0,0)"
                    return
               if not self.cutplane: self.createCutPlane()
               status = self.cutplane.SetPlane(vec[0],vec[1],vec[2],
                                               self.distance)
               if status != vli.kVLIOK:
                    print "status SetPlane: ", status
               
          width = kw.get('width')
          if width is not None:
               if not self.cutplane: self.createCutPlane()
               self.cpwidth = width
               status = self.cutplane.SetThickness(width)
               if status != vli.kVLIOK:
                    print "status SetThickness: ", status

          dist = kw.get('distance')
          if dist is not None:
               if not self.cutplane: self.createCutPlane()
               v=self.orient
               self.distance = dist
               status = self.cutplane.SetPlane(v[0],v[1],v[2],dist)
               if status != vli.kVLIOK:
                    print "status SetPlane(distance): ", status

          fo = kw.get('falloff')
          if fo is not None:
              if not self.cutplane: self.createCutPlane()
              self.falloff = fo
              status = self.cutplane.SetFallOff(fo)
              if status != vli.kVLIOK:
                    print "status SetFallOff: ", status
          self.vf.GUI.VIEWER.Redraw()
          
     def setupUndoBefore(self,  **kw):
          pass

     def __call__(self, **kw):
          """Void <- CutPlane(**kw)
          to add/remove a cut plane:
          switch = 'add' or switch = 'remove';
          set cut plane's parameters:
          vector = (x,y,z) - a vector perpendicular to the plane;
          distance = d - distance from the origin to the plane in world space;
          falloff = fo - fall-off value;
          width = w - thickness of the plane;
          flag = 'inside', or flag = 'outside' - to render the voxels
          inside or outside of the plane. """

          apply(self.doitWrapper, (), kw)
          

     def createCutPlane(self):
          config = vli.VLIConfiguration() 
          cont = volGeom.context
          if cont.GetCutPlaneCount() < config.GetMaxCutPlanes() :
               self.cutplane = vli.VLICutPlane_Create(self.orient[0],
                                                      self.orient[1],
                                                      self.orient[2],
                                                      self.distance,
                                                      self.cpwidth)
               self.cutplane.SetFlags(self.flag.get())
          else:
               self.cutplane = cont.GetCutPlane(0)

     def setCutPlane(self):
          """Sets cutplane parameters"""
          self.cutplane.SetPlane(self.orient[0], self.orient[1],
                                 self.orient[2], self.distance)
           
     def dismiss_cb(self):
          self.idf.form.destroy()
          self.isDisplayed=0


     def AddRemove_cb(self):
          """called when 'Add/Remove Cut Plane' checkbutton is pressed"""
          if self.cpAddRemove.get():
               val =  self.form.checkValues()
               self.orient = [float(val['x']), float(val['y']),
                              float(val['z'])]
               self.distance = val['distance']
               self.cpwidth = val['cpwidth']
               self.falloff = val['falloff']
               self.doitWrapper(switch = 'add')
          else:
               self.doitWrapper(switch = 'remove')
               
     def setFlags_cb(self):
          """called when 'Inside' or 'Outside' checkbuttons are pressed"""
          
          if self.flag.get() == self.cpFlags[0]:
               fl = "inside"
          elif self.flag.get() == self.cpFlags[1]:
               fl = "outside"
          else: fl = "other"
          self.doitWrapper(flag = fl) 

     def setOrientation_cb(self):
          """called when 'Set' button is pressed. Checks values for
          the orientation vector """
          
          val =  self.form.checkValues()
          x,y,z = float(val['x']),float(val['y']),float(val['z'])
          if x==0.0 and y==0.0 and z== 0.0:
               x=1.0
               self.idf.entryByName['x']['widget'].setentry(x)
          self.orient = [x,y,z]
          self.doitWrapper(vector = self.orient)   

     def setDistance_cb(self, value):
          """Called when the value of 'Distance' slider is changed """
          self.doitWrapper(distance = value)

     def setWidth_cb(self, value):
          """Called when the value of 'Width' slider is changed """  
          self.doitWrapper(width = value)

     def setFallOff_cb(self, value):
          """Called when the value of 'Fall Off' slider is changed """
          self.doitWrapper(falloff = value)
        
     def guiCallback(self):
          """Creates an Input Form (GUI). Called every time the 'CutPlane'
          button is pressed """
          
          if not volGeom.volume:
               print "VLI volume has not been loaded"
               return
          if self.isDisplayed: return
          self.isDisplayed=1
          sizeX, sizeY, sizeZ = volGeom.volume.GetSize()
          left = 10
          right = 10
          width = 200
          maxval = max(sizeX,sizeY,sizeZ)
          maxwidth = maxval+left+right
          if width < maxwidth : width = maxwidth
          idf = self.idf = InputFormDescr(title = 'Cut Plane')
          
          idf.append({'name': 'switch',
                      'widgetType': Tkinter.Checkbutton,
                      'wcfg':{'text': 'Add/Remove Cut Plane',
                            'variable':self.cpAddRemove,
                            'onvalue': 1, 'offvalue':0,
                            'command':self.AddRemove_cb},
                    'gridcfg':{'sticky': 'w','column':0, 'columnspan':4}
                    })
          idf.append({'widgetType': Tkinter.Label,
                      'wcfg': {'text': 'Render voxels in/outside of slice:'},
                      'gridcfg':{'sticky': 'we','column':0,
                                 'columnspan':4, 'pady': 5}
                      })
          
          idf.append({'widgetType': Tkinter.Checkbutton,
                      'wcfg':{'text': 'Inside',
                              'variable':self.flag,
                              'onvalue': self.cpFlags[0],
                              'offvalue':self.cpFlags[1] ,
                              'command':self.setFlags_cb},
                    'gridcfg':{'sticky': 'w','column':0,'columnspan':2}
                    })
          idf.append({'widgetType': Tkinter.Checkbutton,
                      'wcfg':{'text': 'Outside',
                              'variable':self.flag,
                              'onvalue': self.cpFlags[1],
                              'offvalue':self.cpFlags[0],
                              'command': self.setFlags_cb},
                      'gridcfg':{'sticky': 'w','column':2,
                               'columnspan': 2, 'row':-1}
                    })
          idf.append({'widgetType':Tkinter.Label,
                      'wcfg': {'text': 'Orientation(vector normal):'},
                      'gridcfg':{'column':0, 'columnspan':4,
                                 'sticky':'w', 'pady':5} })
          
          ew = 5
          idf.append({'widgetType':Pmw.EntryField,
                      'name':'x',
                      'wcfg':{'labelpos':'w',
                              'label_text':'X: ',
                              'entry_width': ew,
                              'validate':'real',
                              'value':self.orient[0]},
                      'gridcfg':{'sticky':'we', 'pady':5}})
          idf.append({'widgetType':Pmw.EntryField,
                    'name':'y',
                    'wcfg':{'labelpos':'w',
                            'label_text':'Y: ',
                            'entry_width': ew,
                            'validate':'real',
                            'value':self.orient[1]},
                    'gridcfg':{'row':-1, 'sticky':'we', 'pady':5}})
          idf.append({'widgetType':Pmw.EntryField,
                      'name':'z',
                      'wcfg':{'labelpos':'w',
                              'label_text':'Z: ',
                              'entry_width': ew,
                              'validate':'real',
                              'value':self.orient[2]},
                      'gridcfg':{'row':-1, 'sticky':'we', 'pady':5}})
          idf.append({'widgetType':Tkinter.Button,
                      'wcfg': {'text': 'Set',
                               'relief' : Tkinter.RAISED,
                               'borderwidth' : 3,
                               'command': self.setOrientation_cb},
                      'gridcfg':{'row':-1,'sticky': 'w'}, 
                      })
          idf.append( {'name': 'distance',
                       'widgetType':ExtendedSliderWidget,
                       'wcfg':{'label': 'Distance',
                               'minval':-maxval,'maxval':maxval,'width':width,
                               'left':left, 'right':right,
                               'incr': 1.0, 'init': self.distance,
                               'labelsCursorFormat':'%3.1f',
                               'sliderType':'float',
                               'command':self.setDistance_cb,
                               'immediate':0,
                               'entrypackcfg':{'side':'right'}
                               },
                       'gridcfg':{'columnspan':4,'sticky':'ew'}
                       })
          idf.append( {'name': 'cpwidth',
                       'widgetType':ExtendedSliderWidget,
                       'wcfg':{'label': 'Width',
                               'minval':0,'maxval':maxval,'width':width,
                               'left':left, 'right':right,
                               'incr': 0.1, 'init': self.cpwidth,
                               'labelsCursorFormat':'%3.1f',
                               'sliderType':'float',
                               'command':self.setWidth_cb,
                               'immediate':0,
                               'entrypackcfg':{'side':'right'}
                               },
                       'gridcfg':{'columnspan':4,'sticky':'ew'}
                       })
          idf.append( {'name': 'falloff',
                       'widgetType':ExtendedSliderWidget,
                       'wcfg':{'label': 'Fall Off',
                               'minval':0,'maxval':16,'width':width,
                               'left':left, 'right':right,
                               'incr': 1, 'init': self.falloff,
                               'labelsCursorFormat':'%d', 'sliderType':'int',
                               'command': self.setFallOff_cb,
                               'immediate':0,
                               'entrypackcfg':{'side':'right'}
                               },
                       'gridcfg':{'columnspan':4,'sticky':'ew'}
                       })

          idf.append({'widgetType':Tkinter.Button,
                      'wcfg': {'text': 'Dismiss',
                               'relief' : Tkinter.RAISED,
                               'borderwidth' : 3,
                               'command':self.dismiss_cb},
                      'gridcfg':{'columnspan':4,'sticky': 'ew'}, 
                      })
          
          self.form = self.vf.getUserInput(idf, modal = 0, blocking=0)
          names = ['distance', 'cpwidth', 'falloff']
          labelfnt = ('arial', 12, 'bold')
          numfnt = ('arial', 8, 'bold')
          for name in names:
               w = idf.entryByName[name]['widget']
               w.label.configure(font=labelfnt)
               w.draw.itemconfig(w.valueLabel, font=numfnt)

commandGUI = CommandGUI()
menuName = 'CutPlane'
menuIndex = 11
commandNames = {'vli': 'VLICutPlane', 'utvolren': None}
commandList = [{'name':'', 'cmd': None, 'gui':commandGUI}]

def initModule(viewer):
     for dict in commandList:
          if dict['name']:
               viewer.addCommand(dict['cmd'], dict['name'], dict['gui'])




