# Allan Spale
# CS 526 - Project 2

import Tkinter
import tkFont
import time
import os
import sys

import vtk
from vtk.tk.vtkTkRenderWindowInteractor import vtkTkRenderWindowInteractor

global I_TRUE
global I_FALSE

I_TRUE = 1
I_FALSE = 0



#------------------------- GUI BUILDER CLASSES -----------------------------------
"""
This class is more like a factory than an instantiatable class.  It helps
create Tkinter widgets without a lot of repetitive configuration.
"""
class GUIFactory:
    global I_TRUE, I_FALSE
    
    def __init__( self ):
        self.__dictStandardGridSettings = { 'padx':5, 'pady':5, 'sticky':Tkinter.SW}
        self.__defaultFont = tkFont.Font( size=11 )


    def newLabel( self, rootPanel, labelText, dictExtraConfig ):
        l = Tkinter.Label( rootPanel, font=self.__defaultFont, text=labelText )

        if ( self.__isConfigEmpty( dictExtraConfig ) == I_FALSE ):
            l.config( dictExtraConfig )
            
        return l


    def newEntry( self, rootPanel, fieldIdentifier, dictExtraConfig ):
        e = Tkinter.Entry( rootPanel, font=self.__defaultFont, text=fieldIdentifier )

        if ( self.__isConfigEmpty( dictExtraConfig ) == I_FALSE ):
            e.config( dictExtraConfig )

        print "Entry...", e
        return e


    def newButton( self, rootPanel, buttonText, buttonCommand, dictExtraConfig ):
        b = Tkinter.Button( rootPanel, font=self.__defaultFont, text=buttonText,
                            command=buttonCommand )
        
        if ( self.__isConfigEmpty( dictExtraConfig ) == I_FALSE ):
            b.config( dictExtraConfig )

        return b


    def newCheckbox( self, rootPanel, checkText, checkCommand, dictExtraConfig ):
        c = Tkinter.Checkbutton( rootPanel, font=self.__defaultFont, text=checkText,
                                 command=checkCommand, indicatoron=I_FALSE )
        
        if ( self.__isConfigEmpty( dictExtraConfig ) == I_FALSE ):
            c.config( dictExtraConfig )

        return c


    def newRadiobutton( self, rootPanel, radioText, radioCommand, sharedVariable,
                        radioValue, dictExtraConfig ):
        r = Tkinter.Radiobutton( rootPanel, font=self.__defaultFont, text=radioText,
                                 command=radioCommand, variable=sharedVariable,
                                 value=radioValue, indicatoron=I_FALSE )

        if ( self.__isConfigEmpty( dictExtraConfig ) == I_FALSE ):
            r.config( dictExtraConfig )

        return r


    def newSlider( self, rootPanel, sliderCommand, valueStart, valueEnd,
                   dictExtraConfig ):

        dictExtraConfig[ 'from' ] = valueStart
        dictExtraConfig[ 'to' ] = valueEnd
        dictExtraConfig[ 'orient' ] = Tkinter.HORIZONTAL
        dictExtraConfig[ 'showvalue' ] = I_FALSE

        s = Tkinter.Scale( rootPanel )
        s.config( dictExtraConfig )
        
        s.bind( '<B1-Motion>', sliderCommand )
        s.bind( '<B2-Motion>', sliderCommand )
        s.bind( '<Button-1>', sliderCommand )
        s.bind( '<Button-2>', sliderCommand )
        s.bind( '<ButtonRelease-1>', sliderCommand )
        s.bind( '<ButtonRelease-2>', sliderCommand )
        
        return s
    

    def newListbox( self, rootPanel, dictExtraConfig ):
        dictExtraConfig[ 'selectmode' ] = Tkinter.SINGLE

        l = Tkinter.Listbox( rootPanel, font=self.__defaultFont )
        l.config( dictExtraConfig )

        return l
    
        
    def packItem( self, object, gridSettings ):
        items = self.__dictStandardGridSettings.keys()

        for x in items:
            gridSettings[ x ] = self.__dictStandardGridSettings[ x ]

        object.grid( gridSettings )


    def __isConfigEmpty( self, dictConfig ):
        if ( dictConfig == None ):
            return I_TRUE
        elif ( len( dictConfig ) > 0 ):
            return I_FALSE
        else:
            return I_TRUE



"""
This is a composited widget made from Tkinter.Scale and Tkinter.Label.
Class methods are available to allow some level of customizability.
"""
class SliderWithValue:
    global I_TRUE, I_FALSE
    
    def __init__( self, rootPanel, startValue, endValue ):
        guiFactory = GUIFactory()
        #self.count = 0
        self.__root = rootPanel
        self.__frame = Tkinter.Frame( self.__root )
                                 
        self.__label = guiFactory.newLabel( self.__frame, '',
                                            { 'padx':5, 'pady':5,
                                              'anchor':Tkinter.SW,
                                              'width':3,
                                              'justify':Tkinter.RIGHT} )
        self.__label.grid(row=0, rowspan=1, column=0, columnspan=3,
                          sticky=Tkinter.NW )

        filler = guiFactory.newLabel( self.__frame, ' ', None )
        filler.grid( row=0, rowspan=1, column=3, columnspan=1 )
                            
        self.__slider = guiFactory.newSlider( self.__frame, self.__updateLabel,
                                              startValue, endValue,
                                              {} )
        self.__slider.grid( row=0, rowspan=1, column=4, columnspan=3,
                            sticky=Tkinter.NE)
        self.__slider.set( startValue )

        self.__modified = I_FALSE
        self.__sliderValue = startValue
        
        self.__updateLabel()


    def addSliderCommandBinding( self, callback ):
        self.__slider.bind( '<B1-Motion>', callback )
        self.__slider.bind( '<B2-Motion>', callback )
        self.__slider.bind( '<Button-1>', callback )
        self.__slider.bind( '<Button-2>', callback )
        self.__slider.bind( '<ButtonRelease-1>', callback )
        self.__slider.bind( '<ButtonRelease-2>', callback )
        

    def configureScale( self, dictConfiguration ):
        self.__slider.config( dictConfiguration )


    def setScaleValue( self, value ):
        self.__slider.set( value )
        self.__updateLabel()

    
    def reset( self ):
        value = self.__slider.cget( 'from' )
        self.__slider.set( value )
        self.__updateLabel()
        self.__modified = I_FALSE

    def configureLabel( self, dictConfiguration ):
        self.__label.config( dictConfiguration )


    def getWidget( self ):
        return self.__frame


    def getValue( self ):
        return self.__slider.get()

    def getValue2( self ):
        return int( self.__label.cget( 'text' ) )


    def getValue3( self ):
        return self.__sliderValue



    def __updateLabel( self, *unusedArgument ):
        #print self.count, self.__valueLabel
        #self.count = self.count+1
        #self.__label = self.__slider.get()
        self.__label.config( text=self.__slider.get() )
        self.__modified = I_TRUE
        self.__sliderValue = self.__slider.get()


    def getModifiedStatus():
        return self.__modified



"""
This is a class that creates a modal dialog.  To prevent confusion of having
the same dialog appear twice (if the user made a selection that would cause
multiple (conflicting) copies of the dialog to appear twice), all dialogs will
be modal.  All dialog boxes will extend this class.
"""
class ModalDialog:
    global I_TRUE, I_FALSE

    
    def __init__( self, topWindow, dialogTitle, useStatusBarFlag ):
        self.__firstTime = I_TRUE
        self.__buildDialog( topWindow, dialogTitle, useStatusBarFlag )

        self.config = {}
        self.config[ 'text' ] = ''
        self.config[ 'foreground' ] = ''
        self.config[ 'background' ] = ''



    def __buildDialog( self, root, title, statusBarFlag ):
        self.__rootWindow = root
        self.__title = title
        self.__statusBarFlag = statusBarFlag
        self.__root = Tkinter.Toplevel( self.__rootWindow )
        self.__root.title( title )
        #self.__root.resizable( 0,0 )
        self.dialog = Tkinter.Frame( self.__root )
        self.dialog.pack()
        

        # constants
        self.B_USE_ERROR_COLOR = I_TRUE
        self.B_NO_ERROR_COLOR = I_FALSE


        """if ( self.__statusBarFlag == I_TRUE ):
            self.__statusBar = Tkinter.Label( self.__root, relief=Tkinter.SUNKEN,
                                              text=' ', justify=Tkinter.LEFT,
                                              anchor=Tkinter.W )
            self.__statusBar.pack()
            self.__statusOriginalBGColor = self.__statusBar.cget( 'background' )
            self.__statusOriginalFGColor = self.__statusBar.cget( 'foreground' )
        """
        self.hide()


    def rebuildDialog( self, newRoot ):
        self.__rootWindow = newRoot
        self.__buildDialog( newRoot, self.__title, self.__statusBarFlag )

        
    def show( self ):
        # modal dialog settings...adopted from "An Introduction to Tkinter"
        print self.__root.state()

        if ( self.__firstTime == I_TRUE ):
            #self.__root.transient( self.__rootWindow )
            self.__firstTime = I_FALSE

        self.__root.deiconify()            
        #self.__root.grab_set() #events sent to this dialog
        #self.__root.focus_set()
        #self.__root.wait_window( self.__root )


    def destroy( self ):
        #self.__root.grab_release()
        self.__root.destroy()


    def hide( self ):
        #self.__root.grab_release()
        #self.__root.withdraw()
        self.__root.iconify()


    def setStatusBarMessage( self, useErrorColorFlag, statusText ):
        if ( self.__statusBarFlag == I_TRUE ):
            self.config[ 'text' ] = statusText

            if ( useErrorColorFlag == I_TRUE ):
                self.config[ 'foreground' ] = '#0000AA'
                self.config[ 'background' ] = '#FFFFFF'
                
            else:
                self.config[ 'foreground' ] = self.__statusOriginalFGColor
                self.config[ 'background' ] = self.__statusOriginalBGColor
    
            self.__statusBar.config( self.config )


    def configStatusBar( self, dictConfiguration ):
        self.__statusBar.config( dictConfiguration )
#------------------------- GUI BUILDER CLASS -----------------------------------



#------------------------- VTK CLASS ----------------------------------------
class VTKRender:
    def __init__(self, frame):

        self.__frame = frame        
        self.NONE = 0
        self.ISO = 10
        self.COMP = 20
        self.MIP = 30

        # ID numbers, not isovalue
        self.SKIN = 100
        self.BONE = 200
        self.OTHER = 300

        self.HEAD = 1000
        self.FEET = 2000


        self.SKIN_ISOSURFACE = 860
        self.BONE_ISOSURFACE = 1250
        self.__otherIso = 1100

        self.__lastActorID = self.NONE
        
        self.readerVolume = vtk.vtkImageReader()
        self.readerVolume.SetDataScalarType( vtk.VTK_UNSIGNED_SHORT )
        self.readerVolume.SetDataScalarType( 5 )
        self.readerVolume.SetDataByteOrder( 0 )
        self.readerVolume.SetFileDimensionality( 3 )
        self.readerVolume.SetDataExtent ( 0,255, 0,255, 0,576)
        self.readerVolume.SetDataSpacing( 1,1,1 )
        self.readerVolume.SetNumberOfScalarComponents( 1 )
        self.readerVolume.SetFileName( "./Female.raw" )

        self.voiFeet = vtk.vtkExtractVOI()
        self.voiFeet.SetInput( self.readerVolume.GetOutput() )
        self.voiFeet.SetVOI( 0,255, 60,255, 500,576 )
        self.voiFeet.SetSampleRate( 1,1,1 )                

        self.voiHead = vtk.vtkExtractVOI()
        self.voiHead.SetInput( self.readerVolume.GetOutput() )
        self.voiHead.SetVOI( 0,255, 60,255, 0,100 )
        self.voiHead.SetSampleRate( 1,1,1 )


        self.compositeFunction = vtk.vtkVolumeRayCastCompositeFunction()

        self.opacityFunctionComp = vtk.vtkPiecewiseFunction()

        for x in range( self.BONE_ISOSURFACE, 5000, 10 ):
            self.opacityFunctionComp.AddPoint( x, 1.0 )#0.3
        for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+200, 5 ):
            self.opacityFunctionComp.AddPoint( y, 0.05 )
        
        #self.opacityFunctionComp.AddPoint( self.BONE_ISOSURFACE, 1.0 )
        #self.opacityFunctionComp.AddPoint( 860, 0.3 )
        self.opacityFunctionComp.ClampingOff()

        self.colorTransferFunctionComp = vtk.vtkColorTransferFunction()
        #self.colorTransferFunctionComp.AddRGBPoint( self.BONE_ISOSURFACE, 1, 1, 1 )
        #self.colorTransferFunctionComp.AddRGBPoint( self.SKIN_ISOSURFACE, 1, 0.547237, 0.319073 )

        for x in range( self.BONE_ISOSURFACE, self.BONE_ISOSURFACE+259, 1 ):
            self.colorTransferFunctionComp.AddRGBPoint( x, 1, 1, 1 )
        for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+100, 1 ):
            self.colorTransferFunctionComp.AddRGBPoint( y, 1, 0.547237, 0.319073 )
        
        self.volumePropertyComp = vtk.vtkVolumeProperty()
        self.volumePropertyComp.SetScalarOpacity( self.opacityFunctionComp )
        self.volumePropertyComp.SetColor( self.colorTransferFunctionComp )
        self.volumePropertyComp.ShadeOn()
        self.volumePropertyComp.SetInterpolationTypeToLinear()

        self.mapperCompHead = vtk.vtkVolumeRayCastMapper()
        self.mapperCompHead.SetVolumeRayCastFunction( self.compositeFunction )
        self.mapperCompHead.SetInput( self.voiHead.GetOutput() )

        self.mapperCompFeet = vtk.vtkVolumeRayCastMapper()
        self.mapperCompFeet.SetVolumeRayCastFunction( self.compositeFunction )
        self.mapperCompFeet.SetInput( self.voiFeet.GetOutput() )

        self.volumeComp = vtk.vtkVolume()
        self.volumeComp.SetOrigin( 0,0,0 )
        self.volumeComp.SetScale( 1,1,1 )
        self.volumeComp.SetMapper( self.mapperCompHead )
        self.volumeComp.SetProperty( self.volumePropertyComp )


        #compositeFunction = vtk.vtkVolumeRayCastCompositeFunction()
        self.compositeFunctionMIP = vtk.vtkVolumeRayCastMIPFunction()
        self.compositeFunctionMIP.SetMaximizeMethodToOpacity()

        self.opacityFunctionMIP = vtk.vtkPiecewiseFunction()
        self.opacityFunctionMIP.AddPoint( self.BONE_ISOSURFACE, 1.0 )
        #opacityFunction.AddPoint( 1200, 1.0 )
        self.opacityFunctionMIP.AddPoint( self.SKIN_ISOSURFACE, 0.3 )
        self.opacityFunctionMIP.ClampingOff()

        for x in range( self.BONE_ISOSURFACE, 5000, 1 ):
            self.opacityFunctionMIP.AddPoint( x, 0.7 )
        for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+10, 1 ):
            self.opacityFunctionMIP.AddPoint( y, 0.5 )


        self.colorTransferFunctionMIP = vtk.vtkColorTransferFunction()
        self.colorTransferFunctionMIP.AddRGBPoint( self.BONE_ISOSURFACE, 1,1,1 )
        #colorTransferFunction.AddRGBPoint( 1200, 1,1,1 )
        self.colorTransferFunctionMIP.AddRGBPoint( self.SKIN_ISOSURFACE, 1, 0.547237, 0.319073 )
        #colorTransferFunction.SetClamping( 2 )

        for x in range( self.BONE_ISOSURFACE, 5000, 100 ):
            self.colorTransferFunctionMIP.AddRGBPoint( x, 1, 1, 1 )
        for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+10, 1 ):
            self.colorTransferFunctionMIP.AddRGBPoint( y, 1, 0.547237, 0.319073 )

        self.volumePropertyMIP = vtk.vtkVolumeProperty()
        self.volumePropertyMIP.SetScalarOpacity( self.opacityFunctionMIP )
        self.volumePropertyMIP.SetColor( self.colorTransferFunctionMIP )
        self.volumePropertyMIP.ShadeOn()
        self.volumePropertyMIP.SetInterpolationTypeToLinear()

        self.mapperMIPHead = vtk.vtkVolumeRayCastMapper()
        self.mapperMIPHead.SetVolumeRayCastFunction( self.compositeFunctionMIP )
        self.mapperMIPHead.SetInput( self.voiHead.GetOutput() )

        self.mapperMIPFeet = vtk.vtkVolumeRayCastMapper()
        self.mapperMIPFeet.SetVolumeRayCastFunction( self.compositeFunctionMIP )
        self.mapperMIPFeet.SetInput( self.voiFeet.GetOutput() )

        self.volumeMIP = vtk.vtkVolume()
        self.volumeMIP.SetOrigin( 0,0,0 )
        self.volumeMIP.SetScale( 1,1,1 )
        self.volumeMIP.SetMapper( self.mapperMIPHead )
        self.volumeMIP.SetProperty( self.volumePropertyMIP )


        #### BONE
        self.contourBoneHead = vtk.vtkMarchingCubes()
        self.contourBoneHead.SetInput( self.voiHead.GetOutput() )
        self.contourBoneHead.ComputeNormalsOn()
        self.contourBoneHead.SetValue( 0, self.BONE_ISOSURFACE )

        self.contourBoneFeet = vtk.vtkMarchingCubes()
        self.contourBoneFeet.SetInput( self.voiFeet.GetOutput() )
        self.contourBoneFeet.ComputeNormalsOn()
        self.contourBoneFeet.SetValue( 0, self.BONE_ISOSURFACE )

        self.geoVolumeBone = vtk.vtkPVGeometryFilter()
        self.geoVolumeBone.SetInput( self.contourBoneHead.GetOutput() )

        self.geoBoneMapper = vtk.vtkPolyDataMapper()
        self.geoBoneMapper.SetInput( self.geoVolumeBone.GetOutput() )
        self.geoBoneMapper.ScalarVisibilityOff()

        self.actorBone = vtk.vtkLODActor()
        self.actorBone.SetNumberOfCloudPoints( 1000000 )
        self.actorBone.SetMapper( self.geoBoneMapper )
        self.actorBone.GetProperty().SetColor( 1, 1, 1 )

        ### SKIN
        self.contourSkinTop = vtk.vtkMarchingCubes()
        self.contourSkinTop.SetInput( self.voiHead.GetOutput() )
        self.contourSkinTop.SetValue( 0, self.SKIN_ISOSURFACE ) #860
        self.contourSkinTop.ComputeNormalsOn()

        self.contourSkinFeet = vtk.vtkMarchingCubes()
        self.contourSkinFeet.SetInput( self.voiFeet.GetOutput() )
        self.contourSkinFeet.ComputeNormalsOn()
        self.contourSkinFeet.SetValue( 0, self.SKIN_ISOSURFACE )

        self.geoVolumeSkin= vtk.vtkPVGeometryFilter()
        self.geoVolumeSkin.SetInput( self.contourSkinTop.GetOutput() )

        self.geoSkinMapper = vtk.vtkPolyDataMapper()
        self.geoSkinMapper.SetInput( self.geoVolumeSkin.GetOutput() )
        self.geoSkinMapper.ScalarVisibilityOff()

        self.actorSkin = vtk.vtkLODActor()
        self.actorSkin.SetNumberOfCloudPoints( 1000000 )
        self.actorSkin.SetMapper( self.geoSkinMapper )
        self.actorSkin.GetProperty().SetColor( 1, 0.547237, 0.319073 )
        self.actorSkin.GetProperty().SetOpacity( 0.3 )


        # Rendering structure taken from VTK Python example given by VTK
        self.renWin = vtk.vtkRenderWindow()
        self.ren = vtk.vtkRenderer()
        self.ren.SetBackground( 0.329412, 0.34902, 0.427451 )
        
        #self.cam = self.ren.GetActiveCamera()
        #self.cam.SetPosition( 0,0,0 )
        self.ren.ResetCameraClippingRange()

        
        self.renWin.AddRenderer(self.ren)
        self.vtkw = vtkTkRenderWindowInteractor(self.__frame,
            rw=self.renWin, width=400, height=400)

        self.renWin.Render()
        self.renWin.PolygonSmoothingOn()
        self.vtkw.Initialize()
        self.vtkw.pack(side="top", fill='both', expand=1)
        self.vtkw.Start()
    

    def showIsosurfaceHead( self ):
        self.__removeOldActorsAndSetNewActor( self.ISO )
        self.geoVolumeSkin.SetInput( self.contourSkinTop.GetOutput() )
        self.geoVolumeBone.SetInput( self.contourBoneHead.GetOutput() )        
        self.ren.AddActor( self.actorSkin )
        self.ren.AddActor( self.actorBone )        

    def showIsosurfaceFeet( self ):
        self.__removeOldActorsAndSetNewActor( self.ISO )
        self.geoVolumeSkin.SetInput( self.contourSkinFeet.GetOutput() )
        self.geoVolumeBone.SetInput( self.contourBoneFeet.GetOutput() )        
        self.ren.AddActor( self.actorSkin )
        self.ren.AddActor( self.actorBone )        

    def showCompHead( self ):
        self.__removeOldActorsAndSetNewActor( self.COMP )
        self.volumeComp.SetMapper( self.mapperCompHead )
        self.ren.AddActor( self.volumeComp )
        
    def showCompFeet( self ):
        self.__removeOldActorsAndSetNewActor( self.COMP )        
        self.volumeComp.SetMapper( self.mapperCompFeet )
        self.ren.AddActor( self.volumeComp )

    def showMIPHead( self ):
        self.__removeOldActorsAndSetNewActor( self.MIP )        
        self.volumeMIP.SetMapper( self.mapperMIPHead )
        self.ren.AddActor( self.volumeMIP )

    def showMIPFeet( self ):
        self.__removeOldActorsAndSetNewActor( self.MIP )        
        self.volumeMIP.SetMapper( self.mapperMIPFeet)        
        self.ren.AddActor( self.volumeMIP )


    def updateSurface( self, dictSettings ):
        if ( dictSettings[ 'tissue' ] == self.SKIN ):
            # ISOSURFACE
            self.actorSkin.GetProperty().SetColor( 
                dictSettings[ 'red' ]/256.0,
                dictSettings[ 'green' ]/256.0,
                dictSettings[ 'blue' ]/256.0 )
            self.actorSkin.GetProperty().SetOpacity(
                dictSettings[ 'opaque' ]/100.0 )
            self.actorSkin.GetMapper().Update()

            # COMPOSITE
            for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+200, 5 ):
                self.opacityFunctionComp.RemovePoint( y )

            for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+100, 1 ):
                self.colorTransferFunctionComp.RemovePoint( y )

            for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+200, 5 ):
                 self.opacityFunctionComp.AddPoint( y,
                    dictSettings[ 'opaque' ]/100.0 )

            for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+100, 1 ):
                 self.colorTransferFunctionComp.AddRGBPoint( y,
                    dictSettings[ 'red' ]/256.0,
                    dictSettings[ 'green' ]/256.0,
                    dictSettings[ 'blue' ]/256.0 )


            self.volumeComp.GetMapper().Update()

            # MIP
            for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+10, 1 ):
                self.opacityFunctionMIP.RemovePoint( y )

            for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+10, 1 ):    
                self.colorTransferFunctionMIP.RemovePoint( y )

            for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+10, 1 ):
                self.opacityFunctionMIP.AddPoint( y,
                    dictSettings[ 'opaque' ]/100.0 )

            for y in range( self.SKIN_ISOSURFACE,self.SKIN_ISOSURFACE+10, 1 ):    
                self.colorTransferFunctionMIP.AddRGBPoint( y,
                    dictSettings[ 'red' ]/256.0,
                    dictSettings[ 'green' ]/256.0,
                    dictSettings[ 'blue' ]/256.0 )                                               

            self.volumeMIP.GetMapper().Update()

            
        elif ( dictSettings[ 'tissue' ] == self.BONE ):
            # ISOSURFACE
            self.actorBone.GetProperty().SetColor(
                dictSettings[ 'red' ]/256.0,
                dictSettings[ 'green' ]/256.0,
                dictSettings[ 'blue' ]/256.0 )
            self.actorBone.GetProperty().SetOpacity(
                dictSettings[ 'opaque' ]/100.0 )
            self.actorBone.GetMapper().Update()

            # COMPOSITE
            for x in range( self.BONE_ISOSURFACE, 5000, 10 ):
                self.opacityFunctionComp.RemovePoint( x )

            for x in range( self.BONE_ISOSURFACE, self.BONE_ISOSURFACE+259, 1 ):                
                self.colorTransferFunctionComp.RemovePoint( x )

            for x in range( self.BONE_ISOSURFACE, 5000, 10 ):
                self.opacityFunctionComp.AddPoint( x,
                    dictSettings[ 'opaque' ]/100.0 )

            for x in range( self.BONE_ISOSURFACE, self.BONE_ISOSURFACE+259, 1 ):                
                self.colorTransferFunctionComp.AddRGBPoint( x,
                    dictSettings[ 'red' ]/256.0,
                    dictSettings[ 'green' ]/256.0,
                    dictSettings[ 'blue' ]/256.0 )

            self.volumeComp.GetMapper().Update()

            # MIP
            for x in range( self.BONE_ISOSURFACE, 5000, 1 ):
                self.opacityFunctionMIP.RemovePoint( x )

            for x in range( self.BONE_ISOSURFACE, 5000, 100 ):    
                self.colorTransferFunctionMIP.RemovePoint( x )

            for x in range( self.BONE_ISOSURFACE, 5000, 1 ):
                self.opacityFunctionMIP.AddPoint( x,
                    dictSettings[ 'opaque' ]/100.0 )

            for x in range( self.BONE_ISOSURFACE, 5000, 100 ):                
                self.colorTransferFunctionMIP.AddRGBPoint( x,
                    dictSettings[ 'red' ]/256.0,
                    dictSettings[ 'green' ]/256.0,
                    dictSettings[ 'blue' ]/256.0 )

            self.volumeMIP.GetMapper().Update()


    def updateSamplingRate( self, xSample, ySample, zSample ):
        self.voiHead.SetSampleRate( xSample,ySample,zSample )
        self.voiFeet.SetSampleRate( xSample,ySample,zSample )
        self.volumeComp.SetScale( xSample,ySample,zSample )
        self.volumeMIP.SetScale( xSample,ySample,zSample )

        self.actorSkin.GetMapper().Update()
        self.actorBone.GetMapper().Update()
        self.volumeComp.GetMapper().Update()
        self.volumeMIP.GetMapper().Update()        


    def __removeOldActorsAndSetNewActor( self, actorID ):
        if ( self.__lastActorID == self.ISO ):
            self.ren.RemoveActor( self.actorSkin )
            self.ren.RemoveActor( self.actorBone )
        elif ( self.__lastActorID == self.COMP ):
            self.ren.RemoveActor( self.volumeComp )
        elif ( self.__lastActorID == self.MIP ):
            self.ren.RemoveActor( self.volumeMIP )

        self.__lastActorID = actorID


    def destroy( self ):
        del self.ren
        del self.renWin
        del self.vtkw        
        
#------------------------- VTK CLASS ----------------------------------------



#-------------- VOLUME OF INTEREST DIALOG -------------------------------------
    
class DialogOptionVOI( ModalDialog ):
    def __init__( self, topWindow, vtkWindow ):
        ModalDialog.__init__( self, topWindow, 'Global Sampling Level', I_TRUE )

        self.__vtkWindow = vtkWindow

        # Constants
        self.I_ERROR = -5
        self.I_OK = 0
        self.I_CANCEL = -10

        self.__action = self.I_CANCEL

        self.__xSampleRate = 1.0
        self.__ySampleRate = 1.0
        self.__zSampleRate = 1.0

        self.__buildDialog()

      
    def __setErrorMessage( self, message ):
        self.setStatusBarMessage( B_USE_ERROR_COLOR, message )

        
    def __buildDialog( self ):
        guiFactory = GUIFactory()
        
        gridSettings = { 'padx':5, 'pady':5, 'sticky':Tkinter.SW,
                         'row':0, 'rowspan':1, 'column':0, 'columnspan':1 }
        
        labelX = guiFactory.newLabel( self.dialog, 'X', {'justify':Tkinter.RIGHT} )
        gridSettings[ 'sticky' ] = Tkinter.NW
        gridSettings[ 'row' ] = 0
        gridSettings[ 'column' ] = 0
        gridSettings[ 'columnspan' ] = 1
        guiFactory.packItem( labelX, gridSettings )

        self.__sliderSampleX = SliderWithValue( self.dialog, 1,8 )
        #self.__sliderSampleX.configureScale( {'variable':self.__xSampleRate} )
        self.__widgetSampleX = self.__sliderSampleX.getWidget()
        gridSettings[ 'column' ] = 1
        self.__sliderSampleX.addSliderCommandBinding( self.__doUpdateSample )
        guiFactory.packItem( self.__widgetSampleX, gridSettings )


        labelY = guiFactory.newLabel( self.dialog, 'Y', {'justify':Tkinter.RIGHT} )
        gridSettings[ 'sticky' ] = Tkinter.NW
        gridSettings[ 'row' ] = 1
        gridSettings[ 'column' ] = 0
        gridSettings[ 'columnspan' ] = 1
        guiFactory.packItem( labelY, gridSettings )


        self.__sliderSampleY = SliderWithValue( self.dialog, 1,8 )
        self.__widgetSampleY = self.__sliderSampleY.getWidget()
        gridSettings[ 'column' ] = 1
        self.__sliderSampleY.addSliderCommandBinding( self.__doUpdateSample )
        guiFactory.packItem( self.__widgetSampleY, gridSettings )


        labelZ = guiFactory.newLabel( self.dialog, 'Z', {'justify':Tkinter.RIGHT} )
        gridSettings[ 'sticky' ] = Tkinter.NW
        gridSettings[ 'row' ] = 2
        gridSettings[ 'column' ] = 0
        gridSettings[ 'columnspan' ] = 1
        guiFactory.packItem( labelZ, gridSettings )


        self.__sliderSampleZ = SliderWithValue( self.dialog, 1,8 )
        self.__widgetSampleZ = self.__sliderSampleZ.getWidget()
        gridSettings[ 'column' ] = 1
        self.__sliderSampleZ.addSliderCommandBinding( self.__doUpdateSample )
        guiFactory.packItem( self.__widgetSampleZ, gridSettings )        



    def __doUpdateSample( self, *unusedArgument ):
        self.__xSampleRate = self.__sliderSampleX.getValue()
        self.__ySampleRate = self.__sliderSampleY.getValue()
        self.__zSampleRate = self.__sliderSampleZ.getValue()

        self.__sliderSampleX.configureLabel( { 'text':self.__xSampleRate } )
        self.__sliderSampleY.configureLabel( { 'text':self.__ySampleRate } )
        self.__sliderSampleZ.configureLabel( { 'text':self.__zSampleRate } )

        self.__vtkWindow.updateSamplingRate(
            self.__xSampleRate, self.__ySampleRate, self.__zSampleRate )
        
#-------------- END OF VOLUME OF INTEREST DIALOG --------------------------------



#-------------- CONTOURS DIALOG -------------------------------------------
 
class DialogIsosurfaces( ModalDialog ):
    global I_TRUE, I_FALSE
    
    def __init__( self, topWindow, vtkWindow, vizType ):
        ModalDialog.__init__( self, topWindow, 'Surface Properties', I_TRUE )
        self.__vtkWindow = vtkWindow
        self.__vizType = vizType

        self.__tissue = Tkinter.IntVar()

        self.__skinValuesDict = {}
        self.__boneValuesDict = {}

        self.__skinValuesDict[ 'tissue' ] = self.__vtkWindow.SKIN 
        self.__skinValuesDict[ 'red' ] = 0
        self.__skinValuesDict[ 'green' ] = 0
        self.__skinValuesDict[ 'blue' ] = 0
        self.__skinValuesDict[ 'opaque' ] = 0
        
        self.__boneValuesDict[ 'tissue' ] = self.__vtkWindow.BONE
        self.__boneValuesDict[ 'red' ] = 0
        self.__boneValuesDict[ 'green' ] = 0
        self.__boneValuesDict[ 'blue' ] = 0
        self.__boneValuesDict[ 'opaque' ] = 0

        self.__buildDialog()
        self.__doSetSkinTissue()
        
            
    def __buildDialog( self ):
        guiFactory = GUIFactory()
        
        gridSettings = { 'padx':5, 'pady':5, 'sticky':Tkinter.SW,
                         'row':0, 'rowspan':1, 'column':0, 'columnspan':1 }

        ###### Tissue Type
        labelType = guiFactory.newLabel( self.dialog, 'Tissue', {} )
        gridSettings[ 'row' ] = 0
        gridSettings[ 'rowspan' ] = 1        
        gridSettings[ 'column' ] = 0
        gridSettings[ 'columnspan' ] = 1
        guiFactory.packItem( labelType, gridSettings )

        ############ Radiobutton Types
        self.__radioMarchingCubes = guiFactory.newRadiobutton( self.dialog,
            'Skin', self.__doSetSkinTissue,
            self.__tissue, self.__vtkWindow.SKIN, {} )  
        gridSettings[ 'column' ] = 1
        guiFactory.packItem( self.__radioMarchingCubes, gridSettings )

        self.__radioRaycastComposite = guiFactory.newRadiobutton( self.dialog,
            'Bone',  self.__doSetBoneTissue, self.__tissue,
            self.__vtkWindow.BONE, {} )
        gridSettings[ 'column' ] = 2
        guiFactory.packItem( self.__radioRaycastComposite, gridSettings )


        ###### Color Picker
        labelColor = guiFactory.newLabel( self.dialog, 'Color',
                                          {'justify':Tkinter.LEFT} )
        gridSettings[ 'row' ] = 1
        gridSettings[ 'column' ] = 0
        gridSettings[ 'columnspan' ] = 1
        guiFactory.packItem( labelColor, gridSettings )

        self.__labelColorChanger = guiFactory.newLabel( self.dialog, '',
            {'justify':Tkinter.LEFT, 'width':18} )
        gridSettings[ 'column' ] = 1
        gridSettings[ 'columnspan' ] = 2
        guiFactory.packItem( self.__labelColorChanger, gridSettings )
        
        
        labelRed = guiFactory.newLabel( self.dialog, 'Red',
                                        {'justify':Tkinter.LEFT} )
        gridSettings[ 'row' ] = 2
        gridSettings[ 'column' ] = 0
        gridSettings[ 'columnspan' ] = 1
        guiFactory.packItem( labelRed, gridSettings )

        self.__sliderRed = SliderWithValue( self.dialog, 0, 255 )
        self.__sliderRed.addSliderCommandBinding( self.__doUpdateColorLabelsEtc )
        self.__widgetRed = self.__sliderRed.getWidget()
        gridSettings[ 'column' ] = 1
        gridSettings[ 'columnspan' ] = 2
        guiFactory.packItem( self.__widgetRed, gridSettings )


        labelGreen = guiFactory.newLabel( self.dialog, 'Green',
                                          {'justify':Tkinter.LEFT} )
        gridSettings[ 'row' ] = 3
        gridSettings[ 'column' ] = 0
        gridSettings[ 'columnspan' ] = 1
        guiFactory.packItem( labelGreen, gridSettings )

        self.__sliderGreen = SliderWithValue( self.dialog, 0, 255 )
        self.__sliderGreen.addSliderCommandBinding( self.__doUpdateColorLabelsEtc )
        self.__widgetGreen = self.__sliderGreen.getWidget()
        gridSettings[ 'column' ] = 1
        gridSettings[ 'columnspan' ] = 2
        guiFactory.packItem( self.__widgetGreen, gridSettings )


        labelBlue = guiFactory.newLabel( self.dialog, 'Blue',
                                         {'justify':Tkinter.LEFT} )
        gridSettings[ 'row' ] = 4
        gridSettings[ 'column' ] = 0
        gridSettings[ 'columnspan' ] = 1
        guiFactory.packItem( labelBlue, gridSettings )

        self.__sliderBlue = SliderWithValue( self.dialog, 0, 255 )
        self.__sliderBlue.addSliderCommandBinding( self.__doUpdateColorLabelsEtc )
        self.__widgetBlue = self.__sliderBlue.getWidget()
        gridSettings[ 'column' ] = 1
        gridSettings[ 'columnspan' ] = 2
        guiFactory.packItem( self.__widgetBlue, gridSettings )

        self.__doUpdateColorLabelsEtc()


        ###### Opacity Value
        self.__labelOpacity = guiFactory.newLabel( self.dialog, 'Opaque (%)',
                                        {'justify':Tkinter.LEFT} )
        gridSettings[ 'row' ] = 5
        gridSettings[ 'column' ] = 0
        gridSettings[ 'columnspan' ] = 1
        guiFactory.packItem( self.__labelOpacity, gridSettings )

        self.__sliderOpacity = SliderWithValue( self.dialog, 0, 100 )
        self.__widgetOpacity = self.__sliderOpacity.getWidget()
        gridSettings[ 'column' ] = 1
        guiFactory.packItem( self.__widgetOpacity, gridSettings )
        self.__sliderOpacity.addSliderCommandBinding( self.__doUpdateOpacity )


    # Color extraction function adapted from that given in the web resource
    # "An Introduction to Tkinter".
    def __doUpdateColorLabelsEtc( self, *unusedArgument ):
        redValue = self.__sliderRed.getValue()
        greenValue = self.__sliderGreen.getValue()
        blueValue = self.__sliderBlue.getValue()

        self.__sliderRed.configureLabel( { 'text':redValue } )
        self.__sliderGreen.configureLabel( { 'text':greenValue } )
        self.__sliderBlue.configureLabel( { 'text':blueValue } )
        
        self.__rgbColor = "#%02x%02x%02x" % ( redValue,
                                              greenValue,
                                              blueValue )
        self.__labelColorChanger.config( background=self.__rgbColor )

        tissueValue = self.__tissue.get()
        if ( tissueValue == self.__vtkWindow.SKIN ):
            self.__skinValuesDict[ 'red' ] = redValue
            self.__skinValuesDict[ 'green' ] = greenValue
            self.__skinValuesDict[ 'blue' ] = blueValue
            print "skin...", redValue, greenValue, blueValue
            self.__vtkWindow.updateSurface( self.__skinValuesDict )

        elif ( tissueValue == self.__vtkWindow.BONE ):
            self.__boneValuesDict[ 'red' ] = redValue
            self.__boneValuesDict[ 'green' ] = greenValue
            self.__boneValuesDict[ 'blue' ] = blueValue
            print "bone...", redValue, greenValue, blueValue
            self.__vtkWindow.updateSurface( self.__boneValuesDict )


    def __doUpdateOpacity( self, *unusedArgument ):
        tissueValue = self.__tissue.get()
        opacityValue = self.__sliderOpacity.getValue()
        self.__sliderOpacity.configureLabel( { 'text':opacityValue } )

        if ( tissueValue == self.__vtkWindow.SKIN ):
            self.__skinValuesDict[ 'opaque' ] = opacityValue
            print "skin opacity...", opacityValue
            self.__vtkWindow.updateSurface( self.__skinValuesDict )

        elif ( tissueValue == self.__vtkWindow.BONE ):
            self.__boneValuesDict[ 'opaque' ] = opacityValue
            print "bone opacity...", opacityValue
            self.__vtkWindow.updateSurface( self.__boneValuesDict )


    def __doSetSkinTissue( self ):
        print ">>> SET SKIN"
        self.__tissue.set( self.__vtkWindow.SKIN )

        self.__sliderOpacity.setScaleValue( self.__skinValuesDict[ 'opaque' ] )
        self.__doUpdateOpacity()
        
        self.__sliderRed.setScaleValue( self.__skinValuesDict[ 'red' ] )
        self.__sliderGreen.setScaleValue( self.__skinValuesDict[ 'green' ] )
        self.__sliderBlue.setScaleValue( self.__skinValuesDict[ 'blue' ] )
        self.__doUpdateColorLabelsEtc()


    def __doSetBoneTissue( self ):
        print ">>> SET BONE"
        self.__tissue.set( self.__vtkWindow.BONE )

        self.__sliderOpacity.setScaleValue( self.__boneValuesDict[ 'opaque' ] )
        self.__doUpdateOpacity()
        
        self.__sliderRed.setScaleValue( self.__boneValuesDict[ 'red' ] )
        self.__sliderGreen.setScaleValue( self.__boneValuesDict[ 'green' ] )
        self.__sliderBlue.setScaleValue( self.__boneValuesDict[ 'blue' ] )
        self.__doUpdateColorLabelsEtc()

            
#----------------------- END OF CONTOURS DIALOG --------------------------------



#------------------------ GUI CLASS ---------------------------------------
class GUI:
    def __init__( self, rootWindow ):
        global I_TRUE, I_FALSE

        # Constants
        self.I_OK = 0
        self.I_NO_FILE = -10


        self.__vizType = Tkinter.IntVar()
        self.__vizDataset = Tkinter.IntVar()
        self.__vizSmooth = Tkinter.IntVar()


        
        guiFactory = GUIFactory()
        
        # Create window frame
        self.__rootWindow = rootWindow
        self.__rootWindow.title( 'CS 526 :: Project 2' )
        self.__frame = Tkinter.Frame( self.__rootWindow )
        self.__frame.pack( fill=Tkinter.BOTH, expand=1, side=Tkinter.TOP )
        
        self.__openFileFlag = I_FALSE
        #self.__openFileFlag = I_TRUE
        print "Add window frame" #<<<<                

        # Add VTK rendering viewport
        self.__vtkViewport = VTKRender( self.__frame )



        # Add menu bar
        menuBar = Tkinter.Menu( self.__frame )
        self.__rootWindow.config( menu=menuBar )
        self.__buildMenu( menuBar )
        print "Add menu bar" #<<<<

        # Add pseudo-status bar (from "An Introduction to Tkinter" )
        self.__statusLabel = guiFactory.newLabel( self.__frame, 'by: Allan Spale',
            {'relief':Tkinter.SUNKEN,'justify':Tkinter.LEFT, 'anchor':Tkinter.W }  )

        self.__statusErrorSettings = { 'background':'#ffffff',
                                       'foreground':'#000099' }
        self.__statusOKSettings = { 'background':self.__statusLabel.cget( 'background' ),
                                    'foreground':self.__statusLabel.cget( 'foreground' ) }

        self.__statusLabel.pack( fill=Tkinter.BOTH, side=Tkinter.BOTTOM )

        print "Done with window" #<<<<


        self.__dialogGlobalDetail = DialogOptionVOI( self.__rootWindow, self.__vtkViewport )
        self.__dialogDatasetProps = DialogIsosurfaces( self.__rootWindow,
                self.__vtkViewport, self.__vizType.get() )

        # Initialize
        self.__vizType.set( self.__vtkViewport.ISO ) 
        self.__vizDataset.set( self.__vtkViewport.HEAD )
        self.__doSetVizType()
        #self.__vizSmooth.set( I_TRUE )
        #self.__doSetVizSmooth()

        
    def __buildMenu( self, menuMainMenu ):          
        menuFile = Tkinter.Menu( menuMainMenu, tearoff=0 )
        menuFile.add_command( label='Exit', command=self.__doMenuFileExit )
        menuMainMenu.add_cascade( label='File', menu=menuFile )

        self.__menuViz = Tkinter.Menu( menuMainMenu, tearoff=0 )
        self.__menuViz.add_radiobutton( label='Isosurface', 
            value=self.__vtkViewport.ISO, variable=self.__vizType,
            command=self.__doSetVizType )
        self.__menuViz.add_radiobutton( label='Raycast - Composite', 
            value=self.__vtkViewport.COMP, variable=self.__vizType,
            command=self.__doSetVizType )
        self.__menuViz.add_radiobutton( label='Raycast - MIP', 
            value=self.__vtkViewport.MIP, variable=self.__vizType,
            command=self.__doSetVizType )
        self.__menuViz.add_separator()
        self.__menuViz.add_radiobutton( label='Head', 
            value=self.__vtkViewport.HEAD, variable=self.__vizDataset,
            command=self.__doSetVizType )
        self.__menuViz.add_radiobutton( label='Feet', 
            value=self.__vtkViewport.FEET, variable=self.__vizDataset,
            command=self.__doSetVizType )
        menuMainMenu.add_cascade( label='Visualization', menu=self.__menuViz )

        self.__menuOptions = Tkinter.Menu( menuMainMenu, tearoff=0 )
        self.__menuOptions.add_command( label='Dataset Properties',
                             command=self.__doMenuOptionProps )
        self.__menuOptions.add_command(
            label='Global Level of Detail', command=self.__doMenuOptionDetail )
        #self.__menuOptions.add_checkbutton( label='Smoothing', 
        #    variable=self.__vizSmooth,
        #    command=self.__doSetVizSmooth )
        menuMainMenu.add_cascade( label='Options', menu=self.__menuOptions )


    def __doSetVizType( self ):
        dataset = self.__vizDataset.get()
        viz = self.__vizType.get()
        
        if ( dataset == self.__vtkViewport.HEAD ):
            if ( viz == self.__vtkViewport.ISO ):
                self.__vtkViewport.showIsosurfaceHead()

            elif ( viz == self.__vtkViewport.COMP ):
                self.__vtkViewport.showCompHead()

            elif ( viz == self.__vtkViewport.MIP ):
                self.__vtkViewport.showMIPHead()

        elif ( dataset == self.__vtkViewport.FEET ):
            if ( viz == self.__vtkViewport.ISO ):
                self.__vtkViewport.showIsosurfaceFeet()

            elif ( viz == self.__vtkViewport.COMP ):
                self.__vtkViewport.showCompFeet()

            elif ( viz == self.__vtkViewport.MIP ):
                self.__vtkViewport.showMIPFeet()


    def __doMenuOptionProps( self ):
        self.__dialogDatasetProps.show()


    def __doMenuOptionDetail( self ):
        self.__dialogGlobalDetail.show()

    """
    def __doSetVizSmooth( self ):
        smoothFlag = self.__vizSmooth.get()
        print "smooth flag = ",smoothFlag
        if ( smoothFlag == I_TRUE ):
            self.__vtkViewport.renWin.PolygonSmoothingOn()
            self.__vtkViewport.renWin.Update()
        else:
            self.__vtkViewport.renWin.PolygonSmoothingOff()
                     self.__vtkViewport.renWin.Update()
    """

    def __doMenuFileExit( self ):
        print "Bye"
        self.__vtkViewport.destroy()
        self.__rootWindow.destroy()        
        sys.exit( 0 )
        print "Done"

#------------------------ END OF GUI CLASS ----------------------------------    


        
#### main function ####
root = Tkinter.Tk()
gui = GUI(root)
root.mainloop()
