"""

This module defines the basic classes used by MayaVi.  The debug
function enables a rudimentary debug support.  Using this one can in
theory fix bugs faster.

This code is distributed under the conditions of the BSD license.  See
LICENSE.txt for details.

Copyright (c) 2001-2003, Prabhu Ramachandran.
"""

__author__ = "Prabhu Ramachandran <prabhu_r@users.sf.net>"
__version__ = "$Revision: 1.11 $"
__date__ = "$Date: 2003/10/22 20:21:48 $"
__credits__ = """Many thanks to Jose Paulo <moitinho@civil.ist.utl.pt> 
for adding tensor data support."""


import Tkinter, vtkpython, math, tkColorChooser
import Common, vtkPipeline.vtkPipeline 
import vtkPipeline.vtkMethodParser

debug = Common.debug


class ParseException (Exception):
    """ An exception that is raised when there is a problem with
    parsing a file."""
    pass


class ModuleException (Exception):
    """ An exception that is raised when there is a problem running a
    particular visualization module."""
    pass


class BaseObject:

    """ The basic object type."""

    def save_config (self, file): 
        """Save the objects configuration to file."""
        debug ("In BaseObject::save_config ()")
        debug ("BaseObject::save_config not implemented.")

    def load_config (self, file): 
        """Load the saved objects configuration from a file."""
        debug ("In BaseObject::load_config ()")
        debug ("BaseObject::load_config not implemented.")

    def config_changed (self): 
        """The configuration file has changed - take notice."""
        debug ("In BaseObject::config_changed ()")
        debug ("BaseObject::config_changed not implemented.")
        

class VizObject (BaseObject):

    """ The basic object type for visualization components."""

    def __init__ (self):
        debug ("In VizObject::__init__ ()")
        self.pipe_objs = None
        self.renwin = None
        self.root = None

    def _auto_sweep_init (self):
        """ Initialize variables for the autosweep GUI controls."""
        debug ("In VizObject::_auto_sweep_init ()")
	self.sweep_var = Tkinter.IntVar ()
	self.sweep_var.set (0)
	self.sweep_step = Tkinter.IntVar ()
	self.sweep_step.set (10)
	self.sweep_delay = Tkinter.DoubleVar ()
	self.sweep_delay.set (1.0)

    def _lift (self):
        """Lifts an already created configuration window to the
        top."""
        debug ("In VizObject::_lift ()")
        self.root.deiconify ()
        self.root.lift ()
        self.root.focus_set ()

    def configure (self, master=None): 
        "Create the GUI configuration controls for this object."
        debug ("In VizObject::configure ()")
        if (self.root and self.root.winfo_exists ()):
            return self._lift ()
        self.root = Tkinter.Toplevel (master)
        main_win = self.root.master.winfo_toplevel ()
        self.root.geometry ("+%d+%d" % (main_win.winfo_rootx()+5,
                                        main_win.winfo_rooty()+5))
        self.root.focus_set ()
        self.root.title ("Configure %s module"%self.__class__.__name__)
        self.root.protocol ("WM_DELETE_WINDOW", self.close_gui)

        self.make_custom_gui ()

    def make_custom_gui (self):
        """ This function is called by configure().  Use this to
        customize your own GUI."""
        debug ("In VizObject::configure ()")
        self.make_pipeline_gui ()
        self.make_main_gui ()
        #self.make_auto_sweep_gui ()
        self.make_close_button ()

    def make_pipeline_gui (self):
        """ Create the GUI for the segmented pipeline browser."""
        debug ("In VizObject::make_pipeline_gui ()")
        self.pipe_frame = Tkinter.Frame (self.root, relief='ridge', bd=2)
        self.pipe_frame.pack (side='top', fill='both', expand=1)
        self.pipe_var = Tkinter.IntVar ()
        self.pipe_var.set (0)
        cb = Tkinter.Checkbutton (self.pipe_frame, text="Show Pipeline",
                                  variable=self.pipe_var, onvalue=1,
                                  offvalue=0, command=self.show_pipeline)
        cb.pack(side='top', fill='both', expand=1)

    def make_auto_sweep_gui (self):
        """ Create the GUI controls for the auto-sweep animation."""
        debug ("In VizObject::make_auto_sweep_gui ()")
	frame = Tkinter.Frame (self.root, relief='ridge', bd=2)
	frame.pack (side='top', fill='both', expand=1)

	cb = Tkinter.Checkbutton (frame, text='Auto Sweep',
                                  variable=self.sweep_var,
                                  command=self.do_sweep)
	cb.grid (row=0, column=0, columnspan=2, sticky='w')

	lab = Tkinter.Label (frame, text="Auto Sweep Step:")
	lab.grid (row=1, column=0, sticky='w')
	entry = Tkinter.Entry (frame, width=5, relief='sunken', 
                               textvariable=self.sweep_step)
	entry.grid (row=1, column=1, sticky='we')
	entry.bind ("<Return>", self.sweep_step_validate)

	lab = Tkinter.Label (frame, text="Auto Sweep Delay:")
	lab.grid (row=2, column=0, sticky='w')
	entry = Tkinter.Entry (frame, width=5, relief='sunken', 
                               textvariable=self.sweep_delay)
	entry.grid (row=2, column=1, sticky='we')
	entry.bind ("<Return>", self.sweep_delay_validate)
        
    def make_close_button (self):
        """ Create a close button for the GUI control."""
        debug ("In VizObject::make_close_button ()")
        but = Tkinter.Button (self.root, text="Close", underline=0,
                               command=self.close_gui)
        but.pack (side='bottom', fill='both', expand=1)
        self.root.bind ("<Alt-c>", self.close_gui)

    def show_pipeline (self, event=None):
        """ Show the segmented pipeline browser when required."""
        debug ("In VizObject::show_pipeline ()")
        val = self.pipe_var.get ()
        if val:
            self.p_frame = Tkinter.Frame (self.pipe_frame)
            self.p_frame.pack (side='top', fill='both', expand=1)
            cl_obj = vtkPipeline.vtkPipeline.vtkPipelineSegmentBrowser
            pipe = cl_obj (self.p_frame, self.pipe_objs, self.renwin)
            pipe.pack (side='top', fill='both', expand=1)
        else:
            if self.p_frame:
                self.p_frame.destroy ()        

    def do_sweep (self, event=None):
        """Called when the user requests auto-sweeping."""
        debug ("In VizObject::do_sweep ()")
        debug ("VizObject::do_sweep () is not implemented.")

    def sweep_step_validate(self, event=None):
        """Validate the sweep step set by the user. """
        debug ("In VizObject::sweep_step_validate ()")
        #if self.sweep_step.get() < 1 :
        #self.sweep_step.set(1)
        pass

    def sweep_delay_validate(self, event=None):
        """Validate the sweep delay set by the user. """
        debug ("In VizObject::sweep_delay_validate ()")
	if self.sweep_delay.get() <= 0:
	   self.sweep_delay.set(1.0)

    def close_gui (self, event=None):
        """Called when the 'close' button is clicked."""
        debug ("In VizObject::close_gui ()")
        self.root.master.focus_set ()
        self.root.destroy ()
        self.root = None
        try:
            self.sweep_var.set (0)
        except AttributeError:
            pass
            

class Source (VizObject):
    
    """Basic source object.  The source is similar in concept to a VTK
    source object.  It is basically a source of data."""

    def __init__ (self):
        VizObject.__init__ (self)
        
    def GetOutput (self): 
        """Output of the vtkSource object wrapped."""
        debug ("In Source::GetOutput ()")
        debug ("Source::GetOutput not implemented.")
    

class DataSource (Source):

    """ The basic class from which all are Data source classes are
    derived. """

    def __init__ (self): 
        debug ("In DataSource::__init__ ()")
        Source.__init__ (self)
        self.file_name = ""
        self.scalar_data_name = ""
        self.vector_data_name = ""        
        self.tensor_data_name = ""
        self.ref_lst = []
        self.grid_type = ""
        self.root = None
    
    def initialize (self, file_name): 
        """Initialize the object given a valid file name."""
        debug ("In DataSource::initialize ()")
        debug ("DataSource::initialize not implemented.")

    def get_file_name (self):
        """Get the data file name. """
        debug ("In DataSource::get_file_name ()")
        return self.file_name

    def add_reference (self, ref):
        """ Add a reference object that is notified each time anything
        is changed in the DataSource object.  This is used for all
        ModuleManager instances."""
        debug ("In DataSource::add_reference ()")
        if ref in self.ref_lst:
            msg = "In DataSource::add_reference: Cannot add "\
                  "reference since reference already exists."
            debug (msg)
            return
        else:
            self.ref_lst.append (ref)

    def del_reference (self, ref):
        """ Remove an object that is to be notified of changes in
        self.  This is called when a ModuleManager is being
        deleted."""
        debug ("In DataSource::del_reference ()")
        self.ref_lst.remove (ref)

    def update_references (self):        
        """ Called when the configuration of the DataSource has
        changed.  This updates all the necessary objects via their
        update method."""
        debug ("In DataSource::update_references ()")
        for ref in self.ref_lst:
            ref.Update ()

    def config_changed (self):
        """ Called when the configuration options (in the Preferences
        menu) is changed."""
        debug ("In DataSource::config_changed ()")
        pass

    def GetOutput (self): 
	"""Get the Data reader's output. """
        debug ("In DataSource::GetOutput ()")
        pass

    def get_grid_type (self): 
	"""Get the type of grid used in the data file."""
        debug ("In DataSource::get_grid_type ()")
	return self.grid_type

    def get_scalar_data_name (self): 
	"""Get the name of the scalar data attribute being used."""
        debug ("In DataSource::get_scalar_data_name ()")
	return self.scalar_data_name

    def get_vector_data_name (self): 
	"""Get the name of the vector data attribute being used."""
        debug ("In DataSource::get_vector_data_name ()")
	return self.vector_data_name

    def get_tensor_data_name (self):
        """Get the name of the tensor data attribute being used."""
        debug ("In DataSource::get_tensor_data_name ()")
        return self.tensor_data_name

class Filter (Source):

    """ The basic Filter class from which all are filters
    derived. This implements sufficient functionality that more
    complex filters are pretty much trivial to create."""

    def __init__ (self, mod_m):
        """ The argument 'mod_m' is a ModuleManager."""
        debug ("In Filter::__init__ ()")
        Common.state.busy ()
        Source.__init__ (self)
        self.mod_m = mod_m
        self.renwin = mod_m.get_render_window ()
        self.prev_fil = mod_m.get_last_source ()
        # try to use this as your filter
        self.fil = None
        self.initialize ()
        Common.state.idle ()

    def initialize (self):
        """Override this to create the actual filter and do what you
        want.  Use self.fil as the wrapped filter."""
        debug ("In Filter::initialize ()")        
        pass

    def set_input_source (self, source):
        """Set the input source object for this Filter."""
        debug ("In Filter::set_input_source ()")
        debug ("Filter::set_input_source not implemented.")        

    def SetInputSource (self, source): 
        """Set the input source object for this Filter."""
        debug ("In Filter::SetInputSource ()")
        self.set_input_source (source)

    def GetOutput (self):
        debug ("In Filter::GetOutput ()")        
        return self.fil.GetOutput ()    

    def get_scalar_data_name (self):
	"""Get the name of the scalar data attribute being used.
	Defaults to returning the previous filters scalar
	data_name."""
        debug ("In Filter::get_scalar_data_name ()")
        return self.prev_fil.get_scalar_data_name ()
        
    def get_vector_data_name (self):
	"""Get the name of the vector data attribute being used.
	Defaults to returning the previous filters vector
	data_name."""
        debug ("In Filter::get_vector_data_name ()")
        return self.prev_fil.get_vector_data_name ()

    def get_tensor_data_name (self):
        """Get the name of the tensor data attribute being used.
        Defaults to returning the previous filters tensor
	data_name."""
        debug ("In Filter::get_tensor_data_name ()")
        return self.prev_fil.get_tensor_data_name ()

    def save_config (self, file):
        """Saves the filter config to passed file."""
        debug ("In Filter::save_config ()")
        p = vtkPipeline.vtkMethodParser.VtkPickler ()
        for obj in (self.fil, ):
            p.dump (obj, file)
        
    def load_config (self, file):
        """Loads the filter config from passed file."""
        debug ("In Filter::load_config ()")
        p = vtkPipeline.vtkMethodParser.VtkPickler ()
        for obj in (self.fil, ):
            p.load (obj, file)
        self.fil.Update ()


class Module (VizObject):
    
    """ The basic Module class from which all modules are derived."""

    def __init__ (self, mod_m): 
        """ The argument 'mod_m' is a ModuleManager."""
        debug ("In Module::__init__ ()")
        VizObject.__init__ (self)
        self.mod_m = mod_m
        self.renwin = mod_m.get_render_window ()
        # Set this to the actor you need to be able to configure
        self.actor = None
        self.mapper = None
    
    def SetInput (self, source):        
        """ Set the input for this Module.  This function is similar
        to the VTK obj1.SetInput(obj2.GetOutput()) kind of function
        call. """
        debug ("In Module::SetInput ()")
        debug ("Module::SetInput not implemented.")

    def _contour_init (self):
        """ Initialize variables for contour GUI controls."""
        debug ("In Module::_contour_init ()")
        self.contour_on = Tkinter.IntVar ()
        self.linew_var = Tkinter.DoubleVar ()
        self.n_cnt = Tkinter.StringVar ()
        self.min_cnt = Tkinter.DoubleVar ()
        self.max_cnt = Tkinter.DoubleVar ()

    def make_contour_gui (self):
        """ Create the GUI controls for contours."""
        debug ("In Module::make_contour_gui ()")
        frame = Tkinter.Frame (self.root, relief='ridge', bd=2)
        frame.pack (side='top', fill='both', expand=1)
        
        cb = Tkinter.Checkbutton (frame, text="Show Contours", 
                                  variable=self.contour_on, onvalue=1,
                                  offvalue=0, command=self.do_contour)
        cb.grid (row=0, column=0, sticky='w')
        
        lab = Tkinter.Label (frame, text="Contours:")
        lab.grid (row=1, column=0, sticky='w')
        entry = Tkinter.Entry (frame, width=15, relief='sunken', 
                               textvariable=self.n_cnt)
        entry.grid (row=1, column=1, sticky='we')
        entry.bind ("<Return>", self.change_contour)
        
        lab = Tkinter.Label (frame, text="Minimum contour:")
        lab.grid (row=2, column=0, sticky='w')
        entry = Tkinter.Entry (frame, width=15, relief='sunken', 
                               textvariable=self.min_cnt)
        entry.grid (row=2, column=1, sticky='we')
        entry.bind ("<Return>", self.change_contour)

        lab = Tkinter.Label (frame, text="Maximum contour:")
        lab.grid (row=3, column=0, sticky='w')
        entry = Tkinter.Entry (frame, width=15, relief='sunken', 
                               textvariable=self.max_cnt)
        entry.grid (row=3, column=1, sticky='we')
        entry.bind ("<Return>", self.change_contour)

    def make_actor_gui (self, event=None, color=1, linewidth=1,
                        scalar=1, representation=1, opacity=1,
                        compact=0):
        """Creates controls for the actor config.  This requires that
        there be an instance variable called self.actor and
        self.mapper."""        
        debug ("In Module::make_actor_gui ()")

        self.scalar_on_var = Tkinter.IntVar ()
        self.scalar_on_var.set (0)
        if self.mapper and hasattr(self.mapper, 'GetScalarVisibility'):
            self.scalar_on_var.set (self.mapper.GetScalarVisibility ())

        frame = Tkinter.Frame (self.root, relief='ridge', bd=2)
        frame.pack (side='top', fill='both', expand=1)

        rw = 0
        if scalar:
            cb = Tkinter.Checkbutton (frame, text="Scalar Coloring",
                                      variable=self.scalar_on_var,
                                      onvalue=1, offvalue=0,
                                      command=self.scalar_config)
            cb.grid (row=rw, column=0, columnspan=2, pady=3, sticky='w')
            rw = rw + 1
        if color:
            but = Tkinter.Button (frame, text="Change Object Color",
                                  underline=2, command=self.change_color)
            but.grid (row=rw, column=0, columnspan=2, pady=3, sticky='ew')
            self.root.bind ("<Alt-a>", self.change_color)            
            rw = rw + 1

        if representation:
            self.rep_var = Tkinter.IntVar ()
            self.rep_var.set (self.actor.GetProperty().GetRepresentation())
            rb1 = Tkinter.Radiobutton (frame,
                                       text="Set Representation "\
                                       "to Wireframe",
                                       variable=self.rep_var, value=1,
                                       command=self.represent_config)
            rb1.grid (row=rw, column=0, columnspan=2, pady=3, sticky='w')
            rw = rw + 1
            rb2 = Tkinter.Radiobutton (frame,
                                       text="Set Representation to Surface",
                                       variable=self.rep_var, value=2,
                                       command=self.represent_config)
            rb2.grid (row=rw, column=0, columnspan=2, pady=3, sticky='w')
            rw = rw + 1

        if opacity:
            self.opaq_res_var = Tkinter.DoubleVar ()
            self.opaq_res_var.set (0.01)
            if compact:
                but = Tkinter.Button (frame, text="Change Object Opacity",
                                      underline=7,
                                      command=self.compact_opacity)
                but.grid (row=rw, column=0, columnspan=2, pady=3,
                          sticky='ew')
                self.root.bind ("<Alt-o>", self.compact_opacity)
                rw = rw + 1
            else:
                sl = Tkinter.Scale (frame, label="Set Opacity", 
                                    from_=0.0, to=1.0,
                                    length="5c", orient='horizontal', 
                                    resolution=self.opaq_res_var.get ())
                sl.set (self.actor.GetProperty ().GetOpacity ())
                sl.grid (row=rw, column=0, columnspan=2, sticky='ew')
                sl.bind ("<ButtonRelease>", self.change_opacity)
                self.opaq_slider = sl
                rw = rw + 1
                lab = Tkinter.Label (frame, text="Opacity resolution: ")
                lab.grid (row=rw, column=0, sticky='w')
                entr = Tkinter.Entry (frame, width=5, relief='sunken',
                                      textvariable=self.opaq_res_var)
                entr.grid (row=rw, column=1, sticky='ew')
                entr.bind ("<Return>", self.set_opacity_resolution)
                rw = rw+1

        if linewidth:
            self.linew_var = Tkinter.DoubleVar ()
            self.linew_var.set (self.actor.GetProperty ().GetLineWidth ())
            lab = Tkinter.Label (frame, text="Line width:")
            lab.grid (row=rw, column=0, sticky='w')
            entr = Tkinter.Entry (frame, width=5, relief='sunken',
                                  textvariable=self.linew_var)
            entr.grid (row=rw, column=1, sticky='we')
            entr.bind ("<Return>", self.set_linewidth)
            rw = rw + 1

    def do_contour (self, event=None):
        """Called when the user requests contour lines."""
        debug ("In Module::do_contour ()")
        debug ("Module::do_contour () is not implemented.")

    def change_contour (self, event=None):
        """Called when the user changes the contour line config."""
        debug ("In Module::change_contour ()")
        debug ("Module::change_contour () is not implemented.")

    def set_linewidth (self, event=None):
        """Called when the actor linewidth to be changed."""
        debug ("In Module::set_linewidth ()")
        Common.state.busy ()
        self.actor.GetProperty ().SetLineWidth (self.linew_var.get ())
        self.renwin.Render ()
        Common.state.idle ()

    def scalar_config (self, event=None):        
        """ Called when the user requests ScalarVisibility on or
        off."""
        debug ("In Module::scalar_config ()")
        Common.state.busy ()
        val = self.scalar_on_var.get ()
        self.mapper.SetScalarVisibility (val)
        self.renwin.Render ()
        Common.state.idle ()

    def represent_config (self, event=None):        
        """ Called when the user changes the Representation of the
        actor."""
        debug ("In Module::plane_config ()")
        Common.state.busy ()
        val = self.rep_var.get ()
        self.actor.GetProperty ().SetRepresentation (val)
        self.renwin.Render ()
        Common.state.idle ()

    def change_color (self, event=None): 
        """ Called when the user changes the color of the actor."""
        debug ("In Module::change_color ()")
	clr = self.actor.GetProperty ().GetColor ()
	init_clr = "#%02x%02x%02x"%(clr[0]*255, clr[1]*255, clr[2]*255)
	color = tkColorChooser.askcolor (title="Change object color", 
					 initialcolor=init_clr)
	if color[1] is not None:
            Common.state.busy ()
	    clr = Common.tk_2_vtk_color (color[0])
	    self.actor.GetProperty ().SetColor (*clr)
	    self.renwin.Render ()        
            Common.state.idle ()
            if self.scalar_on_var.get () != 0:
                msg = "Warning: This setting will have effect only if "\
                      "there is no ScalarVisibility."
                Common.print_err (msg)

    def change_opacity (self, event=None):
        """ Called when the opacity slider is changed."""
        debug ("In Module::change_opacity ()")
        Common.state.busy ()
        val = self.opaq_slider.get ()
        self.actor.GetProperty ().SetOpacity (val)
        self.renwin.Render ()
        Common.state.idle ()

    def set_opacity_resolution (self, event=None):
        """ Called when the opacity slider resolution is changed. """
        self.opaq_slider.config (resolution=self.opaq_res_var.get ())
        
    def compact_opacity (self, event=None):        
        """ Called when the opacity button (when compact=1) is
        clicked."""
        self.o_top = top = Tkinter.Toplevel (self.root)
        top.transient (self.root.master)
        top.protocol ("WM_DELETE_WINDOW", top.destroy)
        frame = Tkinter.Frame (top, relief='ridge', bd=2)
        frame.pack (side='top', fill='both', expand=1)
        rw = 0
        sl = Tkinter.Scale (frame, label="Set Opacity", 
                            from_=0.0, to=1.0,
                            length="5c", orient='horizontal', 
                            resolution=self.opaq_res_var.get ())
        sl.set (self.actor.GetProperty ().GetOpacity ())
        sl.grid (row=rw, column=0, columnspan=2, sticky='ew')
        sl.bind ("<ButtonRelease>", self.change_opacity)
        self.opaq_slider = sl
        rw = rw + 1
        lab = Tkinter.Label (frame, text="Opacity resolution: ")
        lab.grid (row=rw, column=0, sticky='w')
        entr = Tkinter.Entry (frame, width=5, relief='sunken',
                              textvariable=self.opaq_res_var)
        entr.grid (row=rw, column=1, sticky='ew')
        entr.bind ("<Return>", self.set_opacity_resolution)
        rw = rw+1
        but = Tkinter.Button (frame, text="Close", underline=0,
                              command=lambda t=top, e=None: t.destroy ())
        but.grid (row=rw, column=0, columnspan=2, sticky='ew')
        top.bind ("<Alt-c>", self.__opacity_quit)

    def __opacity_quit (self, event=None):
        self.o_top.destroy ()


class CutPlaneModule (Module):

    """ An abstraction for cut plane based modules. """

    def __init__ (self, mod_m):
        debug ("In CutPlaneModule::__init ()")
        Module.__init__ (self, mod_m)
        # this should be a vtkPlane
        self.plane = vtkpython.vtkPlane ()
	self.step_var = Tkinter.DoubleVar ()
	self.n_step_var = Tkinter.IntVar ()
        self.resoln_var = Tkinter.DoubleVar ()
        self.resoln_var.set (1.0)
        self.slider = []

    def get_angles (self):
        debug ("In CutPlaneModule::get_angles ()")
        nor = self.plane.GetNormal ()
        pos = self.plane.GetOrigin ()
        zang = math.acos (nor[2])
        xang = math.atan2 (nor[1], nor[0])
        if xang < 0:
            xang = xang + 2.0*math.pi

        zang = zang*180.0/math.pi
        xang = xang*180.0/math.pi
        return (zang, xang)    

    def config_extents (self, val):
        debug ("In CutPlaneModule::config_extents ()")
        zang = val[0]*math.pi/180.0
        xang = val[1]*math.pi/180.0
        ds = val[2]*self.step_size
        cos1 = math.cos (zang)
        sin1 = math.sin (zang)
        cos2 = math.cos (xang)
        sin2 = math.sin (xang)
        nor = (sin1*cos2, sin1*sin2, cos1)
        cen = self.center
        pos = (cen[0] + ds*nor[0], cen[1] + ds*nor[1], cen[2] + ds*nor[2])
        self.plane.SetNormal (nor)
        self.plane.SetOrigin (pos)

    def make_cut_plane_gui (self):
        debug ("In CutPlaneModule::make_cut_plane_gui ()")
        frame = Tkinter.Frame (self.root, relief='ridge', bd=2)
        frame.pack (side='top', fill='both', expand=1)

        angle = self.get_angles ()
        self.slider = []
        sl = Tkinter.Scale (frame, label="Angle from Z-axis", from_=0,
                            to=180, length="8c", orient='horizontal')
        sl.set (angle[0])
        sl.pack (side='top')
        sl.bind ("<ButtonRelease>", self.change_slider)
        self.slider.append (sl)
        
        sl = Tkinter.Scale (frame, label="Angle from X-axis", from_=0,
                            to=360, length="8c", orient='horizontal')
        sl.set (angle[1])
        sl.pack (side='top')
        sl.bind ("<ButtonRelease>", self.change_slider)
        self.slider.append (sl)
        
        val = self.n_step_var.get ()
        sl = Tkinter.Scale (frame, length="8c", 
                            label="Distance from center along normal",
                            from_=-val, to=val, orient='horizontal')
        sl.set (self.slider_pos)
        sl.pack (side='top')
        sl.bind ("<ButtonRelease>", self.change_slider)
        self.slider.append (sl)

	f = Tkinter.Frame (frame)
	f.pack (side='top')

        rw = 0
	labl = Tkinter.Label (f, text="Angle resolution:")
	labl.grid (row=rw, column=0, sticky='w')
	self.step_entr = Tkinter.Entry (f, width=5, relief='sunken',
                                        textvariable=self.resoln_var)
	self.step_entr.grid (row=rw, column=1, sticky='we')
	self.step_entr.bind ("<Return>", self.change_resoln)
        rw = rw + 1

	labl = Tkinter.Label (f, text="Distance step:")
	labl.grid (row=rw, column=0, sticky='w')
	self.step_entr = Tkinter.Entry (f, width=5, relief='sunken',
                                        textvariable=self.step_var)
	self.step_entr.grid (row=rw, column=1, sticky='we')
	self.step_entr.bind ("<Return>", self.change_cut)
        rw = rw + 1

	labl = Tkinter.Label (f, text="Step bounds:")
	labl.grid (row=rw, column=0, sticky='w')
	self.n_step_entr = Tkinter.Entry (f, width=5, relief='sunken',
                                          textvariable=self.n_step_var)
	self.n_step_entr.grid (row=rw, column=1, sticky='we')
	self.n_step_entr.bind ("<Return>", self.change_cut)
    
    def change_cut (self, event=None):
        debug ("In CutPlaneModule::change_cut ()")
	self.step_size = self.step_var.get ()
	n_s = self.n_step_var.get ()
	self.slider[2].config (from_=-n_s, to=n_s)
	self.change_slider (event)

    def change_slider (self, event=None):
        debug ("In CutPlaneModule::change_slider ()")
        Common.state.busy ()
	val = []
	for i in range (3):
	    val.append (self.slider[i].get ())

        self.slider_pos = val[2]
	self.config_extents (val)
	self.renwin.Render ()
        Common.state.idle ()    

    def change_resoln (self, event=None):
        debug ("In CutPlaneModule::change_resoln ()")
        val = self.resoln_var.get ()
	for i in range (0, 2):
	    self.slider[i].config (resolution=val)        
    
