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

#
#$Header: /opt/cvs/Volume/Pvv/cropCommands.py,v 1.1.1.1 2003/05/23 18:03:19 annao Exp $
#
#$Id: cropCommands.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, InputForm, CallBackFunction
from mglutil.gui.BasicWidgets.Tk.customizedWidgets import ExtendedSliderWidget
from mglutil.gui.BasicWidgets.Tk.customizedWidgets import SliderWidget
import Tkinter
from string import split
volGeom = None

class CropCommand(Command):
     """Command to create and define a crop box. Cropping removes parts
     of the volume using box shapes. The crop box comprises three sets of
     parallel planes (Xmin and Xmax, Ymin and Ymax, Zmin and Zmax)
     that define three slabs(SlabX, SlabY and SlabZ).
     Cropping options:
     - Subvolume: a subvolume defined by the three slabs is displayed;
     - Cross: a subvolume between any two pairs of orthogonal clipping
     planes is displayed;
     - Inverted Cross (the inverse of Cross):a fraction of the volume
     outside of at least two pairs of orthogonal clipping planes is
     displayed;
     - Fence: a fraction of the volume between any pair of
     orthogonal clipping plaines is displayed;
     - Inverted Fence: a fraction of the volume otside of all
     orthogonal clipping planes is displayed.
     The crop box (CB) size can be set by using the GUI or the following
     combination of keyboard and mouse buttons:
     'Control' + mouse button 1, 2 or 3 are used to change the CB size
     along X, Y and Z respectively;
     'meta' + mouse button 3 change the CB size in all directions;
     'meta' + button 1 moves the CB inside the volume."""

     
     def __init__(self):
          Command.__init__(self)
          self.isDisplayed = 0
          self.firstCreated = 0
          self.currentValues = ['SubVolume', 0, 0, 0, 0, 0, 0]

     def onAddCmdToViewer(self):
          self.firstUse = 1
          cropBox = volGeom.cropBox
          self.ask_updateSliders = 1
          cropBox.callbackFunc.append(self.updateSliders)
          volGeom.onaddVolume_list.append(self)
          crop = cropBox.crop
          self.flags = {'off':           crop.kDisable,
                        'SubVolume':     crop.kSubVolume,
                        '3DCross':       crop.k3DCross,
                        '3DCrossInvert': crop.k3DCrossInvert,
                        '3DFence':       crop.k3DFence,
                        '3DFenceInvert': crop.k3DFenceInvert }
          self.checkvar = Tkinter.IntVar()
          self.checkvar.set(1)
          self.log = 0

     def OnAddVolumeToViewer(self):
          """Called when a new volume is loaded.
          Updates the input form (GUI)."""
          
          if self.cmdForms.has_key('crop'): 
               sizeX, sizeY, sizeZ = volGeom.volume.GetSize()
               cropBox = volGeom.cropBox
               self.currentValues[1:] = [cropBox.xmin, cropBox.xmax,
                                         cropBox.ymin, cropBox.ymax,
                                         cropBox.zmin, cropBox.zmax]
               names = ['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax']
               labels = ['Xmin', 'Xmax', 'Ymin', 'Ymax', 'Zmin', 'Zmax']
               coms = [self.getXValue, self.getYValue, self.getZValue]
               maxval = [sizeX-1, sizeX-1, sizeY-1, sizeY-1,
                         sizeZ-1, sizeZ-1]
               left = 10
               right = 10
               width = 200
               maxwidth = max(maxval)+left+right
               if width < maxwidth : width = maxwidth
               for i in range(6):
                    w = self.idf.entryByName[names[i]]['widget']
                    w.configure(width=width, maxval=maxval[i])
                    w.set(self.currentValues[i+1], update=0)

          return

     def updateSliders(self, crop):
          """Called when the GUI is displayed but the crop size is set
          by keyboard/mouse manipulation."""
          
          if not self.cmdForms.has_key('crop'): return
          if self.isDisplayed == 0: return
          names = ['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax']
          vals = [crop.xmin, crop.xmax, crop.ymin, crop.ymax,
                  crop.zmin, crop.zmax]
          currentValues = self.currentValues
          ent = self.idf.entryByName
          for i in range(6):
               if vals[i] != currentValues[i+1]:
                    ent[names[i]]['widget'].set(vals[i], update=0)
                    currentValues[i+1] = vals[i]
          
     def doit(self, SlabX=None, SlabY=None, SlabZ=None, **kw):
          cropBox = volGeom.cropBox
          crop = cropBox.crop
          switch = kw.get('switch')
          if switch:
               crop.SetFlags(self.flags[switch])
               self.currentValues[0] = switch
          if SlabX:
               Xmin, Xmax = SlabX
               cropBox.xmin = Xmin
               cropBox.xmax = Xmax
               cropBox.dx = Xmax - Xmin
               crop.SetXSlab(Xmin, Xmax)
               self.currentValues[1] = Xmin
               self.currentValues[2] = Xmax
          if SlabY:
               Ymin, Ymax = SlabY
               cropBox.ymin = Ymin
               cropBox.ymax = Ymax
               cropBox.dy = Ymax - Ymin
               crop.SetYSlab(Ymin, Ymax)
               self.currentValues[3] = Ymin
               self.currentValues[4] = Ymax
          if SlabZ:
               Zmin, Zmax = SlabZ
               cropBox.zmin = Zmin
               cropBox.zmax = Zmax
               cropBox.dz = Zmax - Zmin
               crop.SetZSlab(Zmin, Zmax)
               self.currentValues[5] = Zmin
               self.currentValues[6] = Zmax
          self.vf.GUI.VIEWER.Redraw()
          
     def __call__(self, SlabX=None, SlabY=None, SlabZ=None, **kw):
          """None <- Crop(SlabX=None, SlabY=None, SlabZ=None, **kw)
          Specify the crop box size:
          SlabX, SlabY, SlabZ - (min, max) tuples - X, Y, Z coordinates,
          in object coordinate space, that define the cropping slabs
          (max value must be grater than or equal to the min value).
          Crop type is set using  'switch' key word(e.g switch = 'off'),
          can be one of the following:
          'SubVolume', '3DCross', '3DCrossInvert', '3DFence',
          '3DFenceInvert' or 'off'.
          """
          has_form = self.cmdForms.has_key('crop')
          if SlabX:
               assert len(SlabX) == 2
               assert SlabX[0]<SlabX[1]
               if has_form:
                    ent=self.idf.entryByName
                    ent['xmin']['widget'].set(SlabX[0], update=0)
                    ent['xmax']['widget'].set(SlabX[1], update=0)
          if SlabY:
               assert len(SlabY) == 2
               assert SlabY[0]<SlabY[1]
               if has_form:
                    ent=self.idf.entryByName
                    ent['ymin']['widget'].set(SlabY[0], update=0)
                    ent['ymax']['widget'].set(SlabY[1], update=0)
          if SlabZ:
               assert len(SlabZ) == 2
               assert SlabZ[0]<SlabZ[1]
               if has_form:
                    ent=self.idf.entryByName
                    ent['zmin']['widget'].set(SlabZ[0], update=0)
                    ent['zmax']['widget'].set(SlabZ[1], update=0)
          switch=kw.get('switch')
          if switch:
               flags = self.flags.keys()
               if switch not in flags:
                    raise ValueError("%s is not valid crop type" % switch)
          apply( self.doitWrapper, (SlabX, SlabY, SlabZ),kw )

     def setSwitch(self):
         val =  self.idf.form.checkValues()
         switch = val['switch']
         self.currentValues[0] = switch
         self.doitWrapper(switch=switch)
          
     def getXValue(self,value):
          """The 'xmin' and 'xmax' sliders callback.
          Gets values of the sliders ."""
          
          ebn = self.idf.entryByName
          Xmin = int(ebn['xmin']['widget'].val)
          Xmax = int(ebn['xmax']['widget'].val)
          if Xmax < Xmin:
               ebn['xmin']['widget'].set(Xmax,update=0)
               Xmin = Xmax
          
          self.doitWrapper(SlabX=(Xmin, Xmax),log=self.log)

     def getYValue(self,value):
          """The 'ymin' and 'ymax' sliders callback.
          Gets values of the sliders."""
          
          ebn = self.idf.entryByName
          Ymin = int(ebn['ymin']['widget'].val)
          Ymax = int(ebn['ymax']['widget'].val)
          if Ymax < Ymin:
               ebn['ymin']['widget'].set(Ymax, update=0)
               Ymin = Ymax
          self.doitWrapper(SlabY=(Ymin, Ymax),log=self.log)

     def getZValue(self,value):
          """The 'zmin' and 'zmax' sliders callback.
          Gets values of the sliders."""
          
          ebn = self.idf.entryByName
          Zmin = int(ebn['zmin']['widget'].val)
          Zmax = int(ebn['zmax']['widget'].val)
          if Zmax < Zmin:
               ebn['zmin']['widget'].set(Zmax, update = 0)
               Zmin = Zmax
          self.doitWrapper(SlabZ=(Zmin, Zmax),log=self.log)

     def update_otherforms(self, val):
          """Updates GUI of other commands that display
          the cropping box size."""
          
          cropBox = volGeom.cropBox
          for f in cropBox.callbackFunc:
               if f is not self.updateSliders:
                    f(cropBox)
         
     def dismiss_cb(self):
          """Withdraws the GUI."""
          self.cmdForms['crop'].withdraw()
          self.isDisplayed=0

     def guiCallback(self):
          """Creates the input form. Called every time the 'Crop'
          button is pressed """
          
          if not volGeom.volume:
               print "volume has not been loaded"
               return
          if self.isDisplayed:
               self.idf.form.lift()
               return
          self.isDisplayed=1
          recreated = 1
          if self.cmdForms.has_key('crop'):
               recreated = 0
          val = self.showForm('crop', modal = 0, blocking=0)
          if recreated:
               labelfnt = ('arial', 12, 'bold')
               numfnt = ('arial', 8, 'bold')
               names = [('xmin', 'red'), ('xmax', 'red'),
                        ('ymin', 'green'), ('ymax', 'green'),
                        ('zmin', 'blue'), ('zmax', 'blue')]
               for name, color in names:
                    w = self.idf.entryByName[name]['widget']
                    w.configure(labelfont=labelfnt)
                    w.configure(cursorfont=numfnt)
                    w.draw.itemconfigure(w.cursor, fill=color)
                    Tkinter.Widget.bind(w.draw, "<ButtonRelease-1>",
                                        self.update_otherforms)
          else:
               self.updateSliders(volGeom.cropBox)

     def buildFormDescr(self,formName):
          """Creates an input form description."""
          
          self.firstCreated = 1
          sizeX, sizeY, sizeZ = volGeom.volume.GetSize()
          cropBox = volGeom.cropBox
          self.currentValues[1:] = [cropBox.xmin, cropBox.xmax,
                                    cropBox.ymin, cropBox.ymax,
                                    cropBox.zmin, cropBox.zmax]
          names = ['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax']
          labels = ['Xmin', 'Xmax', 'Ymin', 'Ymax', 'Zmin', 'Zmax']
          cropOpts = volGeom.cropOpts
          #cropOpts = ['off','SubVolume', '3DCross',
          #            '3DCrossInvert','3DFence', '3DFenceInvert'] #'vli'
          # cropOpts = ['off','SubVolume'] # 'utvolgeom'
          coms = [self.getXValue, self.getYValue, self.getZValue]
          maxval = [sizeX-1, sizeX-1, sizeY-1, sizeY-1, sizeZ-1, sizeZ-1]
          idf = self.idf = InputFormDescr(title = 'Crop')
          idf.append({'name': 'switch',
                      'listtext': cropOpts,
                      'widgetType': Tkinter.Radiobutton,
                      'defaultValue': self.currentValues[0],
                      'wcfg': {'command':self.setSwitch},
                      'direction':'row',
                      'gridcfg':{'sticky':'w'}
                      })
          left = 10
          right = 10
          width = 200
          maxwidth = max(maxval)+left+right
          if width < maxwidth : width = maxwidth
          for i in range(6):
               idf.append({'name': names[i],
                           #'widgetType':ExtendedSliderWidget, 'type':int,
                           'widgetType':SliderWidget, 'type':int,
                           'wcfg':{'label': labels[i], 'width':width,
                                   'minval':0, 'maxval':maxval[i],
                                   'left':left, 'right':right,
                                   'incr': 2, 'init': self.currentValues[i+1],
                                   'command':coms[i/2],
                                   'labelsCursorFormat':'%d',
                                   'sliderType':'int',
                                   'immediate': 1,
                                   #'entrypackcfg':{'side':'right'}
                                   },
                           'gridcfg':{'columnspan':2,'sticky':'we'}} )
          idf.append({'widgetType':Tkinter.Checkbutton,
                      'wcfg':{'text':'immediate(sliders)',
                              'variable':self.checkvar,
                              'command': self.set_immediate},
                      'gridcfg':{'column': 0, 'columnspan':2,
                                 'sticky':'w'},
                      })
          idf.append({'widgetType':Tkinter.Button,
                      'wcfg': {'text': 'Dismiss',
                               'relief' : Tkinter.RAISED,
                               'borderwidth' : 3,
                               'command':self.dismiss_cb},
                      'gridcfg':{'columnspan':2}, 
                      })
          return idf
          

     def set_immediate(self):
          """Called when 'immediate' checkbutton is pressed.
          Alters the behavior of the sliders of the input form (GUI).
          In immediate mode the slider's callback is called
          with every cursor movement. In nonimmediate mode the
          callback is called after the mouse button is released. """
          
          val = self.checkvar.get()
          names = ['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax']
          ent = self.idf.entryByName
          for name in names:
               w = ent[name]['widget']
               w.configure(immediate=val)
               if val == 0: # not immediate
                    w.configure(incr=1)
                    w.AddCallback(self.update_otherforms)
                    self.firstCreated = 0
                    self.log = 1
               else:  # immediate
                    w.configure(incr=2)
                    if not self.firstCreated:
                         # update_otherforms has been
                         # added to the sliders callbacks
                         w.RemoveCallback(self.update_otherforms)
                    self.log = 0
                    

commandGUI = CommandGUI()
commandList = [{'name':'', 'cmd': None, 'gui': None}]
menuName = 'Crop'
menuIndex = 4
commandNames = {'vli':'CropCommand', 'utvolren': 'CropCommand'}


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




